fix(sdk): NaN-safe expiry parsing + AsyncStorage token store tests#14078
fix(sdk): NaN-safe expiry parsing + AsyncStorage token store tests#14078rickyrombo wants to merge 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>
getAccessTokenExpiry()/getRefreshTokenExpiry()returnedNaNwhen localStorage or AsyncStorage held a non-numeric value (corruption, manual edits, format migration).NaNsilently bypasses expiry checks sinceDate.now() >= NaNis alwaysfalse, meaning expired sessions could be treated as valid.Changes
TokenStoreLocalStorage,TokenStoreAsyncStorage): replace bareNumber(raw)withNumber.isFinite()guard — invalid stored values returnnullinstead ofNaNtokenStoreAsyncStorage.test.ts): new test file mocking@react-native-async-storage/async-storage, covering token reads, expiry persistence, removal of expiry keys when not provided, NaN/corruption handling, andclear