feat(mfa): Google Authenticator (TOTP) + branded 2FA login popup

- Settings → MFA Factors: wire the Authenticator tile to the real backend.
  POST /v1/mfa/totp/setup mints the QR + secret + otpauth URL; the response
  binds directly to <img [src]="totp.qrDataUri"> alongside a manual-entry
  fallback ("Can't scan? Enter this in your app:") and an issuer/account
  label. Verify hits POST /v1/mfa/totp/verify; on
  error.msg.user.mfa.totp.code.invalid the message renders inline. Disable
  uses DELETE /mfa/factors/{id}.
- Already-enrolled guard surfaces a dialog and re-hydrates from
  /mfa/factors when error.msg.user.mfa.totp.already.enrolled is returned.
- Mobile click-to-add deep-link bound to otpAuthUrl (shown on touch-only
  browsers via @media (hover:none)).
- access-control.service.ts: TotpSetupResponse + TotpVerifyResponse +
  setupTotp() / verifyTotp() endpoints.
- hydrateFromServer / resetAllFactors now treat TOTP as a persisted factor
  (verified, factorId, verifiedOn) instead of local state.
- Login 2FA picker: iconFor() returns 'qrcode' for TOTP, OTP-context copy
  switches to "Open your authenticator app", Resend button hidden,
  validity badge reads "rotates every 30 s".
- 2FA popup redesigned: CBS Nexora brand lockup at the top (logomark +
  wordmark + 'Secure Sign-in' sub-line), refined card depth with stacked
  shadow + ambient glow, segmented 6-cell OTP input, brighter primary
  button gradient, responsive collapse at <=480px.
- Register faQrcode in icons.module.ts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aditya
2026-05-15 17:45:07 +05:30
co-authored by Claude Opus 4.7
parent 595054b0b8
commit 4322f37c6d
10 changed files with 500 additions and 147 deletions
+4 -2
View File
@@ -192,7 +192,8 @@ import {
faBrain,
faRobot,
faMicrochip,
faMagic
faMagic,
faQrcode
} from '@fortawesome/free-solid-svg-icons';
/**
@@ -391,7 +392,8 @@ export class IconsModule {
faBrain,
faRobot,
faMicrochip,
faMagic
faMagic,
faQrcode
);
}
}