Skip to content

Clear rejected OAuth refresh tokens; keep expected Better Auth 4xx out of error telemetry - #226

Merged
jgpruitt merged 2 commits into
mainfrom
fix-oauth-refresh-minimal
Oct 19, 2026
Merged

jgpruitt merged 2 commits into
mainfrom
fix-oauth-refresh-minimal

Conversation

@jgpruitt

Copy link
Copy Markdown
Collaborator

Minimal replacement for #225.

Problem

  • After the server rejects a refresh token (invalid_grant), the CLI keeps it stored, so every later call (and every process) replays it.
  • Better Auth's tracer records every non-3xx APIError as an exception + ERROR, so expected client rejections (invalid_grant, bad credentials) show red in Logfire.

Changes

  • packages/cli/session.ts: in the existing invalid_grant branch (already under the refresh lock), if the store still holds the rejected token, clear it. The next call makes no network request and gets the existing "Session expired. Run 'me login'" message.
  • packages/cli/commands/login.ts: persist login tokens under the same withRefreshLock, so a concurrent rejection clear can't wipe a fresh login.
  • packages/server/auth/client-error-spans.ts: span processor (registered in telemetry.ts) that, for better-auth scope spans only, skips the exception event and ERROR status for 4xx errors, keeping http.response.status_code. 5xx, status-less errors and other scopes are unchanged.

Known limits

  • If a keychain read fails at the moment of rejection, the dead token is sent once more on the next refresh, which clears it.
  • Relies on Better Auth calling recordException then setStatus; if that changes, spans go red again (never hides real errors).

Validation

  • ./bun run check (typecheck, lint, 1426 unit tests) passes.
  • New tests: rejection clears the token and later calls make no network request; transient failure keeps it; real Better Auth APIError 4xx not red, 5xx/plain errors red, other scopes untouched.

Copilot AI balanced review requested due to automatic review settings October 19, 2026 16:14
@jgpruitt jgpruitt self-assigned this Oct 19, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Queued CLI processes can still replay cleared refresh tokens through their stale credential fallback.

1 open issue
What changed in this PR

This minimal replacement for #225 addresses rejected OAuth refresh tokens in the CLI and expected client-error telemetry from Better Auth.

Changes:

  • Clear rejected stored tokens and serialize login persistence with refresh operations.
  • Suppress exception events and ERROR status for Better Auth 4xx spans.
  • Add refresh-failure and span-classification tests.
File Description
packages/​server/​telemetry.ts Registers the span processor.
packages/​server/​auth/​client-error-spans.ts Filters Better Auth 4xx error telemetry.
packages/​server/​auth/​client-error-spans.test.ts Tests error classification and scope isolation.
packages/​cli/​session.ts Clears rejected refresh credentials.
packages/​cli/​session.test.ts Tests rejection cleanup and transient failures.
packages/​cli/​commands/​login.ts Stores login tokens under the refresh lock.

Review effort: Balanced

Comment thread packages/cli/session.ts Resolved
Copilot AI balanced review requested due to automatic review settings October 19, 2026 16:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

A queued refresh can ignore a fresh device-login session, causing authentication failure and deletion of valid credentials.

1 open issue
1 resolved since last review

Review effort: Balanced

Comment thread packages/cli/session.ts
const refreshToken = current.refresh_token ?? fallback.refresh_token;
if (!refreshToken) return undefined;
return doRefresh(server, refreshToken, current);
if (!current.refresh_token) return undefined;
@jgpruitt
jgpruitt merged commit db220a8 into main Oct 19, 2026
5 of 7 checks passed
@jgpruitt
jgpruitt deleted the fix-oauth-refresh-minimal branch October 19, 2026 16:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants