Skip to main content

Prerequisites

Before this page: create and initialize a Dynamic client (see Creating a Dynamic client, Initializing the Dynamic client), and have a signed-in user (see Email & SMS OTP sign-in or Social OAuth).

What you’ll build

  • Enroll an authenticator app (TOTP) and verify it with a code.
  • Show recovery codes so users can get back in if they lose the device.
  • Manage devices — list them, set a default, and remove one.

Before you start: elevated access tokens

registerTotpMfaDevice and deleteMfaDevice (below) both require an elevated access token — TokenScope.Credentiallink and TokenScope.Credentialunlink respectively — with no fallback. Calling either directly returns a 403 (Elevated access token required) unless the user already holds one. Check first with checkStepUpAuth({ scope }). If it’s required, re-verify the user with an existing credential (email OTP shown here) and pass the scope in requestedScopes — the resulting elevated token is picked up automatically by the gated call that follows.

Enrolling a TOTP device

registerTotpMfaDevice returns { id, secret, uri }. Render uri as a QR code (with your preferred QR library) and show secret as a manual-entry fallback. The user scans it, then enters the 6-digit code so you can verify with authenticateTotpMfaDevice.

Showing recovery codes

After enrolling, show the user their recovery codes once and record that they saw them with acknowledgeRecoveryCodes.

Managing devices

List enrolled devices, set a default, and remove one.
Removal needs the Credentialunlink elevated access token from Before you start: elevated access tokens. A plain authenticateTotpMfaDevice({ code, deviceId }) re-verify — with no requestedScopes — does not produce a token deleteMfaDevice will accept, even though its mfaAuthToken parameter is optional. Pass requestedScopes: [TokenScope.Credentialunlink] on the re-verification call instead; deleteMfaDevice then picks up the resulting elevated token automatically.

Handling errors

See also

Last modified on July 22, 2026