Skip to content

sdk: OAuth 2.0 PKCE rewrite with React Native support#13924

Merged
rickyrombo merged 24 commits intomainfrom
mjp-oauth-breaking-change
Mar 17, 2026
Merged

sdk: OAuth 2.0 PKCE rewrite with React Native support#13924
rickyrombo merged 24 commits intomainfrom
mjp-oauth-breaking-change

Conversation

@rickyrombo
Copy link
Contributor

@rickyrombo rickyrombo commented Mar 14, 2026

Summary

  • Rewrites sdk.oauth to use OAuth 2.0 Authorization Code Flow with PKCE — no backend or client secret required
  • Removes the old implicit flow (Ethereum-signed JWT), init(), renderButton(), write_once scope, and associated callback types
  • login() is now async and returns Promise<void>; errors surface via rejection instead of callbacks
  • redirectUri can be set once in sdk({ redirectUri: '...' }) config and optionally overridden per login() call
  • sdk.oauth is always defined — no null-check or ! assertion needed
  • React Native / Expo now supported out of the box: AsyncStorage token persistence + expo-web-browser OAuth session, with the redirect handled automatically inside login()
  • New methods: handleRedirect(), isAuthenticated(), getUser(), refreshAccessToken(), logout()
  • Token auto-refresh middleware intercepts 401 responses and retries with a fresh token
  • Updated docs and changeset

Test plan

  • Web popup flow: login() resolves after user approves, getUser() returns profile
  • Web full-page redirect flow: handleRedirect() completes exchange, getUser() returns profile
  • Popup cancel: login() rejects with error
  • isAuthenticated() returns true after login, false after logout()
  • refreshAccessToken() returns a new token; auto-refresh fires on 401
  • React Native: login() opens in-app browser and resolves after redirect

🤖 Generated with Claude Code

@changeset-bot
Copy link

changeset-bot bot commented Mar 14, 2026

🦋 Changeset detected

Latest commit: 11a62d7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@audius/sdk Major
@audius/sdk-legacy Patch
@audius/protocol-dashboard Patch
@audius/sp-actions Patch

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

@github-actions
Copy link
Contributor

github-actions bot commented Mar 15, 2026

🌐 Web preview ready

Preview URL: https://audius-web-preview-pr-13924.audius.workers.dev

Unique preview for this PR (deployed from this branch).
Workflow run

@socket-security
Copy link

socket-security bot commented Mar 15, 2026

No dependency changes detected. Learn more about Socket for GitHub.

👍 No dependency changes detected in pull request

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR rewrites the SDK’s OAuth implementation to use OAuth 2.0 Authorization Code Flow with PKCE, adds a token-store abstraction with web + React Native defaults, and updates docs/build wiring to support React Native/Expo.

Changes:

  • Replace legacy implicit OAuth flow APIs with PKCE-based login()/handleRedirect() and async token-store-backed auth state.
  • Introduce OAuthTokenStore interface plus TokenStoreMemory, TokenStoreLocalStorage, and TokenStoreAsyncStorage implementations with updated tests.
  • Add React Native entrypoint (index.native.ts) and update docs/changeset/build config accordingly.

Reviewed changes

