Skip to content

fix: skip token-refresh redirect in database mode#480

Merged
peppescg merged 1 commit into
mainfrom
fix/token-refresh-infinite-redirect-db-mode
Mar 30, 2026
Merged

fix: skip token-refresh redirect in database mode#480
peppescg merged 1 commit into
mainfrom
fix/token-refresh-infinite-redirect-db-mode

Conversation

@peppescg
Copy link
Copy Markdown
Collaborator

@peppescg peppescg commented Mar 30, 2026

Bug

When DATABASE_URL is set (database mode), users hit an infinite redirect loop immediately after login:

[API Client] token near expiry, redirecting to token-refresh | path=/catalog
[API Client] token near expiry, redirecting to token-refresh | path=/catalog
[API Client] token near expiry, redirecting to token-refresh | path=/catalog
...

This results in a "Too Many Redirects" browser error and makes the app completely unusable.

Root cause

In database mode, Better Auth is configured with storeAccountCookie: false (auth.ts:88), meaning the account_data cookie is never written. However, isTokenNearExpiry() always tries to read that cookie to check token expiry:

// utils.ts:73
if (!jwe) return true; // No cookie → treat as expired ← always hits this in DB mode!

This creates an infinite loop:

  1. User visits /catalog
  2. getAuthenticatedClient()isTokenNearExpiry() → no account_data cookie (DB mode) → returns true
  3. Redirect to /api/auth/token-refresh?redirect=%2Fcatalog
  4. Token refresh succeeds in DB, no cookie written (DB mode), redirect back to /catalog
  5. Repeat forever

The bug affects any OIDC provider (Okta, Azure AD, mock) whenever DATABASE_URL is configured — including the default docker-compose.yaml which hardcodes DATABASE_URL.

Fix

Guard the preemptive refresh redirect behind !isDatabaseMode. In database mode, Better Auth reads and writes tokens directly to the DB — no Route Handler cookie workaround is needed.

The cookie-based redirect to /api/auth/token-refresh was introduced specifically to handle the limitation that Server Components cannot write cookies. In database mode this limitation does not apply, so Better Auth handles token refresh transparently via getAccessToken().

Test plan

  • Unit tests added covering DB mode (no redirect) and cookie mode (redirect when near expiry)
  • Verify login works with make compose-down && make compose-up using Okta + docker-compose
  • Verify login still works in cookie mode (no DATABASE_URL) with mock OIDC (pnpm dev)

Copilot AI review requested due to automatic review settings March 30, 2026 18:47
@github-actions github-actions Bot added the size/S Small PR: 100-299 lines changed label Mar 30, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the server-side authenticated API client to avoid the preemptive /api/auth/token-refresh redirect when Better Auth is configured in database-backed mode, since token refresh state is persisted in the DB rather than via rotated cookies.

Changes:

  • Skip the “token near expiry” redirect flow when isDatabaseMode is enabled.
  • Add unit tests for getAuthenticatedClient covering both database mode (no redirect) and cookie mode (redirect on near-expiry).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/lib/api-client.ts Gates the near-expiry redirect logic behind !isDatabaseMode to avoid unnecessary Route Handler redirects in DB mode.
src/lib/api-client.test.ts Introduces Vitest coverage validating redirect behavior differences between database vs cookie session modes.

@peppescg peppescg enabled auto-merge (squash) March 30, 2026 18:51
@peppescg peppescg self-assigned this Mar 30, 2026
@peppescg peppescg merged commit b9637fe into main Mar 30, 2026
17 checks passed
@peppescg peppescg deleted the fix/token-refresh-infinite-redirect-db-mode branch March 30, 2026 18:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/S Small PR: 100-299 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants