Changelog
Guides

Turn on two-factor authentication

Turn authenticator two-factor authentication on and off, and issue recovery codes.

Users manage authenticator (TOTP) two-factor authentication through /identity/manage/2fa. POST requires a signed-in user, a CSRF token in the RequestVerificationToken header, and a fresh ReAuth proof. The body is Identity's TwoFactorRequest.

The steps assume a cookie session. Send credentials: 'include' and get a CSRF token from GET /identity/csrfToken before each POST. To sign in after 2FA is on, see Enter a two-factor code or a recovery code.

Video

Read the 2FA status

Send GET /identity/manage/2fa. The response is { "isTwoFactorEnabled": true } or false. This route needs no CSRF token and no ReAuth.

Turn on 2FA

  1. Complete step-up. See Require step-up before sensitive actions.
  2. Send POST /identity/manage/2fa with the body {}. If the user has no authenticator key, the response includes a new sharedKey.
  3. Show the shared key, or a QR code made from it, so that the user can add it to an authenticator app.
  4. If the ReAuth proof expired, complete step-up again. The default lifetime is 5 minutes (ReAuth.Lifetime).
  5. Send POST /identity/manage/2fa with { "enable": true, "twoFactorCode": "<6-digit code>" }.
  6. On 200, show the recoveryCodes from the response. When the user has no recovery codes left, turning on 2FA issues 10 new codes. The response also has isTwoFactorEnabled: true.

Do not send enable: true together with resetSharedKey: true. That request returns a validation problem with the key CannotResetSharedKeyAndEnable.

These validation problems can also come back from step 5:

  • RequiresTwoFactor: the request had no twoFactorCode.
  • InvalidTwoFactorCode: the code did not match the shared key.

Turn off 2FA

  1. Complete step-up.
  2. Send POST /identity/manage/2fa with { "enable": false }.
  3. On 200, the response has isTwoFactorEnabled: false.

To rotate the authenticator key, send { "resetSharedKey": true }. That request also turns 2FA off. Turn it on again with a code from the new key.

Use the other request fields

FieldEffect
resetRecoveryCodesIssues 10 new recovery codes and returns them in recoveryCodes.
forgetMachineClears the two-factor remember-client cookie.

A persistent cookie login with a valid authenticator code sets the remember-client cookie. Later password logins from that browser skip the 2FA challenge until the user sends forgetMachine or logs out. See Remembered browsers.

Know which sign-ins skip 2FA

Passkey sign-in and GitHub or Google sign-in do not ask for a 2FA code, even when 2FA is on. See Security model.