Copilot reviewed 30 out of 31 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/sdk/src/sdk/utils/oauthScope.ts Removes write_once param validation helper; scope validation now read/write only.
packages/sdk/src/sdk/types.ts Adds tokenStore and openUrl to services; adds redirectUri to SDK config schemas.
packages/sdk/src/sdk/sdkBrowserDist.ts Switches global browser dist export to use createSdk.
packages/sdk/src/sdk/sdk.ts Removes runtime sdk export; keeps AudiusSdk type based on createSdk.
packages/sdk/src/sdk/oauth/types.ts Restricts OAuth scopes to read/write and removes legacy result/params types.
packages/sdk/src/sdk/oauth/tokenStoreLocalStorage.test.ts Adds tests for localStorage token persistence.
packages/sdk/src/sdk/oauth/tokenStore.ts Replaces concrete token store class with OAuthTokenStore async interface.
packages/sdk/src/sdk/oauth/tokenStore.test.ts Updates tests to cover new memory token store behavior.
packages/sdk/src/sdk/oauth/pkce.ts Reworks PKCE helpers to use noble SHA-256 and adds generateState().
packages/sdk/src/sdk/oauth/index.ts Updates OAuth exports (no longer re-exporting pkce helpers).
packages/sdk/src/sdk/oauth/TokenStoreMemory.ts Adds in-memory token store implementation.
packages/sdk/src/sdk/oauth/TokenStoreLocalStorage.ts Adds localStorage-backed token store implementation.
packages/sdk/src/sdk/oauth/TokenStoreAsyncStorage.ts Adds AsyncStorage-backed token store for React Native.
packages/sdk/src/sdk/oauth/OAuth.ts Major OAuth rewrite: async login(), handleRedirect(), token exchange + refresh + logout.
packages/sdk/src/sdk/oauth/OAuth.test.ts Expands/updates tests for new OAuth behavior and API surface.
packages/sdk/src/sdk/middleware/addTokenRefreshMiddleware.ts Updates refresh middleware to await async hasRefreshToken().
packages/sdk/src/sdk/middleware/addTokenRefreshMiddleware.test.ts Updates middleware tests for async hasRefreshToken().
packages/sdk/src/sdk/index.ts SDK entry exports updated; sdk alias export removed here.
packages/sdk/src/sdk/createSdkWithServices.ts Updates service initialization + token store wiring; OAuth is always constructed.
packages/sdk/src/sdk/createSdk.ts Wires redirectUri/openUrl into OAuth; uses async token store for Configuration.accessToken.
packages/sdk/src/index.ts Adds top-level sdk alias export (sdk = createSdk).
packages/sdk/src/index.native.ts Adds React Native entrypoint with default AsyncStorage + expo-web-browser login handling.
packages/sdk/rollup.config.ts Updates React Native build input to src/index.native.ts.
packages/sdk/package.json Adds RN peer deps (async-storage, expo-web-browser) and dev dep for building.
packages/protocol-dashboard/package.json Pins @audius/sdk and updates legacy SDK version.
packages/distro/src/components/DistributorCard.tsx Removes deprecated oauth.init() usage.
package-lock.json Locks new dependencies/versions for updated packages.
docs/docs/pages/sdk/oauth.mdx Rewrites OAuth documentation for PKCE + web/mobile flows + new APIs.
.changeset/tricky-zoos-film.md Removes obsolete changeset.
.changeset/angry-knives-fold.md Removes obsolete changeset.
.changeset/oauth-pkce-breaking-changes.md Adds major-version changeset documenting breaking changes and migration guide.
Comments suppressed due to low confidence (1)

packages/sdk/src/sdk/index.ts:7

  • sdk is no longer exported from this entrypoint, but the Rollup browser builds use src/sdk/index.ts as their input (sdkBrowserConfigCjs / sdkBrowserConfigEs in packages/sdk/rollup.config.ts). That means browser consumers importing { sdk } from @audius/sdk will lose the alias in browser bundles even though src/index.ts reintroduces it. Consider re-exporting sdk from here (e.g. export { createSdk as sdk }) or switching the browser build inputs to src/index.ts so the alias is consistently available across environments.
/* eslint-disable import/export */
export { createSdk } from './createSdk'
export {
  createSdkWithServices,
  type AudiusSdkWithServices
} from './createSdkWithServices'
export type { AudiusSdk } from './sdk'

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

},
"peerDependencies": {
"@solana/web3.js": "^1.95.8"
"@react-native-async-storage/async-storage": ">=1.0.0",
Copy link
Member

Choose a reason for hiding this comment

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

can/should these be pinned?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no idts, that's too restrictive for peer deps imo

rickyrombo and others added 22 commits March 16, 2026 16:47
…tton(), make login() async and return void, make getRedirectResult() return void, make basePath required
Removes the `typeof window !== 'undefined'` guard that made `oauth`
typed as `OAuth | undefined`, matching the fix already applied in
`createSdk.ts`. This resolves type errors in web where callers expect
`oauth: OAuth`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces scattered _settleLogin() early-returns in login() with a
top-level try/catch so the lock is guaranteed to release on any
unexpected error (e.g. sessionStorage quota exceeded). Adds a test
covering lock release after an unexpected setup failure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move TokenStoreAsyncStorage instantiation inside sdk() in index.native.ts
- Add comment explaining the let/definite-assignment pattern for sdkInstance
- Distinguish expo-web-browser 'locked' result from user cancellation
- Make _clearPopupCheckInterval private
- Add JSDoc note that hasRedirectResult() returns false after handleRedirect()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… Native

- Dynamic import expo-web-browser inside defaultOpenUrl so the module is
  only loaded when login() is called, not at SDK initialization
- Add peer dependency install instructions to the OAuth docs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…browser

- Settle login promise with error when server redirects back with an
  error param (e.g. access_denied on cancel) so callers aren't left hanging
- Switch dynamic import() to require() for expo-web-browser so Metro can
  resolve the module statically at bundle time

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The browser CJS and ESM rollup configs were using src/sdk/index.ts as
their entry point, which omitted the `sdk` alias added in src/index.ts.
Switch to src/index.ts to match the non-browser configs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@rickyrombo rickyrombo force-pushed the mjp-oauth-breaking-change branch from b8e49aa to 70c59c0 Compare March 16, 2026 23:47
rickyrombo and others added 2 commits March 16, 2026 16:55
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@rickyrombo rickyrombo merged commit 383db12 into main Mar 17, 2026
10 checks passed
@rickyrombo rickyrombo deleted the mjp-oauth-breaking-change branch March 17, 2026 00:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants