Skip to content

Return 401 for expired OAuth access tokens#768

Merged
rickyrombo merged 2 commits intomainfrom
mjp-oauth-expired-token-401
Apr 17, 2026
Merged

Return 401 for expired OAuth access tokens#768
rickyrombo merged 2 commits intomainfrom
mjp-oauth-expired-token-401

Conversation

@rickyrombo
Copy link
Copy Markdown
Contributor

Summary

  • Expired OAuth access tokens were causing the API to return 403 on writes (e.g. POST /v1/tracks?user_id=…) because the bearer token failed to resolve to a wallet and the downstream authorization check ran with an empty wallet.
  • 403 implies the caller is authenticated but unauthorized, which keeps clients from realizing they need to refresh their token. Now we return 401 "Invalid or expired access token" whenever a Bearer token was supplied but no auth path resolved it (and the route asserts an identity via ?user_id= or :wallet).
  • Anonymous/unauthenticated requests are unchanged. Endpoints with no identity assertion still pass through silently if a stale bearer is sent.

Test plan

  • go test ./api/ -run "TestAuthMiddlewareInvalidBearerReturns401|TestAuthorized|TestRequireAuthMiddleware|TestRequireWriteScope|TestGetApiSigner" passes
  • New TestAuthMiddlewareInvalidBearerReturns401 covers the three cases: invalid bearer + myId → 401, invalid bearer + :wallet → 401, invalid bearer with no identity assertion → pass-through
  • Manually verify a real expired OAuth token against POST /v1/tracks returns 401

🤖 Generated with Claude Code

When an OAuth access token expired, requests asserting a caller identity
(via ?user_id= or :wallet) returned 403 because the bearer token failed
to resolve to a wallet and the authorization check ran with an empty
wallet. 403 implies the caller is authenticated but unauthorized, which
prevents clients from realizing they need to refresh their token.
Return 401 when a bearer token was supplied but no auth path could
resolve it, so clients can refresh and retry.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@rickyrombo rickyrombo requested a review from dylanjeffers April 17, 2026 02:30
A PKCE token belonging to user A used in a request for user B should
still return 403 (the credential is valid, just unauthorized for the
target). Track whether any auth path successfully decoded the bearer
token; only return 401 when no path could validate it (expired,
revoked, or undecodable).

Fixes TestAuthMiddleware_PKCEToken_UserIDMismatch which expects 403
for valid-but-mismatched tokens.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@rickyrombo rickyrombo merged commit 92aa8c4 into main Apr 17, 2026
5 checks passed
@rickyrombo rickyrombo deleted the mjp-oauth-expired-token-401 branch April 17, 2026 02:40
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.

1 participant