You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A member whose role requires MFA, and whose enrolment was ever left unfinished (tab closed, or the 15-minute enrolment token expired), can never sign in again. Every later login lands on enrolment, and enrolment fails with "Impossible de préparer la double authentification … rechargez la page". Reloading changes nothing.
Reproduced (2026-10-02, local instance on master + #888)
Invite an admin and set the MFA policy to 0 grace days. Their login answers mfa_enrollment_required.
The enrolment screen calls POST /auth/mfa/setup → 200. A row is stored in mfa_secrets with is_verified = false.
Sign in again → enrolment → POST /auth/mfa/setup → 400, every time.
[12:18:42] 200 - POST /api/v1/auth/mfa/setup
[12:34:53] 400 - POST /api/v1/auth/mfa/setup
mfa_secrets: enrol872@example.test | is_verified=f | 2026-10-02 13:18:42
Cause
SetupMFAUseCase.Execute (backend/internal/application/auth/mfa_usecase.go) refuses only a verified existing secret, then always calls CreateMFASecret. mfa_secrets.user_id is unique, so an unverified row left by an earlier attempt makes every insert fail.
The same defect is why the frontend carries a "setup is NOT idempotent" workaround in AuthScreen.tsx.
Acceptance criteria
When an unverified secret exists, setup replaces its key material and answers 200 with a fresh secret, QR code and backup codes. Only the new secret verifies.
When a verified secret exists, setup still answers 409 already_enabled, and the stored secret is untouched.
Problem
A member whose role requires MFA, and whose enrolment was ever left unfinished (tab closed, or the 15-minute enrolment token expired), can never sign in again. Every later login lands on enrolment, and enrolment fails with "Impossible de préparer la double authentification … rechargez la page". Reloading changes nothing.
Reproduced (2026-10-02, local instance on master + #888)
mfa_enrollment_required.POST /auth/mfa/setup→ 200. A row is stored inmfa_secretswithis_verified = false.401 TOKEN_EXPIRED, and the user is sent back to the password (fix(frontend): the MFA code screen does not tell users to sign in again or wait after too many wrong codes #872).POST /auth/mfa/setup→ 400, every time.Cause
SetupMFAUseCase.Execute(backend/internal/application/auth/mfa_usecase.go) refuses only a verified existing secret, then always callsCreateMFASecret.mfa_secrets.user_idis unique, so an unverified row left by an earlier attempt makes every insert fail.The same defect is why the frontend carries a "setup is NOT idempotent" workaround in
AuthScreen.tsx.Acceptance criteria
already_enabled, and the stored secret is untouched.TestSetupMFA_ReplacesAnUnverifiedSecret,TestSetupMFA_VerifiedSecretIsKept.Definition of Done
go test ./internal/application/auth/ -raceplus the full backend suite, with output pasted here.Closesthis issue.