feat(users): OTP delivery method picker on user creation + post-login agreement
Create User dialog now has a mandatory Email/Mobile OTP delivery method picker (styled as a pill toggle) alongside the existing fields. Adds a new post-login agreement acceptance dialog: right after the existing 2FA flow (TwoFactorAuthenticationComponent) succeeds, or immediately after login for users without 2FA, the user must accept the bank's current agreement/disclaimer at least once before reaching the app. Also adds a "Manage User Agreement" admin screen (System section) to publish versioned agreement text without a code deploy. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -281,12 +281,8 @@ export class AuthenticationService {
|
||||
message: 'Your password has expired, please reset your password!'
|
||||
});
|
||||
} else {
|
||||
this.setCredentials(credentials);
|
||||
this.alertService.alert({
|
||||
type: 'Authentication Success',
|
||||
message: `${credentials.username} successfully logged in!`
|
||||
});
|
||||
delete this.credentials;
|
||||
this.credentials = credentials;
|
||||
this.finalizeLogin();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -546,16 +542,60 @@ export class AuthenticationService {
|
||||
message: 'Your password has expired, please reset your password!'
|
||||
});
|
||||
} else {
|
||||
this.setCredentials(this.credentials);
|
||||
this.alertService.alert({
|
||||
type: 'Authentication Success',
|
||||
message: `${this.credentials.username} successfully logged in!`
|
||||
});
|
||||
delete this.credentials;
|
||||
this.storage.setItem(this.twoFactorAuthenticationTokenStorageKey, JSON.stringify(response));
|
||||
this.finalizeLogin();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs after primary login (and 2FA, if the account has it enrolled) succeeds. Every user must
|
||||
* accept the bank's current agreement/disclaimer at least once — checks that here and either
|
||||
* finishes the login immediately (already accepted, or nothing published yet) or fires an alert
|
||||
* so `login.component.ts` can show the accept dialog.
|
||||
*/
|
||||
private finalizeLogin(): void {
|
||||
this.http.get<{ accepted: boolean }>('/useragreements/active/status').subscribe({
|
||||
next: (status) => {
|
||||
if (status?.accepted) {
|
||||
this.completeAuthentication();
|
||||
} else {
|
||||
this.alertService.alert({
|
||||
type: 'Agreement Acceptance Required',
|
||||
message: 'Please review and accept the agreement to continue.'
|
||||
});
|
||||
}
|
||||
},
|
||||
// Don't let an agreement-status hiccup block login entirely.
|
||||
error: () => this.completeAuthentication()
|
||||
});
|
||||
}
|
||||
|
||||
/** Finishes the login: persists credentials and fires the existing 'Authentication Success' alert. */
|
||||
private completeAuthentication(): void {
|
||||
this.setCredentials(this.credentials);
|
||||
this.alertService.alert({
|
||||
type: 'Authentication Success',
|
||||
message: `${this.credentials.username} successfully logged in!`
|
||||
});
|
||||
delete this.credentials;
|
||||
}
|
||||
|
||||
/** Fetches the active agreement's text for display, when the current user hasn't accepted it yet. */
|
||||
getAgreementStatus(): Observable<{
|
||||
accepted: boolean;
|
||||
id?: number;
|
||||
version?: number;
|
||||
title?: string;
|
||||
body?: string;
|
||||
}> {
|
||||
return this.http.get<any>('/useragreements/active/status');
|
||||
}
|
||||
|
||||
/** Records the current user's acceptance of the active agreement, then finishes the login. */
|
||||
acceptActiveAgreement(): Observable<void> {
|
||||
return this.http.post<any>('/useragreements/active/accept', {}).pipe(map(() => this.completeAuthentication()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the user's password and authenticates the user.
|
||||
* @param {any} passwordDetails New password.
|
||||
|
||||
Reference in New Issue
Block a user