Multi-factor authentication
Torii’s multi-factor authentication (MFA) adds a second factor to sign-in: a time-based one-time password (TOTP) from an authenticator app such as Google Authenticator, 1Password, or Authy. The authenticator app is the counted second factor; email is never used as a factor.
MFA is a Pro plan and up feature.
How it fits together
Section titled “How it fits together”- Enrollment. A signed-in user turns on two-factor from their account. Torii generates a secret, the SDK shows a QR code and a manual key, and the user confirms with a 6-digit code. Torii then reveals a set of single-use recovery codes.
- Sign-in challenge. Once a user has a confirmed factor, password sign-in resolves to a challenge step instead of a session. The user enters a code from their app (or a recovery code) to finish signing in.
- Lockout recovery. A user who loses both their authenticator and their recovery codes is recovered by an operator who disables the factor from the dashboard. There is no email self-reset of the second factor: that would defeat the point of the factor.
Enrollment in the SDK
Section titled “Enrollment in the SDK”The prebuilt <UserProfile> already includes a
two-factor row in its Security section, so most apps get enrollment for free.
For a custom account page, drop in <MfaEnrollment>:
import { SignedIn, MfaEnrollment } from '@torii-js/torii-react';
<SignedIn> <MfaEnrollment /></SignedIn>;To build your own UI, use the headless useMfaEnrollment
hook: it exposes enroll, confirm, disable, and regenerate, and returns
the otpauth:// URI plus the manual secret so you can render your own QR code.
The shared secret and recovery codes are returned once. Torii stores the secret encrypted and the recovery codes hashed; they can’t be read back, so show them to the user immediately and prompt them to save the recovery codes.
The sign-in challenge
Section titled “The sign-in challenge”No extra wiring is needed: <SignIn> renders the MFA
challenge step automatically when an account has a confirmed factor. The user
sees a code field with a “use a recovery code instead” toggle, and the session
is only created once a valid code is verified. The headless
useSignIn hook surfaces the same flow as an
mfa-required state with a verifyMfa(code) action.
Requiring MFA for everyone
Section titled “Requiring MFA for everyone”By default each user chooses whether to enable two-factor. To require it for an
entire environment, turn on Require MFA at sign-in in the dashboard
(environment settings). With enforcement on, a user without a factor must enroll
to finish the ceremony (on both sign-in and sign-up) and no session is
minted until they do, so an account can never sit unprotected. <SignIn> and
<SignUp> render the enrollment step automatically; the headless useSignIn /
useSignUp hooks surface it as an mfa-enrollment-required state with a
confirmMfaEnrollment(code) action.
OAuth is gated too. OAuth sign-in and OAuth sign-up enforce the second factor
just like the password paths: in an enforced environment the callback returns
without a session and Torii renders the same challenge (existing factor) or
enrollment (no factor yet) step over your app, applying the session only once the
factor is verified. No extra wiring is needed beyond mounting <ToriiProvider>.
Recovery codes
Section titled “Recovery codes”Recovery codes are single-use. Each one works in place of an authenticator code at the sign-in challenge. Users can regenerate them at any time (which invalidates the old set) from the same security surface; regenerating requires a current authenticator code.
Lockout recovery (operators)
Section titled “Lockout recovery (operators)”When a user loses access to both their authenticator and their recovery codes, an operator disables the factor from the dashboard Users screen (“Disable MFA”). This removes the factor and the recovery codes, letting the user sign in with their password and enroll again. The action is recorded in the audit log.
Labels & localization
Section titled “Labels & localization”Every string in the enrollment widget and the sign-in challenge is overridable
through the label system: the mfa* keys on ToriiSignupLabels. See
Labels.