Skip to content

feat(oauth): auto re-authenticate on invalid_grant error#215

Merged
porjo merged 2 commits into
porjo:masterfrom
karian7:feat/oauth-reauth-on-invalid-grant
Feb 26, 2026
Merged

feat(oauth): auto re-authenticate on invalid_grant error#215
porjo merged 2 commits into
porjo:masterfrom
karian7:feat/oauth-reauth-on-invalid-grant

Conversation

@karian7

@karian7 karian7 commented Feb 20, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Auto re-authenticate when OAuth2 refresh token is revoked or expired (invalid_grant)
  • Persist refreshed access tokens to the cache file via cachingTokenSource

Problem

When the cached refresh token becomes invalid (e.g., Google Cloud app in "testing" mode where refresh tokens expire after 7 days, or token manually revoked), BuildOAuthHTTPClient returns a client that silently fails on the first API call with:

oauth2: "invalid_grant" "Bad Request"

The program exits with an error, requiring the user to manually delete the token cache file and re-run the command.

Additionally, config.Client() does not persist refreshed tokens to disk, so the access token is only refreshed in memory and lost between runs.

Solution

  1. Proactive token validation: After reading the cached token, attempt a token refresh before returning the client. If the refresh fails with invalid_grant, delete the stale cache file and fall through to the browser-based three-legged OAuth flow automatically.

  2. cachingTokenSource wrapper: Wraps the standard oauth2.TokenSource to persist any refreshed tokens back to the cache file on disk, so subsequent runs reuse the fresh token.

  3. IsInvalidGrant helper: Detects invalid_grant errors from both oauth2.RetrieveError.ErrorCode and error string fallback.

Test plan

  • TestIsInvalidGrant — 6 subtests covering nil, unrelated errors, RetrieveError with matching/different codes, wrapped errors, and string fallback
  • TestCacheFile_RoundTrip — token write/read round-trip
  • TestCacheFile_DeletedOnInvalidGrant — cache file removal on invalid_grant
  • TestCacheFile_TokenReturnsErrorForMissingFile — missing file error
  • TestCacheFile_PutTokenOverwritesExisting — token overwrite behavior
  • Existing TestRateLimit integration test passes

🤖 Generated with Claude Code

When the cached refresh token is revoked or expired, detect the
invalid_grant error during token validation and automatically fall
through to the browser-based OAuth re-authentication flow instead
of failing with an opaque error.

Also adds cachingTokenSource to persist refreshed access tokens
to the cache file on disk between runs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@karian7 karian7 force-pushed the feat/oauth-reauth-on-invalid-grant branch from 187fba9 to 3638fe6 Compare February 20, 2026 10:08
@karian7 karian7 marked this pull request as ready for review February 20, 2026 10:15
@karian7 karian7 force-pushed the feat/oauth-reauth-on-invalid-grant branch from d614ed0 to 3638fe6 Compare February 22, 2026 03:16
Comment thread oauth.go Outdated
PutToken overwrites the cache file with O_TRUNC on re-auth, so
explicitly deleting it is unnecessary. Simplify to log a warning
and fall through to re-authentication.

Rename test to reflect actual behavior: cache file is preserved,
not deleted, when invalid_grant is encountered.
@karian7 karian7 requested a review from porjo February 23, 2026 01:12

@porjo porjo left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks!

@porjo porjo merged commit 7ac6a75 into porjo:master Feb 26, 2026
2 checks passed
@karian7 karian7 deleted the feat/oauth-reauth-on-invalid-grant branch February 26, 2026 10:51
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