Add Google Authenticator (TOTP) as MFA factor: device mapping + login

Backend:
- New dependency dev.samstevens.totp:totp:1.7.1 (bundles ZXing for QR PNG).
- TotpService: secret/QR/otpauth-URL/verify primitives. SHA1, 6 digits, 30s —
  the standard combo every consumer authenticator app accepts unconfigured.
  Issuer label is configurable via FINERACT_MFA_TOTP_ISSUER, defaults to
  "CBSnexora".
- Two-step device mapping driven by TotpEnrollmentService:
  * POST /v1/mfa/totp/setup  — generate secret, upsert m_user_mfa_factor row
    as factor_type=TOTP/verified=false, return {secret, qrDataUri, otpAuthUrl,
    issuer, factorId}. Refuses to overwrite an already-verified-and-active
    factor so an accidental click can't unbind the user's working device.
  * POST /v1/mfa/totp/verify — body {code}; verify against the stored secret
    and flip verified=true on success.
- Login wiring in MfaFactorBackedTwoFactorServiceImpl:
  * getDeliveryMethodsForUser surfaces verified TOTP factors as
    {name:"totp", target:"Google Authenticator"} so the picker offers it
    alongside SMS/EMAIL.
  * createNewOTPToken("totp") creates a no-op OTPRequest (no LincPay/SMTP
    round-trip) carrying the factor id in the token field so validate can
    look up the secret.
  * createAccessTokenFromOTP branches on channel — TOTP path verifies code
    vs factor.secret with totp library (sliding 1-step window) and mints
    a TFAccessToken with the same lifetime + extended-token logic as OTP.
  * Defence-in-depth: re-checks at validate time that the factor still
    belongs to the user, is verified, and not disabled.
- TwoFactorConstants.TOTP_DELIVERY_METHOD_NAME = "totp".
- SecurityConfig: /api/*/mfa/totp/** is fullyAuthenticated() (exempt from
  the TFA gate so users can enrol before they can satisfy 2FA).

UI changes pending (see chat): add a "Google Authenticator" tile on the
MFA Factors settings page that calls /setup → shows QR + secret → calls
/verify; extend two-factor-authentication.component.ts iconFor() and copy
for the new "totp" delivery method. Existing requestOTP / validateOTP
pipeline handles the login flow transparently.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aditya
2026-05-15 17:41:55 +05:30
co-authored by Claude Opus 4.7
parent 6558ea6998
commit 45a041b023
10 changed files with 3150 additions and 13 deletions
+5 -1
View File
@@ -48,7 +48,11 @@
"PowerShell(Get-Process -Id 9340 -ErrorAction SilentlyContinue | Select-Object Id, ProcessName, StartTime, @{Name='MemMB';Expression={[math]::Round\\($_.WorkingSet64/1MB,0\\)}}, Path | Format-List)",
"Bash(git -c core.editor=true rebase --continue)",
"Bash(git rebase *)",
"Bash(rm -rf .git/rebase-merge; git status -sb | head -3)"
"Bash(rm -rf .git/rebase-merge; git status -sb | head -3)",
"Bash(git -c commit.gpgsign=false commit -m ' *)",
"Bash(git pull *)",
"Bash(./gradlew :fineract-security:compileJava :fineract-provider:compileJava -q)",
"Bash(git ls-remote *)"
],
"additionalDirectories": [
"c:\\Users\\adity\\Music\\CBS\\cfcfrms-bank-api",