Docs

Multi-factor authentication

Let app users protect their account with an authenticator app or SMS codes and one-time backup codes: enrolment, signing in with a second factor, recovery, lockouts and turning it off.

Updated Oct 17, 2026Open in console

With MFA on, signing in needs a password (or link, or provider) and a code from something the user has. Sovaria supports authenticator apps (TOTP — Google Authenticator, 1Password, Authy…), SMS codes and backup codes. MFA for app users is available on every plan; hardware security keys are listed in Plan limits.

FreeProUltraTeamEnterprise
Multi-factor authentication for app users✓✓✓✓✓
Hardware-key MFA————✓

Turn on an authenticator app

  1. Start enrolment

    For the signed-in user, enroll returns a secret, an otpauth:// URI to show as a QR code, and backup codes.

    const { totp_uri, secret, backup_codes } = await sovaria.auth.mfa.enroll()
    showQrCode(totp_uri)          // or let the user type `secret`
    showOnce(backup_codes)        // they are never shown again
  2. Confirm with a code

    The user enters the 6-digit code from their app.

    await sovaria.auth.verifyMfa(code)
  3. Done

    sovaria.auth.mfa.status() now reports MFA on. Future sign-ins ask for a code.

Sign in with a second factor

const result = await sovaria.auth.signIn(email, password)
if (result.mfa_required) {
  const code = await askForCode()                  // authenticator, SMS or a backup code
  await sovaria.auth.verifyMfa(code)               // 'totp' (default) or verifyMfa(code, 'phone')
}
// signed in, with a two-factor (aal2) session

After the password step the client holds only a short challenge token; nothing else works until the code is verified. A verified session is marked aal2 in its token (auth.jwt() ->> 'aal'), so you can require it for sensitive actions.

SMS codes

Users can add a phone number as a factor (POST /api/v1/auth/mfa/phone/enroll, then verify-enrollment with the code they receive). At sign-in, POST /api/v1/auth/mfa/phone/challenge sends a code and verifyMfa(code, 'phone') checks it. Codes are rate-limited per user to stop abuse and SMS costs.

Backup codes and recovery

  • Each backup code works once, in place of an authenticator code. Show them only at enrolment; Sovaria stores only hashes.
  • GET /api/v1/auth/mfa/backup-codes reports how many are left; POST /api/v1/auth/mfa/backup-codes/regenerate issues a new set (the old set stops working).
  • A user who lost their phone and codes can't pass MFA. Your team can reset MFA for them from the Users page after checking who they are.

Built-in protection

  • Lockout: 5 wrong codes within 5 minutes lock the factor for a while (423 with Retry-After) — per user, so rotating IPs doesn't help an attacker.
  • No replay: a code that was accepted can't be used again (401 CODE_ALREADY_USED).
  • Turning MFA off needs a current code: sovaria.auth.mfa.disable(code).

Project settings

Under Authentication › Multi-factor choose which second factors your users can add.

Something unclear or missing on this page?Tell us