Skip to main content

Context

Every credential-stuffing incident in this category ends the same way: a reused password, no second factor, and a full read of a customer database. MFA is the single control with the best ratio of effort to prevented incidents, and it is table stakes for any B2B platform holding personal data. Two design positions. TOTP, not SMS. SMS is phishable, SIM-swappable and costs money per message; TOTP works offline, costs nothing and is what every authenticator app already does. And recovery codes are the recovery path, not support. A support-mediated MFA reset is a social engineering target, and it is the door attackers actually knock on. The open question the PRD carries — mandatory for everyone or only for sensitive roles — is a real trade. Mandatory everywhere is safer and adds friction for a marketing assistant who can only read a dashboard; mandatory for sensitive permissions targets the risk precisely and needs the permission model to be right, which FS-IDN-0002 makes it.

Scope (normative)

  • TOTP enrolment, verification and removal.
  • Single-use recovery codes, hashed, generated as a set.
  • MFA challenge during sign-in and on re-authentication.
  • Enforcement policy: mandatory for roles holding sensitive permissions.
  • Organization-level policy allowing a tenant to require MFA for everyone.

Non-scope (normative)

  • SMS as a factor. Explicitly not offered — phishable, SIM-swappable, and it costs per message for weaker security than a free app.
  • Hardware keys as a distinct factor. A passkey (FS-IDN-0003) already covers that ground.
  • MFA for members. Members authenticate by OTP or delegated token, and both are already single-use.
  • Support-mediated MFA reset — see behaviour rule 6.

Behaviour (normative)

  1. TOTP follows the standard 30-second window with a ±1 window tolerance for clock drift. A code is single-use: replaying it inside its window is rejected.
  2. Enrolment is not complete until a code is verified. An enrolment that stores a secret without proving the user can generate from it locks people out.
  3. Recovery codes are generated as a set of ten at enrolment, shown exactly once, stored hashed, and each is single-use. Using one notifies the user.
  4. Fewer than three unused recovery codes remaining warns the user and offers regeneration. Regenerating invalidates the whole previous set.
  5. Disabling MFA requires re-authentication and a current second factor, and notifies the user.
  6. Support cannot reset MFA. A user who loses both their authenticator and their recovery codes is recovered by an organization owner, and if the last owner is the one locked out, by a documented manual process with identity verification that is deliberately slow. Making this easy is making the attack easy.
  7. A role holding any is_sensitive permission requires MFA. Assigning such a role to a user without MFA grants it but blocks its use until they enrol, and tells them why.
  8. A tenant may set an organization-wide requirement, which applies to every member of that organization on their next sign-in.
  9. The MFA challenge is rate-limited and locks out progressively, like any credential path.

Data (normative)

API (normative)

Events (normative)

identity.mfa.enabled and identity.mfa.disabled. Both also produce a notification to the user through messaging, because an MFA change the user did not make is the strongest possible signal that something is wrong.

Acceptance criteria (normative)

  1. A TOTP code cannot be replayed within its window.
  2. Clock drift of ±30 seconds still verifies; ±90 does not.
  3. Enrolment without a verified code leaves no active factor.
  4. A recovery code works once and notifies the user; the same code fails afterwards.
  5. Regenerating recovery codes invalidates every previous one.
  6. Assigning a role with a sensitive permission to a user without MFA grants it and blocks its use until enrolment, with a typed reason.
  7. An organization-wide requirement applies on the next sign-in for every member.
  8. Disabling MFA without re-authentication and a current factor is rejected.
  9. Negative: no support endpoint, admin action or Ops tool can reset a user’s MFA.

Execution

Single slice, synchronous command. TOTP through Better Auth’s plugin; secrets encrypted at column level. Notifications go through messaging by event.

Open questions

Changelog

Delivery record

Not implemented yet.