fix(sdk): Add token expiry tracking and retry logic to OAuth#14078
fix(sdk): Add token expiry tracking and retry logic to OAuth#14078rickyrombo merged 3 commits intomainfrom
Conversation
- Add getAccessTokenExpiry/getRefreshTokenExpiry to OAuthTokenStore interface - Track expiry in all three store implementations (Memory, LocalStorage, AsyncStorage) - isAuthenticated() now checks token expiry and attempts silent refresh - getUser() retries once with refreshed token on 401 instead of throwing - Pass expires_in and refresh_expires_in from server to token stores Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: cd7e1fe The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
Pull request overview
This PR enhances the SDK OAuth session handling by persisting token expiry timestamps in token stores and using that information to proactively refresh/validate sessions, plus adding a single retry in getUser() after a token refresh on 401.
Changes:
- Extend
OAuthTokenStoreto track access/refresh token expiries and persist them (Memory, LocalStorage, AsyncStorage). - Update
OAuth.isAuthenticated()to consider expiry (including silent refresh) and updategetUser()to retry once on 401 after refresh. - Expand/adjust unit tests for the new expiry/refresh behavior and add a changeset entry.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/sdk/src/sdk/oauth/tokenStore.ts | Expands the public token store interface with expiry getters and optional expiry params in setTokens(). |
| packages/sdk/src/sdk/oauth/TokenStoreMemory.ts | Stores expiry timestamps in-memory and clears them with tokens. |
| packages/sdk/src/sdk/oauth/TokenStoreLocalStorage.ts | Persists expiry timestamps to localStorage alongside tokens. |
| packages/sdk/src/sdk/oauth/tokenStoreLocalStorage.test.ts | Adds tests for expiry key persistence/removal/reading in localStorage store. |
| packages/sdk/src/sdk/oauth/TokenStoreAsyncStorage.ts | Persists expiry timestamps to AsyncStorage alongside tokens. |
| packages/sdk/src/sdk/oauth/tokenStore.test.ts | Updates memory-store tests to include expiry behavior. |
| packages/sdk/src/sdk/oauth/OAuth.ts | Implements expiry-aware isAuthenticated(), adds getUser() 401 refresh+retry, and persists token expiries from token responses. |
| packages/sdk/src/sdk/oauth/OAuth.test.ts | Adds/updates tests for expiry-aware authentication and getUser() retry behavior. |
| .changeset/oauth-token-expiry-tracking.md | Declares a release bump and describes the OAuth reliability improvements. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
🌐 Web preview readyPreview URL: https://audius-web-preview-pr-14078.audius.workers.dev Unique preview for this PR (deployed from this branch). |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
## Summary - Adds `refresh_expires_in` field (30 days in seconds) to both the authorization code exchange and refresh token grant responses - Companion to AudiusProject/apps#14078 which adds client-side token expiry tracking to the SDK ## Test plan - [ ] `POST /v1/oauth/token` (authorization_code grant) response includes `refresh_expires_in: 2592000` - [ ] `POST /v1/oauth/token` (refresh_token grant) response includes `refresh_expires_in: 2592000` - [ ] Existing `expires_in` field unchanged (still 3600) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
… expiry getters Agent-Logs-Url: https://github.com/AudiusProject/apps/sessions/0d8ac21d-1301-49dd-af18-338163d60570 Co-authored-by: rickyrombo <3690498+rickyrombo@users.noreply.github.com>
Summary
getAccessTokenExpiry()/getRefreshTokenExpiry()toOAuthTokenStoreinterface and all three implementations (Memory, LocalStorage, AsyncStorage)isAuthenticated()now checks token expiry — returns false when refresh token is expired, attempts silent refresh when access token is expiredgetUser()retries once with a refreshed token on 401 instead of immediately throwingsetTokens()now accepts optionalexpiresIn/refreshExpiresIn(seconds), which are persisted as absolute epoch timestampsNote: Companion API change to return
refresh_expires_inin the token response: AudiusProject/api#756Test plan
isAuthenticated()returns false when refresh token is expiredisAuthenticated()silently refreshes when access token is expired but refresh token is validgetUser()retries and succeeds after token refresh on 401getUser()throws when both initial request and refresh fail🤖 Generated with Claude Code