Files
better-auth/docs/content/docs/plugins/2fa.mdx
Bereket Engida 7c5e30afed docs: fix typo
2024-11-04 09:53:45 +03:00

411 lines
12 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: Two-Factor Authentication (2FA)
description: Enhance your app's security with two-factor authentication
---
`OTP` `TOTP` `Backup Codes` `Trusted Devices`
Two-Factor Authentication (2FA) adds an extra security step when users log in. Instead of just using a password, they'll need to provide a second form of verification. This makes it much harder for unauthorized people to access accounts, even if they've somehow gotten the password.
This plugin offers two main methods to do a second factor verification:
1. **OTP (One-Time Password)**: A temporary code sent to the user's email or phone.
2. **TOTP (Time-based One-Time Password)**: A code generated by an app on the user's device.
**Additional features include:**
- Generating backup codes for account recovery
- Enabling/disabling 2FA
- Managing trusted devices
## Installation
<Steps>
<Step>
### Add the plugin to your auth config
Add the two-factor plugin to your auth configuration and specify your app name as the issuer.
```ts title="auth.ts"
import { betterAuth } from "better-auth"
import { twoFactor } from "better-auth/plugins" // [!code highlight]
export const auth = betterAuth({
// ... other config options
appName: "My App", // provide your app name. It'll be used as an issuer. // [!code highlight]
plugins: [
twoFactor() // [!code highlight]
]
})
```
</Step>
<Step>
### Migrate the database
Run the migration or generate the schema to add the necessary fields and tables to the database.
<Tabs items={["migrate", "generate"]}>
<Tab value="migrate">
```bash
npx @better-auth/cli migrate
```
</Tab>
<Tab value="generate">
```bash
npx @better-auth/cli generate
```
</Tab>
</Tabs>
See the [Schema](#schema) section to add the fields manually.
</Step>
<Step>
### Add the client Plugin
Add the client plugin and Specify where the user should be redirected if they need to verify 2nd factor
```ts title="client.ts"
import { createAuthClient } from "better-auth/client"
import { twoFactorClient } from "better-auth/client/plugins"
const client = createAuthClient({
plugins: [
twoFactorClient({
twoFactorPage: "/two-factor" //redirect for two factor verification is required // [!code highlight]
})
]
})
```
</Step>
</Steps>
## Usage
### Enabling 2FA
To enable two-factor authentication, call `twoFactor.enable` with the user's password:
```ts title="two-factor.ts"
const { data } = await authClient.twoFactor.enable({
password: "password" // user password required
})
```
When 2FA is enabled:
- An encrypted `secret` and `backupCodes` are generated.
- `enable` returns `totpURI` and `backupCodes`.
Note: `twoFactorEnabled` wont be set to `true` until the user verifies their TOTP code.
To verify, display the QR code for the user to scan with their authenticator app. After they enter the code, call `verifyTotp`:
```ts
await authClient.twoFactor.verifyTotp({
code: "" // user input
})
```
<Callout>
You can skip verification by setting `skipVerificationOnEnable` to true in your plugin config.
</Callout>
### Sign In with 2FA
When a user with 2FA enabled tries to sign in via email, they will be redirected to the `twoFactorPage` (specified in your client config) unless they're using a trusted device.
```ts title="sign-in.ts"
await authClient.signIn.email({
email: "user@example.com",
password: "password123",
})
```
By default, if a user has 2FA enabled, they will be redirected to the `twoFactorPage`. If you want to manage the 2FA verification directly within your application instead, simply pass `redirect:false` to the client plugin and handle the verification in the callback.
```ts title="sign-in.ts"
import { createAuthClient } from "better-auth/client";
import { twoFactorClient } from "better-auth/client/plugins";
const authClient = createAuthClient({
plugins: [twoFactorClient({
redirect: false
})]
})
await authClient.signIn.email({
email: "user@example.com",
password: "password123",
}, {
async onSuccess(context) {
if (context.data.twoFactorRedirect) {
// Handle the 2FA verification in place
}
}
}
})
```
### TOTP
TOTP is a time-based one-time password algorithm that generates a code based on the current time.
#### Getting TOTP URI
After enabling 2FA, you can get the TOTP URI to display to the user.
```ts
const { data, error } = await client.twoFactor.getTotpUri({
password: "password" // user password required
})
if (data) {
// Use data.totpURI to generate a QR code or display to the user
}
```
**Example: Using React**
```tsx title="user-card.tsx"
import QRCode from "react-qr-code";
export default function UserCard(){
const { data: session } = client.useSession();
const { data: qr } = useQuery({
queryKey: ["two-factor-qr"],
queryFn: async () => {
const res = await client.twoFactor.getTotpUri();
return res.data;
},
enabled: !!session?.user.twoFactorEnabled,
});
return (
<QRCode value={qr?.totpURI || ""} />
)
}
```
#### Verifying TOTP
After the user has entered their 2FA code, you can verify it usinng `twoFactor.verifyTotp` method.
```ts
const verifyTotp = async (code: string) => {
const { data, error } = await client.twoFactor.verifyTotp({ code })
}
```
### OTP
OTP (One-Time Password) is similar to TOTP but the code is sent directly to the user, and the code is valid for 3 mins by default.
Before using OTP to verify the second factor, you need to configure `sendOTP` in your Better Auth instance. This function is responsible for sending the OTP to the user's email, phone, or any other method supported by your application.
```ts title="auth.ts"
import { betterAuth } from "better-auth"
import { twoFactor } from "better-auth/plugins"
export const auth = betterAuth({
plugins: [
twoFactor({
otpOptions: {
async sendOTP(user, otp) {
// send otp to user
},
},
})
]
})
```
#### Sending OTP
Sending an OTP is done by calling the `twoFactor.sendOtp` function. This function will trigger your sendOTP implementation that you provided in the Better Auth configuration.
```ts
const { data, error } = await authClient.twoFactor.sendOtp()
if (data) {
// redirect or show the user to enter the code
}
```
#### Verifying OTP
After the user has entered their OTP code, you can verify it
```ts
const verifyOtp = async (code: string) => {
await authClient.twoFactor.verifyOtp({ code }, {
onSuccess(){
//redirect the user on success
},
onError(ctx){
alert(ctx.error.message)
}
})
}
```
### Backup Codes
Backup codes are generated and stored in the database. This can be used to recover access to the account if the user loses access to their phone or email.
#### Generating Backup Codes
Generate backup codes for account recovery:
```ts
const { data, error } = await authClient.twoFactor.generateBackupCodes({
password: "password" // user password required
})
if (data) {
// Show the backup codes to the user
}
```
#### Using Backup Codes
You can now allow users to provider backup code as account recover method.
```ts
await authClient.twoFactor.verifyBackupCode({code: ""}, {
onSuccess(){
//redirect the user on success
},
onError(ctx){
alert(ctx.error.message)
}
})
```
### Trusted Devices
You can mark a device as trusted by passing `trustDevice` to `verifyTotp` or `verifyOtp`.
```ts
const verify2FA = async (code: string) => {
const { data, error } = await client.twoFactor.verifyTotp({
code,
callbackURL: "/dashboard",
trustDevice: true // Mark this device as trusted
})
if (data) {
// 2FA verified and device trusted
}
}
```
When `trustDevice` is set to `true`, the current device will be remembered for 60 days. During this period, the user won't be prompted for 2FA on subsequent sign-ins from this device. The trust period is refreshed each time the user signs in successfully.
## Schema
The plugin requires 1 additional fields in the `user` table and 1 additional table to store the two factor authentication data.
<DatabaseTable
fields={[
{ name: "twoFactorEnabled", type: "boolean", description: "Whether two factor authentication is enabled for the user.", isOptional: true },
]}
/>
Table: `twoFactor`
<DatabaseTable
fields={[
{ name: "secret", type: "string", description: "The secret used to generate the TOTP code.", isOptional: true },
{ name: "backupCodes", type: "string", description: "The backup codes used to recover access to the account if the user loses access to their phone or email.", isOptional: true },
]}
/>
## Options
### Server
**twoFactorTable**: The name of the table that stores the two factor authentication data. Default: `twoFactor`.
**skipVerificationOnEnable**: Skip the verification process before enabling two factor for a user.
**Issuer**: The issuer is the name of your application. It's used to generate totp codes. It'll be displayed in the authenticator apps.
**TOTP options**
these are options for TOTP.
<TypeTable
type={{
digits:{
description: 'The number of digits the otp to be',
type: 'number',
default: 6,
},
period: {
description: 'The period for otp in seconds.',
type: 'number',
default: 30,
},
}}
/>
**OTP options**
these are options for OTP.
<TypeTable
type={{
sendOTP: {
description: "a function that sends the otp to the user's email or phone number. It takes two parameters: user and otp",
type: "function",
},
period: {
description: 'The period for otp in seconds.',
type: 'number',
default: 30,
},
}}
/>
**Backup Code Options**
backup codes are generated and stored in the database when the user enabled two factor authentication. This can be used to recover access to the account if the user loses access to their phone or email.
<TypeTable
type={{
amount: {
description: "The amount of backup codes to generate",
type: "number",
default: 10,
},
length: {
description: "The length of the backup codes",
type: "number",
default: 10,
},
customBackupCodesGenerate: {
description: "A function that generates custom backup codes. It takes no parameters and returns an array of strings.",
type: "function",
},
}}
/>
### Client
To use the two factor plugin in the client, you need to add it on your plugins list.
```ts title="client.ts"
import { createAuthClient } from "better-auth/client"
import { twoFactorClient } from "better-auth/client/plugins"
const client = createAuthClient({
plugins: [
twoFactorClient({ // [!code highlight]
twoFactorPage: "/two-factor" // [!code highlight]
}) // [!code highlight]
] // [!code highlight]
})
```
**Options**
`twoFactorPage`: The page to redirect the user to after they have enabled 2-Factor. This is the page where the user will be redirected to verify their 2-Factor code.
`redirect`: If set to `false`, the user will not be redirected to the `twoFactorPage` after they have enabled 2-Factor.