[GH-ISSUE #7710] oauthProvider: callback triggered twice causing state_mismatch (worked with oidcProvider) #19513

Closed
opened 2026-04-15 18:43:17 -05:00 by GiteaMirror · 13 comments
Owner

Originally created by @StipleAdmin on GitHub (Jan 30, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/7710

Is this suited for github?

Yes, this is suited for github

To Reproduce

  1. Set up auth server with oauthProvider plugin (migrated from oidcProvider)
  2. Set up client app with genericOAuth plugin
  3. Both apps on subdomains (e.g., auth.example.com, app.example.com)
  4. Attempt OAuth login

Current vs. Expected behavior

Expected: User authenticates and redirects to callbackURL

Actual: OAuth callback endpoint is hit twice in quick succession:

  • First request: consumes state verification successfully
  • Second request: fails with state_mismatch: verification not found

This worked perfectly with oidcProvider - the issue started after migrating to oauthProvider.

Logs

=== CALLBACK DEBUG ===
State in URL: RW9cg1d_8VmS5HT6zrAGvPesRVdFa1ti
All cookies: __Secure-app.state=RW9cg1d_8VmS5HT6zrAGvPesRVdFa1ti.xxx
=== CALLBACK DEBUG ===
State in URL: RW9cg1d_8VmS5HT6zrAGvPesRVdFa1ti
All cookies: __Secure-app.state=RW9cg1d_8VmS5HT6zrAGvPesRVdFa1ti.xxx
ERROR [Better Auth]: Failed to parse state [Error [BetterAuthError]: State mismatch: verification not found]

Workaround

Added deduplication in route handler to block duplicate callbacks.

System info

  • better-auth: latest
  • Next.js: 16.1.1
  • Node.js: 20.x

Auth server config

import { oauthProvider } from "@better-auth/oauth-provider";
import { betterAuth } from "better-auth";
import { jwt } from "better-auth/plugins";

export const auth = betterAuth({
  plugins: [
    jwt(),
    oauthProvider({
      loginPage: "/",
      consentPage: "/consent",
    }),
    nextCookies(),
  ],
});

Client config

import { genericOAuth } from "better-auth/plugins";

export const auth = betterAuth({
  plugins: [
    genericOAuth({
      config: [{
        providerId: "my-provider",
        discoveryUrl: "https://auth.example.com/api/auth/.well-known/openid-configuration",
        // ...
      }],
    }),
    nextCookies(),
  ],
});
Originally created by @StipleAdmin on GitHub (Jan 30, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/7710 ### Is this suited for github? Yes, this is suited for github ### To Reproduce 1. Set up auth server with `oauthProvider` plugin (migrated from `oidcProvider`) 2. Set up client app with `genericOAuth` plugin 3. Both apps on subdomains (e.g., auth.example.com, app.example.com) 4. Attempt OAuth login ### Current vs. Expected behavior **Expected:** User authenticates and redirects to callbackURL **Actual:** OAuth callback endpoint is hit twice in quick succession: - First request: consumes state verification successfully - Second request: fails with `state_mismatch: verification not found` This worked perfectly with `oidcProvider` - the issue started after migrating to `oauthProvider`. ### Logs ``` === CALLBACK DEBUG === State in URL: RW9cg1d_8VmS5HT6zrAGvPesRVdFa1ti All cookies: __Secure-app.state=RW9cg1d_8VmS5HT6zrAGvPesRVdFa1ti.xxx === CALLBACK DEBUG === State in URL: RW9cg1d_8VmS5HT6zrAGvPesRVdFa1ti All cookies: __Secure-app.state=RW9cg1d_8VmS5HT6zrAGvPesRVdFa1ti.xxx ERROR [Better Auth]: Failed to parse state [Error [BetterAuthError]: State mismatch: verification not found] ``` ### Workaround Added deduplication in route handler to block duplicate callbacks. ### System info - better-auth: latest - Next.js: 16.1.1 - Node.js: 20.x ### Auth server config ```typescript import { oauthProvider } from "@better-auth/oauth-provider"; import { betterAuth } from "better-auth"; import { jwt } from "better-auth/plugins"; export const auth = betterAuth({ plugins: [ jwt(), oauthProvider({ loginPage: "/", consentPage: "/consent", }), nextCookies(), ], }); ``` ### Client config ```typescript import { genericOAuth } from "better-auth/plugins"; export const auth = betterAuth({ plugins: [ genericOAuth({ config: [{ providerId: "my-provider", discoveryUrl: "https://auth.example.com/api/auth/.well-known/openid-configuration", // ... }], }), nextCookies(), ], }); ```
GiteaMirror added the lockedidentityoauthbug labels 2026-04-15 18:43:17 -05:00
Author
Owner

@better-auth-agent[bot] commented on GitHub (Jan 30, 2026):

It looks like you're experiencing an issue with the oauthProvider where the OAuth callback endpoint gets triggered twice, resulting in a state_mismatch error. A possible approach to troubleshoot this situation could involve checking your setup to ensure consistency in how the OAuth state is managed.

The presence of different cookie names during the OAuth flow, such as in the case of the plugin for Expo, can lead to this mismatch problem. In particular, if your callback is seeing a mix-up of cookies (e.g., using both better-auth.oauth_state and better-auth.state), it can result in failed state verification[1][2]. This suggests that you may need to ensure that you are consistently using the same naming for your OAuth state throughout the entire authentication flow.

Since you mentioned you've added deduplication logic as a workaround, consider investigating further into how the state is being stored and verified, to prevent the second callback from being handled incorrectly.

It might also be worthwhile to check for any updates or open issues related to oauthProvider that might address this specific behavior. I found an open pull request that might be relevant for you regarding state verification issues in OAuth flows, which could potentially help with the management of error callbacks[1].

Would you like me to help you find more detailed information or provide further assistance with your configuration?

If you need more help, tag @better-auth-agent in a comment so I can respond.

DiagramDiscordGitHub

Diagram Join Star

<!-- gh-comment-id:3825209981 --> @better-auth-agent[bot] commented on GitHub (Jan 30, 2026): It looks like you're experiencing an issue with the `oauthProvider` where the OAuth callback endpoint gets triggered twice, resulting in a `state_mismatch` error. A possible approach to troubleshoot this situation could involve checking your setup to ensure consistency in how the OAuth state is managed. The presence of different cookie names during the OAuth flow, such as in the case of the plugin for Expo, can lead to this mismatch problem. In particular, if your callback is seeing a mix-up of cookies (e.g., using both `better-auth.oauth_state` and `better-auth.state`), it can result in failed state verification[[1]](https://github.com/better-auth/better-auth/pull/5521)[[2]](https://github.com/better-auth/better-auth/issues/6847). This suggests that you may need to ensure that you are consistently using the same naming for your OAuth state throughout the entire authentication flow. Since you mentioned you've added deduplication logic as a workaround, consider investigating further into how the state is being stored and verified, to prevent the second callback from being handled incorrectly. It might also be worthwhile to check for any updates or open issues related to `oauthProvider` that might address this specific behavior. I found an open pull request that might be relevant for you regarding state verification issues in OAuth flows, which could potentially help with the management of error callbacks[[1]](https://github.com/better-auth/better-auth/pull/5521). Would you like me to help you find more detailed information or provide further assistance with your configuration? _If you need more help, tag @better-auth-agent in a comment so I can respond._ <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) • [Discord](https://discord.gg/better-auth) • [GitHub](https://github.com/better-auth/better-auth) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/better-auth) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/better-auth/better-auth)
Author
Owner

@StipleAdmin commented on GitHub (Jan 30, 2026):

@better-auth-agent This is not a cookie naming issue.

The state cookie name is consistent (__Secure-labs_auth.state) and the value matches the URL state parameter perfectly. The issue is that the callback endpoint is being hit twice - the first request consumes the state verification, so when the second request arrives milliseconds later, the verification is already deleted.

This did NOT happen with oidcProvider - only with the new oauthProvider plugin. Same Next.js version, same client setup, same cookie configuration.

The double callback appears to be triggered internally by the plugin or its interaction with Next.js 16.

<!-- gh-comment-id:3825234860 --> @StipleAdmin commented on GitHub (Jan 30, 2026): @better-auth-agent This is not a cookie naming issue. The state cookie name is consistent (`__Secure-labs_auth.state`) and the value matches the URL state parameter perfectly. The issue is that the **callback endpoint is being hit twice** - the first request consumes the state verification, so when the second request arrives milliseconds later, the verification is already deleted. This did NOT happen with `oidcProvider` - only with the new `oauthProvider` plugin. Same Next.js version, same client setup, same cookie configuration. The double callback appears to be triggered internally by the plugin or its interaction with Next.js 16.
Author
Owner

@better-auth-agent[bot] commented on GitHub (Jan 30, 2026):

It seems you're experiencing an issue with the oauthProvider callback being triggered twice, which is causing a state mismatch error. This behavior indeed appears to be linked to how the new oauthProvider plugin interacts with Next.js.

You mentioned that the state cookie name is consistent, which rules out naming issues. In similar cases, users have reported that the callback validation fails due to discrepancies in cookie names or state management strategies. For instance, there was an issue where different cookie names were used during the OAuth flow, resulting in validation failures [1][2].

As a next step, consider checking the following:

  1. Callback Endpoint Timing: Ensure that your callback endpoint is not being hit twice due to rapid redirects or misconfigured settings. Try to analyze the timing and source of both requests.

  2. Module Versions: Confirm that your modules (e.g., better-auth, Next.js) are up to date, as issues with earlier versions can sometimes cause bugs.

  3. Logging: Increase the logging around the callback to capture additional details that might help identify why the second request is being sent.

  4. Deduplication Logic: Since you've implemented deduplication in the route handler, ensure it is correctly configured to capture both instances of the callback and prevent further processing after the first successful validation.

If the problem persists, please consider tagging @bekacru or @ping__ for more specialized assistance from the Better Auth development team!

What are your thoughts on the next steps? Do you need help with any specific part of the process?

DiagramDiscordGitHub

Diagram Join Star

<!-- gh-comment-id:3825236367 --> @better-auth-agent[bot] commented on GitHub (Jan 30, 2026): It seems you're experiencing an issue with the `oauthProvider` callback being triggered twice, which is causing a state mismatch error. This behavior indeed appears to be linked to how the new `oauthProvider` plugin interacts with Next.js. You mentioned that the state cookie name is consistent, which rules out naming issues. In similar cases, users have reported that the callback validation fails due to discrepancies in cookie names or state management strategies. For instance, there was an issue where different cookie names were used during the OAuth flow, resulting in validation failures [[1]](https://github.com/better-auth/better-auth/pull/5521)[[2]](https://github.com/better-auth/better-auth/issues/6847). As a next step, consider checking the following: 1. **Callback Endpoint Timing**: Ensure that your callback endpoint is not being hit twice due to rapid redirects or misconfigured settings. Try to analyze the timing and source of both requests. 2. **Module Versions**: Confirm that your modules (e.g., `better-auth`, `Next.js`) are up to date, as issues with earlier versions can sometimes cause bugs. 3. **Logging**: Increase the logging around the callback to capture additional details that might help identify why the second request is being sent. 4. **Deduplication Logic**: Since you've implemented deduplication in the route handler, ensure it is correctly configured to capture both instances of the callback and prevent further processing after the first successful validation. If the problem persists, please consider tagging @bekacru or @ping__ for more specialized assistance from the Better Auth development team! What are your thoughts on the next steps? Do you need help with any specific part of the process? <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) • [Discord](https://discord.gg/better-auth) • [GitHub](https://github.com/better-auth/better-auth) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/better-auth) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/better-auth/better-auth)
Author
Owner

@StipleAdmin commented on GitHub (Jan 30, 2026):

@Bekacru This appears to be a bug in the new oauthProvider plugin.

Summary:

  • oidcProvider → works fine
  • oauthProvider → callback fires twice, second fails

The callback endpoint receives two identical requests within milliseconds. First consumes the state, second can't find it.

I had to add a deduplication workaround in my route handler to block the duplicate request. This shouldn't be necessary.

Happy to provide more details or test a fix if needed.

<!-- gh-comment-id:3825241835 --> @StipleAdmin commented on GitHub (Jan 30, 2026): @Bekacru This appears to be a bug in the new `oauthProvider` plugin. **Summary:** - `oidcProvider` → works fine - `oauthProvider` → callback fires twice, second fails The callback endpoint receives two identical requests within milliseconds. First consumes the state, second can't find it. I had to add a deduplication workaround in my route handler to block the duplicate request. This shouldn't be necessary. Happy to provide more details or test a fix if needed.
Author
Owner

@bytaesu commented on GitHub (Jan 31, 2026):

Hi @StipleAdmin, I'm checking this

<!-- gh-comment-id:3826660689 --> @bytaesu commented on GitHub (Jan 31, 2026): Hi @StipleAdmin, I'm checking this
Author
Owner

@bytaesu commented on GitHub (Jan 31, 2026):

Hi @StipleAdmin,

I opened PR for this issue. You can test the updated version using the link below. Would this fix resolve your issue? 🙂

https://github.com/better-auth/better-auth/pull/7716#issuecomment-3826721339

<!-- gh-comment-id:3826792909 --> @bytaesu commented on GitHub (Jan 31, 2026): Hi @StipleAdmin, I opened PR for this issue. You can test the updated version using the link below. Would this fix resolve your issue? 🙂 https://github.com/better-auth/better-auth/pull/7716#issuecomment-3826721339
Author
Owner

@StipleAdmin commented on GitHub (Feb 2, 2026):

@bytaesu Thanks for the quick fix!

I tried to test the PR version but ran into dependency loop errors with both bun and npm:

error: Package "@better-auth/oauth-provider@..." has a dependency loop

For now I'm using a workaround (deduplication in route handler) which works.

I'll update to the official release once this PR is merged. Let me know if you need any additional info or testing help.

<!-- gh-comment-id:3835995914 --> @StipleAdmin commented on GitHub (Feb 2, 2026): @bytaesu Thanks for the quick fix! I tried to test the PR version but ran into dependency loop errors with both bun and npm: ``` error: Package "@better-auth/oauth-provider@..." has a dependency loop ``` For now I'm using a workaround (deduplication in route handler) which works. I'll update to the official release once this PR is merged. Let me know if you need any additional info or testing help.
Author
Owner

@bytaesu commented on GitHub (Feb 2, 2026):

@StipleAdmin,

That PR does not modify any dependencies. Please try reinstalling node_modules or restarting the TypeScript server 🙏

<!-- gh-comment-id:3836021559 --> @bytaesu commented on GitHub (Feb 2, 2026): @StipleAdmin, That PR does not modify any dependencies. Please try reinstalling node_modules or restarting the TypeScript server 🙏
Author
Owner

@dvanmali commented on GitHub (Feb 4, 2026):

@StipleAdmin if the callback is being called twice, likely the issue is with the Generic OAuth plugin instead of the OAuth provider plugin

<!-- gh-comment-id:3845762790 --> @dvanmali commented on GitHub (Feb 4, 2026): @StipleAdmin if the callback is being called twice, likely the issue is with the Generic OAuth plugin instead of the OAuth provider plugin
Author
Owner

@HenryOoO commented on GitHub (Mar 6, 2026):

@bytaesu
This issue still exists. After the provider's login page uses authClient.signIn.email, the client's backend will definitely receive a state_mismatch error. My environment
The framework is tanstack start
Both the client and the provider are better-auth v1.5.3

<!-- gh-comment-id:4009042125 --> @HenryOoO commented on GitHub (Mar 6, 2026): @bytaesu This issue still exists. After the provider's login page uses authClient.signIn.email, the client's backend will definitely receive a state_mismatch error. My environment The framework is tanstack start Both the client and the provider are better-auth v1.5.3
Author
Owner

@bytaesu commented on GitHub (Mar 9, 2026):

Hi @dvanmali 😁

Could you take a look at this issue? What do you think is the best approach here?

<!-- gh-comment-id:4025753893 --> @bytaesu commented on GitHub (Mar 9, 2026): Hi @dvanmali 😁 Could you take a look at this issue? What do you think is the best approach here?
Author
Owner

@gustavovalverde commented on GitHub (Mar 28, 2026):

This was fixed in #8519

<!-- gh-comment-id:4148494307 --> @gustavovalverde commented on GitHub (Mar 28, 2026): This was fixed in #8519
Author
Owner

@github-actions[bot] commented on GitHub (Apr 5, 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:4187977961 --> @github-actions[bot] commented on GitHub (Apr 5, 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#19513