[GH-ISSUE #7703] on stateless setup, calling getAccessToken does not refresh the access token when needed #19511

Open
opened 2026-04-15 18:42:56 -05:00 by GiteaMirror · 4 comments
Owner

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

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

Critical bug: access token never refreshes after expiry (breaks auth flow)

This has also been mentioned on Discord but didn’t get traction:

Summary

This appears to be a critical bug because it breaks the authentication flow entirely.

Once the accessToken expires, it is never refreshed. Instead, calls that should transparently refresh the token fail with a 400 BAD_REQUEST.

Observed behavior

When the token is expired, calling getAccessToken throws:

{
  status: 'BAD_REQUEST',
  body: [Object],
  headers: {},
  statusCode: 400,
  [cause]: [Object]
}

Current vs. Expected behavior

Current issue

The accessToken expires and is not automatically refreshed. In practice, the token expires every time and the auth flow breaks once it does.

Expected behavior

Calling getAccessToken should automatically refresh the expired accessToken, as described in the documentation, and return a valid token without requiring a full re-authentication flow.

What version of Better Auth are you using?

1.4.10

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 25.1.0: Mon Oct 20 19:34:05 PDT 2025; root:xnu-12377.41.6~2/RELEASE_ARM64_T6041",
    "release": "25.1.0",
    "cpuCount": 14,
    "cpuModel": "Apple M4 Pro",
    "totalMemory": "48.00 GB",
    "freeMemory": "0.38 GB"
  },
  "node": {
    "version": "v22.17.1",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "10.9.2"
  },
  "frameworks": [
    {
      "name": "next",
      "version": "~16.1.4"
    },
    {
      "name": "react",
      "version": "~19.2.1"
    }
  ],
  "databases": null,
  "betterAuth": {
    "version": "^1.4.10",
    "config": null
  }
}

Which area(s) are affected? (Select all that apply)

Client, Backend

Auth config (if applicable)

import { betterAuth } from "better-auth"
export const auth = betterAuth({
  plugins: [
    genericOAuth({
      config: [
        keycloak({
            clientId: process.env.KEYCLOAK_ID ?? '',
            clientSecret: process.env.KEYCLOAK_SECRET ?? '',
            issuer: process.env.KEYCLOAK_ISSUER ?? '',
          }),
      ]
    })
  ],

  session: {
    expiresIn: 10 * 60 * 60,
    updateAge: 30 * 60,
    cookieCache: {
      enabled: true,
      strategy: 'jwe',
      maxAge: 10 * 60 * 60,
      refreshCache: true
    }
  },
});

Additional context

No response

