[PR #8001] [MERGED] fix: preserve refresh token when provider omits it on refresh #7678

Closed
opened 2026-03-13 13:45:24 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/better-auth/better-auth/pull/8001
Author: @async3619
Created: 2/16/2026
Status: Merged
Merged: 2/28/2026
Merged by: @Bekacru

Base: canaryHead: fix/preserve-refresh-token-on-access-token-refresh


📝 Commits (4)

  • e570504 fix: preserve refresh token when provider omits it on refresh
  • 17364eb style: fix biome formatting for refreshTokenExpiresAt line
  • ca0f1c4 style: match biome formatting for refreshTokenExpiresAt
  • e9571ee Merge branch 'canary' into fix/preserve-refresh-token-on-access-token-refresh

📊 Changes

1 file changed (+17 additions, -11 deletions)

View changed files

📝 packages/better-auth/src/api/routes/account.ts (+17 -11)

📄 Description

Some OAuth providers like Google don't return a new refresh_token in the token refresh response — they only give you a fresh access_token. This is normal and expected behavior from Google's side.

The problem is that getAccessToken and refreshToken both unconditionally write newTokens.refreshToken (which is undefined in this case) back into the account data. The spread in setAccountCookie then overwrites the original refresh token with undefined, so the next refresh attempt fails silently because the guard account.refreshToken && accessTokenExpired short-circuits.

What this fixes:

In both getAccessToken and refreshToken handlers, fall back to the existing account.refreshToken when the provider doesn't return a new one:

refreshToken: newTokens?.refreshToken
    ? await setTokenUtil(newTokens.refreshToken, ctx.context)
    : account.refreshToken,

Same treatment for refreshTokenExpiresAt.

Ran into this while debugging a stateless setup (storeAccountCookie: true, no database) with Google OAuth — tokens refreshed fine the first time, then broke on every subsequent refresh because the refresh token was gone.

Closes #7999


Summary by cubic

Preserves the existing refresh token when a provider doesn’t return refresh_token during an access token refresh. Prevents token loss with providers like Google and keeps subsequent refreshes working.

  • Bug Fixes

    • In getAccessToken and refreshToken, fall back to account.refreshToken and account.refreshTokenExpiresAt when the provider omits them.
    • Avoid overwriting stored tokens with undefined in cookie/database; works in stateless mode (storeAccountCookie: true).
    • API responses now return tokens.refreshToken or the decrypted refresh token, and the resolved refreshTokenExpiresAt.
  • Refactors

    • Match Biome formatting for the refreshTokenExpiresAt lines.

Written for commit e9571ee65c. Summary will update on new commits.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/better-auth/better-auth/pull/8001 **Author:** [@async3619](https://github.com/async3619) **Created:** 2/16/2026 **Status:** ✅ Merged **Merged:** 2/28/2026 **Merged by:** [@Bekacru](https://github.com/Bekacru) **Base:** `canary` ← **Head:** `fix/preserve-refresh-token-on-access-token-refresh` --- ### 📝 Commits (4) - [`e570504`](https://github.com/better-auth/better-auth/commit/e57050444dea5c00a5f11433fec4fe7078852d59) fix: preserve refresh token when provider omits it on refresh - [`17364eb`](https://github.com/better-auth/better-auth/commit/17364ebe380b6cc1fc5c65e0e40fa5ecc1fc8e6f) style: fix biome formatting for refreshTokenExpiresAt line - [`ca0f1c4`](https://github.com/better-auth/better-auth/commit/ca0f1c46fdd58bbc43d4d3b1647a9c9986d1b67f) style: match biome formatting for refreshTokenExpiresAt - [`e9571ee`](https://github.com/better-auth/better-auth/commit/e9571ee65cd58d2bf6971ff4de306440b3257dac) Merge branch 'canary' into fix/preserve-refresh-token-on-access-token-refresh ### 📊 Changes **1 file changed** (+17 additions, -11 deletions) <details> <summary>View changed files</summary> 📝 `packages/better-auth/src/api/routes/account.ts` (+17 -11) </details> ### 📄 Description Some OAuth providers like Google don't return a new `refresh_token` in the token refresh response — they only give you a fresh `access_token`. This is normal and expected behavior from Google's side. The problem is that `getAccessToken` and `refreshToken` both unconditionally write `newTokens.refreshToken` (which is `undefined` in this case) back into the account data. The spread in `setAccountCookie` then overwrites the original refresh token with `undefined`, so the next refresh attempt fails silently because the guard `account.refreshToken && accessTokenExpired` short-circuits. **What this fixes:** In both `getAccessToken` and `refreshToken` handlers, fall back to the existing `account.refreshToken` when the provider doesn't return a new one: ```ts refreshToken: newTokens?.refreshToken ? await setTokenUtil(newTokens.refreshToken, ctx.context) : account.refreshToken, ``` Same treatment for `refreshTokenExpiresAt`. Ran into this while debugging a stateless setup (`storeAccountCookie: true`, no database) with Google OAuth — tokens refreshed fine the first time, then broke on every subsequent refresh because the refresh token was gone. Closes #7999 <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Preserves the existing refresh token when a provider doesn’t return refresh_token during an access token refresh. Prevents token loss with providers like Google and keeps subsequent refreshes working. - **Bug Fixes** - In getAccessToken and refreshToken, fall back to account.refreshToken and account.refreshTokenExpiresAt when the provider omits them. - Avoid overwriting stored tokens with undefined in cookie/database; works in stateless mode (storeAccountCookie: true). - API responses now return tokens.refreshToken or the decrypted refresh token, and the resolved refreshTokenExpiresAt. - **Refactors** - Match Biome formatting for the refreshTokenExpiresAt lines. <sup>Written for commit e9571ee65cd58d2bf6971ff4de306440b3257dac. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. --> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-03-13 13:45:24 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#7678