Skip to content

Account security

A user turns on two-step verification by adding an authenticator app (TOTP) or a passkey. The first method a user adds gives them 10 single-use backup codes; removing the last method turns two-step verification off. From then on a sign-in with a password, an email code or a social provider asks for a second factor: an authenticator app code, a backup code or a passkey.

When Auth settings require two-step verification, a user without it signs in to a pending session with the task setup_mfa: it gets no session token until they add a method. Users then cannot remove their last method. A user who lost their devices can have it removed in the dashboard or with the Backend API (DELETE /v1/backend/users/{userId}/mfa), which also ends their sessions.

A passkey signs a user in on its own: it is both factors, and never asks for a second step. It can also be the second factor after a password. A passkey belongs to a domain (its RP ID):

  • Production: your domain (example.com for accounts.example.com), so a passkey added in your app on app.example.com also works on the hosted pages, and the other way round.
  • Development: each page’s own host, so a passkey added on localhost does not work on the hosted pages and the other way round.

Passkeys work only over HTTPS or on http://localhost, never on an IP address such as 127.0.0.1 (passkey_origin_unsupported). An account has at most 10.

Sensitive account changes need a recent identity check, not just a signed-in session: changing the password or the email addresses, adding or removing an authenticator app, backup codes or passkeys, connecting or removing a social account, and deleting the account. Zautha asks for the second factor (or the first, for a user without two-step verification) within the last 10 minutes, and otherwise answers reverification_required; the components ask the user to verify again and retry.

Your own sensitive endpoints can ask for the same. The session token’s fva claim says how many minutes ago the session verified each factor, and every server SDK checks it:

Requirement Window User with two-step verification User without it
strict 10 minutes second factor first factor
moderate 1 hour second factor first factor
lax 1 day second factor first factor
strict_mfa 10 minutes both factors first factor

A custom requirement sets the window (1 to 1,440 minutes) and the level (first_factor, second_factor or multi_factor). The first factor is the password, an email code or a link; the second is an authenticator app code or a backup code; a passkey can be either. When a check fails, your endpoint answers 403 reverification_required and useReverification (@zautha/react) asks the user to verify that level and retries. See @zautha/backend, @zautha/nextjs and Zautha.AspNetCore.

An impersonation session never passes a reverification check.