new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Better Auth · all subjects

two-factor authentication

34 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Two-Factor Authentication plugin installation

Add the twoFactor plugin to the auth configuration by importing it from 'better-auth/plugins'. Set appName in the auth config to provide the issuer name. Example: import { twoFactor } from 'better-auth/plugins'; then add twoFactor() to the plugins array in betterAuth config.

Two-Factor Authentication client plugin installation

Add the twoFactorClient plugin to the client configuration by importing from 'better-auth/client/plugins'. The client plugin must be added to the createAuthClient plugins array to enable 2FA functionality on the client side.

Two-Factor Authentication database migration

After adding the twoFactor plugin to auth config, run 'npx auth migrate' or 'npx auth generate' to create the necessary database tables and fields for 2FA functionality.

Enable Two-Factor Authentication endpoint

POST endpoint /two-factor/enable requires session. Parameters: password (string, optional, required for credential accounts, default 'secure-password'), issuer (string, optional, defaults to app-name defined in auth config). Returns encrypted secret and backupCodes. twoFactorEnabled won't be true until TOTP is verified unless skipVerificationOnEnable is true.

Disable Two-Factor Authentication endpoint

POST endpoint /two-factor/disable requires session. Parameters: password (string, optional, required for credential accounts). Disables 2FA for the user.

TOTP configuration in twoFactor plugin

TOTP options include: digits (number, default 6) - number of digits in OTP code; period (number, default 30) - period for TOTP in seconds. These control the TOTP algorithm behavior.

Get TOTP URI endpoint

POST endpoint /two-factor/get-totp-uri requires session. Parameters: password (string, optional, required for credential accounts). Returns TOTP URI that can be used to generate a QR code for scanning with authenticator app.

OTP configuration with sendOTP function

To use OTP for 2FA verification, configure sendOTP in twoFactor plugin options. The sendOTP function receives user and otp parameters and is responsible for sending the OTP to the user's email, phone, or other method.

OTP plugin options

OTP options include: sendOTP (function, required) - sends OTP to user; period (number, default 3) - period for OTP in minutes; storeOTP (string, default 'plain') - how to store OTP value (plain text, encrypted, or hashed with custom encryptor/hasher).

Send OTP endpoint

POST endpoint /two-factor/send-otp requires headers. Parameters: trustDevice (boolean, optional, default true) - if true, device is trusted for 30 days. Triggers the sendOTP implementation configured in Better Auth, sending OTP to user.

Verify OTP endpoint

POST endpoint /two-factor/verify-otp requires headers. Parameters: code (string, required, e.g. '012345') - OTP code to verify; trustDevice (boolean, optional, default true) - if true, device is trusted for 30 days.

Generate Backup Codes endpoint

POST endpoint /two-factor/generate-backup-codes requires session. Parameters: password (string, optional, required for credential accounts). Returns backup codes. Warning: when new codes are generated, old backup codes are deleted.

Verify Backup Code endpoint

POST endpoint /two-factor/verify-backup-code requires headers. Parameters: code (string, required, e.g. '123456') - backup code to verify; disableSession (boolean, optional, default false) - if true, session cookie won't be set; trustDevice (boolean, optional, default true) - if true, device is trusted for 30 days. After use, backup code is removed and can't be used again.

View Backup Codes endpoint

POST endpoint /two-factor/view-backup-codes is server-only. Parameters: userId (string or null, optional, default 'user-id') - user ID to view all backup codes. Should only be called if user has a fresh session.

Backup Code plugin options

Backup code options include: amount (number, default 10) - number of backup codes to generate; length (number, default 10) - length of backup codes; customBackupCodesGenerate (function) - custom function to generate backup codes, takes no parameters, returns array of strings; storeBackupCodes (string, default 'plain') - how to store codes in database (plain text or encrypted with custom encryptor).

Account lockout configuration for 2FA

Account lockout options include: enabled (boolean, default true) - whether account-level lockout is enforced; maxFailedAttempts (number, default 10) - consecutive failed verifications across challenges and factors before lockout; durationSeconds (number, default 900) - how long account stays locked. Limit applies per account across TOTP, OTP, and backup codes with shared counter. Successful verification resets counter. Locked attempts return HTTP 429 with ACCOUNT_TEMPORARILY_LOCKED error code.

Two-Factor Authentication database schema

User table requires twoFactorEnabled field (boolean, optional). TwoFactor table has fields: id (string, primary key), userId (string, foreign key to user.id), secret (string, used for TOTP), backupCodes (string, for account recovery), verified (boolean, whether TOTP secret verified during enrollment), failedVerificationCount (number, for account lockout), lockedUntil (date, optional, when account lockout expires, null when not locked).

Two-Factor plugin server options

