Two-factor authentication
Users can protect their account with an authenticator app (Google Authenticator, 1Password, Authy, Microsoft Authenticator…). Sign-in then asks for a 6-digit code after a correct password. It's available in every project; users choose to turn it on.
How it works
- The user turns it on in your app: they confirm their password, scan a QR code, and enter a first code. They also get 10 one-time backup codes.
- When they sign in with their password, Tausi doesn't create a session yet. It answers with
twoFactorRequiredand a short-lived challenge. - Your app asks for the code and sends it. A correct code (or an unused backup code) creates the session, as a normal sign-in would.
| Rule | |
|---|---|
| Time to enter the code | 5 minutes, then sign in again (TWO_FACTOR_CHALLENGE_EXPIRED). |
| Wrong codes | 5 per sign-in attempt. After 10 in a row the factor locks for 15 minutes. |
| Backup codes | 10, each usable once. Users can generate a new set (the old ones stop working). |
| Notifications | The user is emailed when two-factor is turned on or off, or the backup codes change. |
| Social sign-in | Google, Apple, GitHub and the others aren't asked for a code: those providers have their own two-factor. |
| Authenticator name | Your project's name, so users can tell your app apart in their authenticator. |
Sign-in with a code
Users without two-factor sign in in one step, exactly as before. For users with it on, add a code step. In JavaScript and Node.js signIn() returns { twoFactorRequired: true }; Python, PHP and Flutter raise a TwoFactorRequired error, so existing code that only catches TausiError keeps working.
const result = await tausi.signIn({ email, password });
if (result.twoFactorRequired) {
// Show a code field. The client keeps the pending sign-in for 5 minutes.
return setStep("code");
}
// signed in: result.user, result.session
// When the user submits the code:
await tausi.verifyTwoFactor({ code }); // 6-digit code from the app
await tausi.verifyTwoFactor({ code, method: "backup_code" }); // or one of their backup codesTurning it on and off
Add a security page to your app with these calls. The user has to be signed in, and each step that changes the setting asks for their password. Turning it on or off signs the current device in with a fresh token; the SDKs store it for you.
const { totpURI, backupCodes } = await tausi.setUpTwoFactor(password);
// Show totpURI as a QR code (e.g. the "qrcode" package) and the backup codes once.
await tausi.enableTwoFactor(codeFromApp); // now it's on; the user is emailed
await tausi.regenerateBackupCodes(password);
await tausi.disableTwoFactor(password);
tausi.getState().user?.twoFactorEnabled; // show the right buttonTry it without writing code: the test app's account page (Dashboard → project → Quickstart → Open test app) has a working two-factor section.
When a user loses their phone
With a backup code they can sign in and set two-factor up again. Without one, turn it off for them, after checking it's really them. They're emailed, and the action is recorded in your project's logs.
- Dashboard: Users → the user → Turn off two-factor (asks for your password).
- Your backend, with the secret key:
await tausi.users.resetTwoFactor(userId); // Node.js
backend.users.reset_two_factor(user_id) # Python
$backend->resetTwoFactor($userId); // PHP
curl -X DELETE https://tausi-auth.onrender.com/api/v1/backend/users/$USER_ID/two-factor -H "Authorization: Bearer sk_…"Over plain HTTP
POST /api/v1/auth/sign-in/email { "email": "…", "password": "…" }
→ 200 { "data": { "twoFactorRequired": true, "twoFactorToken": "…" } }
POST /api/v1/auth/two-factor/verify { "method": "totp", "code": "123456", "twoFactorToken": "…" }
→ 200 { "data": { "user": {…}, "session": {…}, "sessionToken": "…" } }
POST /api/v1/auth/two-factor/setup { "password": "…" } (signed in) → { totpURI, backupCodes }
POST /api/v1/auth/two-factor/enable { "code": "123456" } → { enabled: true, sessionToken }
POST /api/v1/auth/two-factor/disable { "password": "…" } → { enabled: false, sessionToken }
POST /api/v1/auth/two-factor/backup-codes { "password": "…" } → { backupCodes }method is totp or backup_code. All error codes are on the HTTP API page.