[GH-ISSUE #8542] Allow storage option for OTP and email verification tokens, and allow 'secondary-storage' #11115

Closed
opened 2026-04-13 07:29:23 -05:00 by GiteaMirror · 6 comments
Owner

Originally created by @kvanbere on GitHub (Mar 11, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/8542

Is this suited for github?

  • Yes, this is suited for github

Observation:
Generation of email verification, magic link and two factor OTPs creates many verification rows in the table.
This creates database traffic that could be unnecessary.
It seems like these would be well-suited to secondary storage (KV storage) if available.

Describe the solution you'd like

Add a storage option to each plugin that generates one time passwords.
If all plugins have the storage option set to secondary-storage, then a table won't be created to store codes.

Describe alternatives you've considered

N/A

Additional context

No response

Originally created by @kvanbere on GitHub (Mar 11, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/8542 ### Is this suited for github? - [x] Yes, this is suited for github ### Is your feature request related to a problem? Please describe. **Observation:** Generation of email verification, magic link and two factor OTPs creates many `verification` rows in the table. This creates database traffic that could be unnecessary. It seems like these would be well-suited to secondary storage (KV storage) if available. ### Describe the solution you'd like Add a `storage` option to each plugin that generates one time passwords. If all plugins have the `storage` option set to `secondary-storage`, then a table won't be created to store codes. ### Describe alternatives you've considered N/A ### Additional context _No response_
GiteaMirror added the enhancementlocked labels 2026-04-13 07:29:23 -05:00
Author
Owner

@GautamBytes commented on GitHub (Mar 12, 2026):

The behavior you were asking for is already supported, but it’s configured through Better Auth’s global verification storage settings rather than per-plugin storage options.

I opened a docs PR to make that clearer here

<!-- gh-comment-id:4047259953 --> @GautamBytes commented on GitHub (Mar 12, 2026): The behavior you were asking for is already supported, but it’s configured through Better Auth’s global verification storage settings rather than per-plugin `storage` options. I opened a docs PR to make that clearer [here](https://github.com/better-auth/better-auth/pull/8569)
Author
Owner

@kvanbere commented on GitHub (Mar 13, 2026):

@GautamBytes even better, thank you!

<!-- gh-comment-id:4054073168 --> @kvanbere commented on GitHub (Mar 13, 2026): @GautamBytes even better, thank you!
Author
Owner

@kvanbere commented on GitHub (Mar 14, 2026):

Hey @GautamBytes I'm using v1.5.4 with the following configuration:

    secondaryStorage: { /* ... */ },
    verification: {
      storeIdentifier: 'hashed',
      storeInDatabase: false,
      disableCleanup: true,
    },

I see the session table disappear from the schema, that's working fine.

But it's still generating a table in my database schema for verifications:

export const verification = sqliteTable(
  "verification",
  {
    id: text("id").primaryKey(),
    identifier: text("identifier").notNull(),
    value: text("value").notNull(),
    expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(),
    createdAt: integer("created_at", { mode: "timestamp_ms" })
      .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
      .notNull(),
    updatedAt: integer("updated_at", { mode: "timestamp_ms" })
      .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
      .$onUpdate(() => /* @__PURE__ */ new Date())
      .notNull(),
  },
  (table) => [index("verification_identifier_idx").on(table.identifier)],
);

Is there a gotcha here?

<!-- gh-comment-id:4059582251 --> @kvanbere commented on GitHub (Mar 14, 2026): Hey @GautamBytes I'm using `v1.5.4` with the following configuration: ```ts secondaryStorage: { /* ... */ }, verification: { storeIdentifier: 'hashed', storeInDatabase: false, disableCleanup: true, }, ``` I see the `session` table disappear from the schema, that's working fine. But it's still generating a table in my database schema for verifications: ```ts export const verification = sqliteTable( "verification", { id: text("id").primaryKey(), identifier: text("identifier").notNull(), value: text("value").notNull(), expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(), createdAt: integer("created_at", { mode: "timestamp_ms" }) .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) .notNull(), updatedAt: integer("updated_at", { mode: "timestamp_ms" }) .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) .$onUpdate(() => /* @__PURE__ */ new Date()) .notNull(), }, (table) => [index("verification_identifier_idx").on(table.identifier)], ); ``` Is there a gotcha here?
Author
Owner

@GautamBytes commented on GitHub (Mar 14, 2026):

I suspect this might actually be a version mismatch with the CLI rather than your config.

Better Auth actually moved to a new standalone CLI (npx auth) in 1.5. The old @better-auth/cli package is still stuck on 1.4.x. If you generated the schema using the old CLI package, it makes sense that it's still emitting the verification table because it doesn't recognize the storeInDatabase flag yet.

Could you share:

  • The exact command you used to run the generator?
  • What you get when you run npm ls better-auth auth @better-auth/cli @better-auth/core?
<!-- gh-comment-id:4060326229 --> @GautamBytes commented on GitHub (Mar 14, 2026): I suspect this might actually be a version mismatch with the CLI rather than your config. Better Auth actually moved to a new standalone CLI (npx auth) in 1.5. The old @better-auth/cli package is still stuck on 1.4.x. If you generated the schema using the old CLI package, it makes sense that it's still emitting the verification table because it doesn't recognize the storeInDatabase flag yet. Could you share: - The exact command you used to run the generator? - What you get when you run `npm ls better-auth auth @better-auth/cli @better-auth/core`?
Author
Owner

@kvanbere commented on GitHub (Mar 15, 2026):

@GautamBytes that was it, I switched to the new CLI using auth@latest and it immediately deleted the verifications table, thanks!

<!-- gh-comment-id:4064075195 --> @kvanbere commented on GitHub (Mar 15, 2026): @GautamBytes that was it, I switched to the new CLI using `auth@latest` and it immediately deleted the `verifications` table, thanks!
Author
Owner

@github-actions[bot] commented on GitHub (Mar 31, 2026):

This issue has been locked as it was closed more than 7 days ago. If you're experiencing a similar problem or you have additional context, please open a new issue and reference this one.

<!-- gh-comment-id:4165918027 --> @github-actions[bot] commented on GitHub (Mar 31, 2026): This issue has been locked as it was closed more than 7 days ago. If you're experiencing a similar problem or you have additional context, please open a new issue and reference this one.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#11115