SCRUM-262 — admin-auth admin reset of another user's MFA¶
Found while writing the admin access guide (SCRUM-243).
What exists¶
POST /api/admin/users/{id}/mfa/reset (admin, via requireAdmin), no body:
| Case | Answer |
|---|---|
| unknown user | 404 not_found |
| root target | 403 root_protected (root has no second factor) |
| own account | 403 self_modification |
| target is an admin, caller is not root | 403 insufficient_role (D3) |
| nothing enrolled (no factor, no pending secret, no recovery codes) | 409 mfa_not_enrolled |
| otherwise | 200 with the user; in one transaction under FOR UPDATE: factor, pending secret and step cleared, recovery codes deleted, open MFA tickets burned, refresh sessions revoked, audit mfa.reset with {had_factor, recovery_codes_removed, sessions_revoked} |
The caller's root flag is read from the database in the same transaction. An admin whose MFA was reset is sent through enrollment at the next login (SCRUM-208).
How to verify¶
cd services/admin_auth
ADMIN_AUTH_TEST_DATABASE_URL=postgres://auth_rw:pw@127.0.0.1:5433/admin_auth_test?sslmode=disable \
go test -race -count=1 ./...
internal/server/admin_mfa_test.go: the rule table (self, root, admin→admin 403,
root→admin 200, admin→live_ops 200, non-admin callers 403, unknown 404, nothing
enrolled 409); the effect (login then gives tokens with no MFA challenge, the old
recovery code, ticket and refresh cookie no longer work, one audit row with details);
an admin reset by root must enroll again; two concurrent resets → one 200 and one 409,
one audit row.
Results at time of writing¶
gofmt,go vet,go test -racewith the DB (9 packages): pass, on the branch rebased onto the merged staging.
How it was built¶
DeepSeek run scoped (Landlock) to services/admin_auth; interrupted twice by WSL
crashes and finished by continuation rounds. Claude review: transaction scope, rule
order and revocation checked; no changes needed.