Originally created by @stoompa on GitHub (Jan 30, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/7703 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce ## Critical bug: access token never refreshes after expiry (breaks auth flow) This has also been mentioned on Discord but didn’t get traction: - https://discord.com/channels/1288403910284935179/1464286520776069326 - https://discord.com/channels/1288403910284935179/1461083247160328393 ### Summary This appears to be a critical bug because it breaks the authentication flow entirely. Once the `accessToken` expires, it is **never refreshed**. Instead, calls that should transparently refresh the token fail with a `400 BAD_REQUEST`. ### Observed behavior When the token is expired, calling `getAccessToken` throws: ```js { status: 'BAD_REQUEST', body: [Object], headers: {}, statusCode: 400, [cause]: [Object] } ``` ### Current vs. Expected behavior ## Current issue The `accessToken` expires and is not automatically refreshed. In practice, the token expires every time and the auth flow breaks once it does. ## Expected behavior Calling `getAccessToken` should automatically refresh the expired `accessToken`, as described in the documentation, and return a valid token without requiring a full re-authentication flow. ### What version of Better Auth are you using? 1.4.10 ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 25.1.0: Mon Oct 20 19:34:05 PDT 2025; root:xnu-12377.41.6~2/RELEASE_ARM64_T6041", "release": "25.1.0", "cpuCount": 14, "cpuModel": "Apple M4 Pro", "totalMemory": "48.00 GB", "freeMemory": "0.38 GB" }, "node": { "version": "v22.17.1", "env": "development" }, "packageManager": { "name": "npm", "version": "10.9.2" }, "frameworks": [ { "name": "next", "version": "~16.1.4" }, { "name": "react", "version": "~19.2.1" } ], "databases": null, "betterAuth": { "version": "^1.4.10", "config": null } } ``` ### Which area(s) are affected? (Select all that apply) Client, Backend ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" export const auth = betterAuth({ plugins: [ genericOAuth({ config: [ keycloak({ clientId: process.env.KEYCLOAK_ID ?? '', clientSecret: process.env.KEYCLOAK_SECRET ?? '', issuer: process.env.KEYCLOAK_ISSUER ?? '', }), ] }) ], session: { expiresIn: 10 * 60 * 60, updateAge: 30 * 60, cookieCache: { enabled: true, strategy: 'jwe', maxAge: 10 * 60 * 60, refreshCache: true } }, }); ``` ### Additional context _No response_
GiteaMirror added the coreoauthbug labels 2026-04-15 18:42:57 -05:00
Author
Owner

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

It looks like you're experiencing a critical issue with the getAccessToken not refreshing the access token when it expires. This is indeed a recognized problem, as you've noted it breaks the authentication flow entirely.

From previous discussions and issues, it was highlighted that getAccessToken should ideally refresh the expired access token transparently. However, in your case, the API is returning a 400 BAD_REQUEST error instead, which indicates something might be misconfigured or not functioning as intended.

Here are a few steps to troubleshoot this issue:

  1. Check provider configurations: Ensure that your providerId is correctly referenced and that the OAuth provider supports token refreshing.
  2. Token Attributes: Confirm that your tokens indeed have an accessTokenExpiresAt attribute and that a refreshToken is available. If these are missing, it can lead to issues with token refreshing.
  3. Refresh Logic: Since your bug report indicates potential internal issues with how tokens refresh, refer to any updates or fixes in the Better Auth documentation regarding token refresh handling.

If this issue persists, I recommend tagging Beka (@bekacru) or Max (@ping__) from the Better Auth team in your follow-up message for further assistance.

In the meantime, have you had a chance to review the latest updates or documentation regarding any changes to the token handling in Better Auth?

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

DiagramDiscordGitHub

Diagram Join Star

<!-- gh-comment-id:3822969616 --> @better-auth-agent[bot] commented on GitHub (Jan 30, 2026): It looks like you're experiencing a critical issue with the `getAccessToken` not refreshing the access token when it expires. This is indeed a recognized problem, as you've noted it breaks the authentication flow entirely. From previous discussions and issues, it was highlighted that `getAccessToken` should ideally refresh the expired access token transparently. However, in your case, the API is returning a `400 BAD_REQUEST` error instead, which indicates something might be misconfigured or not functioning as intended. Here are a few steps to troubleshoot this issue: 1. **Check provider configurations**: Ensure that your `providerId` is correctly referenced and that the OAuth provider supports token refreshing. 2. **Token Attributes**: Confirm that your tokens indeed have an `accessTokenExpiresAt` attribute and that a `refreshToken` is available. If these are missing, it can lead to issues with token refreshing. 3. **Refresh Logic**: Since your bug report indicates potential internal issues with how tokens refresh, refer to any updates or fixes in the Better Auth documentation regarding token refresh handling. If this issue persists, I recommend tagging Beka (@bekacru) or Max (@ping__) from the Better Auth team in your follow-up message for further assistance. In the meantime, have you had a chance to review the latest updates or documentation regarding any changes to the token handling in Better Auth? _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

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

Tagging @Bekacru here

<!-- gh-comment-id:3822976336 --> @stoompa commented on GitHub (Jan 30, 2026): Tagging @Bekacru here
Author
Owner

@dosubot[bot] commented on GitHub (Jan 30, 2026):

This is a known and recurring limitation in Better Auth's stateless mode, especially with providers like Keycloak. In stateless setups, tokens and session/account state are stored in cookies (like account_data). These cookies expire (default 5 minutes) and are only refreshed on sign-in or when a provider token is actually refreshed. If the cookie expires or isn't updated, getAccessToken fails with errors like 400 BAD_REQUEST or "Account not found", breaking the auth flow. There is no built-in automatic refresh for the account cookie, and workarounds like polling a keep-alive endpoint or calling getAccessToken periodically do not reliably update the cookie's expiry or refresh the token in practice. The refreshCache strategy only updates the cookie expiration, not the token itself, unless a refresh is triggered and the cookie is still present.

This limitation is well-documented and affects both client and backend flows. For production, maintainers and users recommend using at least minimal persistent storage for user/account data, or implementing custom logic to manage token storage and refresh if you must remain stateless. There are also Next.js-specific issues: if you call getAccessToken during Server Component rendering and a token refresh is needed, the Set-Cookie header is dropped, causing a refresh loop and stale cookies. The recommended workaround is to perform token refresh in middleware or route handlers before SSR begins, where cookies can be set properly.

Stateless auth support is still maturing in Better Auth, and users have reported moving away from it due to these issues. If you need robust token/session management, a persistent store is currently the only reliable option. More details and workarounds are discussed in these threads:

Let me know if you want more details on custom workarounds or have questions about your specific setup.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:3822989141 --> @dosubot[bot] commented on GitHub (Jan 30, 2026): <!-- Answer --> This is a known and recurring limitation in Better Auth's stateless mode, especially with providers like Keycloak. In stateless setups, tokens and session/account state are stored in cookies (like `account_data`). These cookies expire (default 5 minutes) and are only refreshed on sign-in or when a provider token is actually refreshed. If the cookie expires or isn't updated, `getAccessToken` fails with errors like 400 BAD_REQUEST or "Account not found", breaking the auth flow. There is no built-in automatic refresh for the account cookie, and workarounds like polling a keep-alive endpoint or calling `getAccessToken` periodically do not reliably update the cookie's expiry or refresh the token in practice. The `refreshCache` strategy only updates the cookie expiration, not the token itself, unless a refresh is triggered and the cookie is still present. This limitation is well-documented and affects both client and backend flows. For production, maintainers and users recommend using at least minimal persistent storage for user/account data, or implementing custom logic to manage token storage and refresh if you must remain stateless. There are also Next.js-specific issues: if you call `getAccessToken` during Server Component rendering and a token refresh is needed, the Set-Cookie header is dropped, causing a refresh loop and stale cookies. The recommended workaround is to perform token refresh in middleware or route handlers before SSR begins, where cookies can be set properly. Stateless auth support is still maturing in Better Auth, and users have reported moving away from it due to these issues. If you need robust token/session management, a persistent store is currently the only reliable option. More details and workarounds are discussed in these threads: - [Stateless mode limitations and workarounds](https://github.com/better-auth/better-auth/discussions/6715) - [Known issues with getAccessToken in stateless mode](https://github.com/better-auth/better-auth/issues/2202#issuecomment-3471902430) - [Next.js App Router Set-Cookie bug](https://github.com/better-auth/better-auth/issues/7394) Let me know if you want more details on custom workarounds or have questions about your specific setup. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/7cf685e8-5ebb-4c86-9947-9ec7e9b57271?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/7cf685e8-5ebb-4c86-9947-9ec7e9b57271?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/7cf685e8-5ebb-4c86-9947-9ec7e9b57271?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/7cf685e8-5ebb-4c86-9947-9ec7e9b57271?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/7cf685e8-5ebb-4c86-9947-9ec7e9b57271?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/7cf685e8-5ebb-4c86-9947-9ec7e9b57271?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/7cf685e8-5ebb-4c86-9947-9ec7e9b57271?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=This%20is%20a%20known%20and%20recurring%20limitation%20in%20Better%20Auth%27s%20stateless%20mode%2C%20especially%20with%20providers%20like%20Keycloak.%20In%20stateless%20setups%2C%20tokens%20and%20session/account%20state%20are%20stored%20in%20cookies%20%28like%20%60account_data%60%29.%20These%20cookies%20expire%20%28default%205%20minutes%29%20and%20are%20only%20refreshed%20on%20sign-in%20or%20when%20a%20provider%20token%20is%20actually%20refreshed.%20If%20the%20cookie%20expires%20or%20isn%27t%20updated%2C%20%60getAccessToken%60%20fails%20with%20errors%20like%20400%20BAD_REQUEST%20or%20%22Account%20not%20found%22%2C%20breaking%20the%20auth%20flow.%20There%20is%20no%20built-in%20automatic%20refresh%20for%20the%20account%20cookie%2C%20and%20workarounds%20like%20polling%20a%20keep-alive%20endpoint%20or%20calling%20%60getAccessToken%60%20periodically%20do%20not%20reliably%20update%20the%20cookie%27s%20expiry%20or%20refresh%20the%20token%20in%20practice.%20The%20%60refreshCache%60%20strategy%20only%20updates%20the%20cookie%20expiration%2C%20not%20the%20token%20itself%2C%20unless%20a%20refresh%20is%20triggered%20and%20the%20cookie%20is%20still%20present.%0A%0AThis%20limitation%20is%20well-documented%20and%20affects%20both%20client%20and%20backend%20flows.%20For%20production%2C%20maintainers%20and%20users%20recommend%20using%20at%20least%20minimal%20persistent%20storage%20for%20user/account%20data%2C%20or%20implementing%20custom%20logic%20to%20manage%20token%20storage%20and%20refresh%20if%20you%20must%20remain%20stateless.%20There%20are%20also%20Next.js-specific%20issues%3A%20if%20you%20call%20%60getAccessToken%60%20during%20Server%20Component%20rendering%20and%20a%20token%20refresh%20is%20needed%2C%20the%20Set-Cookie%20header%20is%20dropped%2C%20causing%20a%20refresh%20loop%20and%20stale%20cookies.%20The%20recommended%20workaround%20is%20to%20perform%20token%20refresh%20in%20middleware%20or%20route%20handlers%20before%20SSR%20begins%2C%20where%20cookies%20can%20be%20set%20properly.%0A%0AStateless%20auth%20support%20is%20still%20maturing%20in%20Better%20Auth%2C%20and%20users%20have%20reported%20moving%20away%20from%20it%20due%20to%20these%20issues.%20If%20you%20need%20robust%20token/session%20management%2C%20a%20persistent%20store%20is%20currently%20the%20only%20reliable%20option.%20More%20details%20and%20workarounds%20are%20discussed%20in%20these%20threads%3A%20%20%0A-%20%5BStateless%20mode%20limitations%20and%20workarounds%5D%28https%3A//github.com/better-auth/better-auth/discussions/6715%29%0A-%20%5BKnown%20issues%20with%20getAccessToken%20in%20stateless%20mode%5D%28https%3A//github.com/better-auth/better-auth/issues/2202%23issuecomment-3471902430%29%0A-%20%5BNext.js%20App%20Router%20Set-Cookie%20bug%5D%28https%3A//github.com/better-auth/better-auth/issues/7394%29%0A%0ALet%20me%20know%20if%20you%20want%20more%20details%20on%20custom%20workarounds%20or%20have%20questions%20about%20your%20specific%20setup.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/7703)
Author
Owner

@Lookwe69 commented on GitHub (Mar 23, 2026):

Any news ?

<!-- gh-comment-id:4111410653 --> @Lookwe69 commented on GitHub (Mar 23, 2026): Any news ?
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#19511