docs: clarify verification storage for OTP flows (#8569)

Signed-off-by: Gautam Manchandani <manchandanigautam@gmail.com>
This commit is contained in:
Gautam Manchandani
2026-03-13 02:36:00 +05:30
committed by GitHub
parent d59923cf6c
commit c2ee5520be
5 changed files with 19 additions and 12 deletions

View File

@@ -124,7 +124,7 @@ If you're using Cloudflare D1 with Drizzle or Prisma, use [`cloudflare:workers`]
## Secondary Storage
Secondary storage in Better Auth allows you to use key-value stores for managing session data, rate limiting counters, etc. This can be useful when you want to offload the storage of intensive records to a high performance storage or even RAM.
Secondary storage in Better Auth allows you to use key-value stores for managing session data, verification records, rate limiting counters, and other short-lived auth data. This can be useful when you want to offload the storage of intensive records to a high performance storage or even RAM.
### Implementation

View File

@@ -532,7 +532,7 @@ these are options for OTP.
default: 3,
},
storeOTP: {
description: "How to store the otp in the database. Whether to store it as plain text, encrypted or hashed. You can also provide a custom encryptor or hasher.",
description: "How to transform the stored OTP value, whether plain text, encrypted, or hashed. You can also provide a custom encryptor or hasher. The storage backend is controlled by the global verification config, so secondary storage can be used instead of the database.",
type: "string",
default: "plain",
},

View File

@@ -411,13 +411,13 @@ export const auth = betterAuth({
When the maximum attempts are exceeded, the `verifyOTP`, `signIn.emailOtp`, `verifyEmail`, and `resetPassword` methods will return an error with code `TOO_MANY_ATTEMPTS`.
- `storeOTP`: The method to store the OTP in your database, whether `encrypted`, `hashed` or `plain` text. Default is `plain` text.
- `storeOTP`: The method used to transform the OTP before it is stored by Better Auth's verification layer, whether `encrypted`, `hashed` or `plain` text. Default is `plain` text.
<Callout>
Note: This will not affect the OTP sent to the user, it will only affect the OTP stored in your database.
Note: This will not affect the OTP sent to the user. It only affects the stored OTP value. The storage backend itself is controlled by the global [`verification`](/docs/reference/options#verification) config, so if you configure `secondaryStorage`, these verification records can live there instead of the database.
</Callout>
Alternatively, you can pass a custom encryptor or hasher to store the OTP in your database.
Alternatively, you can pass a custom encryptor or hasher to control how the stored OTP value is persisted.
**Custom encryptor**

View File

@@ -149,10 +149,12 @@ and a `ctx` context object as the second parameter.
default, we return a long and cryptographically secure string.
</Callout>
**storeToken**: The `storeToken` function is called to store the magic link token in the database. The default value is `"plain"`.
**storeToken**: The `storeToken` function controls how the magic link token is transformed before it is stored by Better Auth's verification layer. The default value is `"plain"`.
The `storeToken` function can be one of the following:
- `"plain"`: The token is stored in plain text.
- `"hashed"`: The token is hashed using the default hasher.
- `{ type: "custom-hasher", hash: (token: string) => Promise<string> }`: The token is hashed using a custom hasher.
The storage backend itself is controlled by the global [`verification`](/docs/reference/options#verification) config. If you configure `secondaryStorage`, magic link verification records can be stored there instead of the database.

View File

@@ -192,7 +192,7 @@ Read more about databases [here](/docs/concepts/database).
## `secondaryStorage`
Secondary storage configuration used to store session and rate limit data.
Secondary storage configuration used to store session data, verification records, and rate limit data.
```ts
import { betterAuth } from "better-auth";
@@ -491,12 +491,13 @@ Verification configuration options.
```ts
import { betterAuth } from "better-auth";
export const auth = betterAuth({
secondaryStorage: {
// your Redis or KV implementation
},
verification: {
modelName: "verifications",
fields: {
userId: "user_id"
},
disableCleanup: false
disableCleanup: false,
storeIdentifier: "hashed",
storeInDatabase: false
},
})
```
@@ -504,6 +505,10 @@ export const auth = betterAuth({
- `modelName`: The model name for the verification table
- `fields`: Map fields to different column names
- `disableCleanup`: Disable cleaning up expired values when a verification value is fetched
- `storeIdentifier`: How to store verification identifiers such as tokens and OTP keys. Supports `"plain"`, `"hashed"`, or a custom hasher. You can also use `{ default, overrides }` to apply different strategies per identifier prefix.
- `storeInDatabase`: Store verification records in the database even when `secondaryStorage` is configured. Default is `false`.
If `secondaryStorage` is configured, verification records are stored there by default. That behavior applies to flows that use Better Auth's shared verification layer, including OTP and magic-link style flows.
## `rateLimit`