diff --git a/docs/content/docs/concepts/database.mdx b/docs/content/docs/concepts/database.mdx index 8c7e0ec9db..78eca210fc 100644 --- a/docs/content/docs/concepts/database.mdx +++ b/docs/content/docs/concepts/database.mdx @@ -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 diff --git a/docs/content/docs/plugins/2fa.mdx b/docs/content/docs/plugins/2fa.mdx index b03c655644..dab2dfb073 100644 --- a/docs/content/docs/plugins/2fa.mdx +++ b/docs/content/docs/plugins/2fa.mdx @@ -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", }, diff --git a/docs/content/docs/plugins/email-otp.mdx b/docs/content/docs/plugins/email-otp.mdx index 6a93e2f5e1..aa84cd5c74 100644 --- a/docs/content/docs/plugins/email-otp.mdx +++ b/docs/content/docs/plugins/email-otp.mdx @@ -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. -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. -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** diff --git a/docs/content/docs/plugins/magic-link.mdx b/docs/content/docs/plugins/magic-link.mdx index 16b2146aa5..40c59e342e 100644 --- a/docs/content/docs/plugins/magic-link.mdx +++ b/docs/content/docs/plugins/magic-link.mdx @@ -149,10 +149,12 @@ and a `ctx` context object as the second parameter. default, we return a long and cryptographically secure string. -**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 }`: 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. diff --git a/docs/content/docs/reference/options.mdx b/docs/content/docs/reference/options.mdx index 75960d851d..93b203d911 100644 --- a/docs/content/docs/reference/options.mdx +++ b/docs/content/docs/reference/options.mdx @@ -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`