Server options: twoFactorTable (string, default 'twoFactor') - table name for 2FA data; skipVerificationOnEnable (boolean) - skip verification before enabling 2FA; allowPasswordless (boolean) - allow enabling/managing 2FA without password for passwordless users (doesn't change which sign-in methods are challenged); issuer (string) - application name for TOTP display in authenticator apps.

Two-Factor client plugin options

Client plugin option: onTwoFactorRedirect (callback) - called when user needs to verify 2FA, receives context object with twoFactorMethods array (e.g. ['totp', 'otp']) containing enabled 2FA methods, can be used to redirect user to 2FA page. Alternative option: twoFactorPage (string) - page path to redirect to for 2FA verification (causes full page reload).

2FA sign-in flow and twoFactorRedirect response

When 2FA-enabled user signs in via credential endpoint (email, username, phone), response contains twoFactorRedirect set to true and twoFactorMethods array (e.g. ['totp', 'otp']). Use twoFactorMethods to decide which verification UI to show. 2FA sign-in enforcement applies to /sign-in/email, /sign-in/username, /sign-in/phone-number endpoints by default. Non-credential sign-in methods (email OTP, magic link, OAuth, passkey, anonymous) are not gated by 2FA by default.

2FA pending session behavior

When 2FA-enabled user signs in via credential endpoint, 2FA challenge is issued instead of completing sign-in. Pending session is discarded and ctx.context.newSession is reset to null - no authenticated session exists until second factor is verified. Server-side hooks reading ctx.context.newSession must null-check before accessing newSession.user to avoid throwing during 2FA challenge.

allowPasswordless option behavior for 2FA

Setting allowPasswordless: true allows passwordless users (passkeys, magic links, email OTP, OAuth/social, anonymous) to enable and manage 2FA without password. Password is still required if user has credential account. This option does not change which sign-in methods are challenged for 2FA.

TOTP code acceptance window

Better Auth follows standard practice by accepting TOTP codes from one period before and one after the current code, ensuring users can authenticate even with minor time delays on their device.

Trusted device duration

When trustDevice is set to true on verifyTotp or verifyOtp, the device is remembered for 30 days. During this period, user won't be prompted for 2FA on subsequent sign-ins from this device. Trust period is refreshed on each successful sign-in.

twoFactorPage redirect causes full page reload

Using the twoFactorPage option in twoFactorClient config will cause a full page reload when redirecting to 2FA page. To avoid page reloads, use onTwoFactorRedirect callback instead to handle redirect programmatically.

auth.api server-side 2FA handling with headers

When calling auth.api.signInEmail on server with 2FA-enabled user, it returns object with twoFactorRedirect set to true. Use 'in' operator to check twoFactorRedirect. authClient.twoFactor.* handles cookies automatically in browser, but auth.api.* calls require passing incoming request headers so Better Auth can read current 2FA state and set 2FA/session cookies. When chaining multiple auth.api calls, forward cookies from previous response into next call.

Example: Enable 2FA and display TOTP QR code in React

```tsx import { authClient } from "@/lib/auth-client" import QRCode from "react-qr-code"; export default function UserCard({ password }: { password: string }){ const { data: session } = authClient.useSession(); const { data: qr } = useQuery({ queryKey: ["two-factor-qr"], queryFn: async () => { const res = await authClient.twoFactor.getTotpUri({ password }); return res.data; }, enabled: !!session?.user.twoFactorEnabled, }); return ( <QRCode value={qr?.totpURI || ""} /> ) } ``` This example shows how to get TOTP URI and generate a QR code for user to scan with authenticator app.

Example: Handle 2FA redirect with onSuccess callback

```tsx import { authClient } from "@/lib/auth-client" await authClient.signIn.email({ email: "user@example.com", password: "password123", }, { async onSuccess(context) { if (context.data.twoFactorRedirect) { const methods = context.data.twoFactorMethods // e.g. ["totp", "otp"] // Show the appropriate 2FA verification UI based on available methods } }, } ) ``` This example shows how to detect 2FA redirect in sign-in response and handle based on available methods.

Example: Configure twoFactorClient with onTwoFactorRedirect

```ts import { createAuthClient } from "better-auth/client"; import { twoFactorClient } from "better-auth/client/plugins"; const authClient = createAuthClient({ plugins: [ twoFactorClient({ onTwoFactorRedirect({ twoFactorMethods }){ // twoFactorMethods is e.g. ["totp", "otp"] // Handle the 2FA verification globally }, }), ], }); ``` This example shows how to use onTwoFactorRedirect callback to handle 2FA verification globally.

Example: Configure twoFactorClient with twoFactorPage

```ts import { createAuthClient } from "better-auth/client"; import { twoFactorClient } from "better-auth/client/plugins"; const authClient = createAuthClient({ plugins: [ twoFactorClient({ twoFactorPage: "/two-factor", // the page to redirect if a user needs to verify their 2nd factor }), ], }); ``` This example shows how to use twoFactorPage option to redirect users to 2FA page (causes full page reload).

Example: Trust device after 2FA verification

```ts const verify2FA = async (code: string) => { const { data, error } = await authClient.twoFactor.verifyTotp({ code, trustDevice: true, // Mark this device as trusted }) if (data) { // 2FA verified and device trusted } } ``` This example shows how to set trustDevice to true when verifying TOTP to remember device for 30 days.

Example: Configure twoFactor plugin with OTP

```ts import { betterAuth } from "better-auth" import { twoFactor } from "better-auth/plugins" export const auth = betterAuth({ plugins: [ twoFactor({ otpOptions: { async sendOTP({ user, otp }, ctx) { // send otp to user }, }, }) ] }) ``` This example shows how to configure sendOTP function in twoFactor plugin to handle OTP delivery.

Example: Configure twoFactor plugin with issuer

```ts twoFactor({ issuer: "my-app-name" }) ``` This example shows how to set custom issuer name that will appear in authenticator apps instead of default app name or 'Better Auth'.

Example: Server-side 2FA handling with auth.api

```ts import { auth } from "@/lib/auth" const { headers: responseHeaders, response } = await auth.api.signInEmail({ returnHeaders: true, body: { email: "test@test.com", password: "test", }, }); if ("twoFactorRedirect" in response) { // response.twoFactorMethods is e.g. ["totp", "otp"] // Forward the cookies from responseHeaders into the next auth.api 2FA call. // Handle the 2FA verification in place } ``` This example shows how to detect and handle 2FA redirect when using auth.api on server, checking with 'in' operator and forwarding cookies.

Give your agent this brain