feat: MFA/2FA, OTP audit logs, KYC + client/user enhancements

- Access Control module: SMS/Email/TOTP/Hardware-token MFA factor management
- OTP Audit Logs module under Admin menu (list + detail) with 401/403 handling
- Two-factor authentication login flow updates
- Client KYC, India address, family-members, ckyc, nominee enhancements
- New shared KYC helpers (kyc-proof-types, kyc-upload-helper.service)
- User create/edit/view enhancements
- Shell navigation: OTP Audit Logs entry under real Admin menu
This commit is contained in:
Aditya
2026-05-10 13:24:48 +05:30
parent 2d4fb4237e
commit 3b752dc6ae
56 changed files with 7741 additions and 599 deletions
+31
View File
@@ -43,6 +43,7 @@ import { TenantSelectorComponent } from '../shared/tenant-selector/tenant-select
import { LoginFormComponent } from './login-form/login-form.component';
import { ResetPasswordComponent } from './reset-password/reset-password.component';
import { TwoFactorAuthenticationComponent } from './two-factor-authentication/two-factor-authentication.component';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { MatMenuTrigger, MatMenu, MatMenuItem } from '@angular/material/menu';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
@@ -216,6 +217,8 @@ export class LoginComponent implements OnInit, OnDestroy {
private alertService = inject(AlertService);
private settingsService = inject(SettingsService);
private themingService = inject(ThemingService);
private dialog = inject(MatDialog);
private twoFactorDialogRef: MatDialogRef<TwoFactorAuthenticationComponent> | null = null;
private router = inject(Router);
private versionService = inject(VersionService);
@@ -270,13 +273,16 @@ export class LoginComponent implements OnInit, OnDestroy {
const alertType = alertEvent.type;
if (alertType === 'Password Expired') {
this.twoFactorAuthenticationRequired = false;
this.closeTwoFactorDialog();
this.resetPassword = true;
} else if (alertType === 'Two Factor Authentication Required') {
this.resetPassword = false;
this.twoFactorAuthenticationRequired = true;
this.openTwoFactorDialog();
} else if (alertType === 'Authentication Success') {
this.resetPassword = false;
this.twoFactorAuthenticationRequired = false;
this.closeTwoFactorDialog();
this.router.navigate(['/'], { replaceUrl: true });
} else if (alertType === 'Tenant Changed') {
this.updateLogo();
@@ -323,9 +329,34 @@ export class LoginComponent implements OnInit, OnDestroy {
if (this.theme$) {
this.theme$.unsubscribe();
}
this.closeTwoFactorDialog();
clearInterval(this.slideTimer);
}
/** Open the 2FA challenge as a non-dismissable modal */
private openTwoFactorDialog(): void {
if (this.twoFactorDialogRef) return;
this.twoFactorDialogRef = this.dialog.open(TwoFactorAuthenticationComponent, {
width: '440px',
maxWidth: '95vw',
panelClass: 'tfa-dialog-panel',
backdropClass: 'tfa-dialog-backdrop',
disableClose: true,
autoFocus: 'first-tabbable',
restoreFocus: true
});
this.twoFactorDialogRef.afterClosed().subscribe(() => {
this.twoFactorDialogRef = null;
});
}
private closeTwoFactorDialog(): void {
if (this.twoFactorDialogRef) {
this.twoFactorDialogRef.close();
this.twoFactorDialogRef = null;
}
}
goToSlide(index: number): void {
this.currentSlide = index;
this.restartSlider();