[PR #6933] [MERGED] fix(expo): fix cookie-based OAuth state with expo-authorization-proxy #15221

Closed
opened 2026-04-13 09:53:50 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/better-auth/better-auth/pull/6933
Author: @ruff-exec
Created: 12/22/2025
Status: Merged
Merged: 1/14/2026
Merged by: @himself65

Base: canaryHead: fix/expo-oauth-state-cookie


📝 Commits (10+)

  • 6cf6066 fix(expo): forward oauth_state to expo authorization proxy
  • ca44de0 test(expo): add regression test for oauth_state proxy param
  • 102c42f fix(expo): refactor proxy URL construction to use URLSearchParams for oauthState
  • 9218db6 Merge remote-tracking branch 'origin/canary' into fix/expo-oauth-state-cookie
  • b2e5f9d test(expo): fix storeStateStrategy cookie regression test
  • 3019b6f test(expo): reproduce cookie storeStateStrategy oauth proxy flow
  • f4a9f15 Merge branch 'canary' into fix/expo-oauth-state-cookie
  • 6260e2b refactor: use cookie helpers
  • 4ada29c test: add database strategy test case
  • d9dd6d4 refactor: use safeJSONParse

📊 Changes

3 files changed (+179 additions, -4 deletions)

View changed files

📝 packages/expo/src/client.ts (+45 -4)
📝 packages/expo/src/routes.ts (+14 -0)
📝 packages/expo/test/expo.test.ts (+120 -0)

📄 Description

Summary

This PR fixes Expo native OAuth flows when storeStateStrategy: "cookie" is enabled.

In the Expo flow, the /expo-authorization-proxy step runs in the system browser and did not have access to the encrypted better-auth.oauth_state cookie created during /sign-in/*. As a result, the OAuth callback failed with State Mismatch. OAuth state cookie not found.

What changed / Why

Expo proxy route (packages/expo/src/routes.ts)

  • Added an optional oauthState query param to /expo-authorization-proxy.
  • When provided, the proxy sets the oauth_state auth cookie (better-auth.oauth_state / __Secure-better-auth.oauth_state) with a 10 minute TTL (matching core), then redirects to the provider authorizationURL.
  • If not provided, the proxy preserves the existing behavior (signed state cookie derived from the provider authorization URL).

Expo client (packages/expo/src/client.ts)

  • Extracts the stored oauth_state cookie value from the Expo cookie jar and appends it to the proxy URL as oauthState=... when present.
  • This bridges the native-stored cookie into the system browser context so the callback can successfully read and decrypt oauth_state.

Tests

  • Added a regression test to ensure the proxy URL passed to openAuthSessionAsync() includes oauthState= when storeStateStrategy is configured as "cookie".

Context / Background

Core Better Auth’s cookie-based OAuth state strategy stores the full state payload (incl. PKCE verifier) in an encrypted oauth_state cookie and the callback handler requires it to be present. Expo’s system browser navigation cannot include custom request headers, so the cookie must be set within the browser context before redirecting to the provider.

Breaking changes / Deprecations

  • None. Existing behavior is preserved when oauthState is not provided.

UI changes / Screenshots

  • None.

Closes #6847


Summary by cubic

Fixes Expo OAuth when using cookie-based state by forwarding the encrypted oauth_state to the system browser via the authorization proxy. Prevents “State Mismatch. OAuth state cookie not found” during native sign-in.

  • Bug Fixes
    • Client reads oauth_state from the Expo cookie jar and adds it as oauthState to the proxy URL.
    • Proxy sets the oauth_state cookie with a 10-minute TTL, then redirects to the provider.
    • For database strategy, the proxy URL omits oauthState; tests cover both strategies.

Written for commit 1c47c9cf84. Summary will update on new commits.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/better-auth/better-auth/pull/6933 **Author:** [@ruff-exec](https://github.com/ruff-exec) **Created:** 12/22/2025 **Status:** ✅ Merged **Merged:** 1/14/2026 **Merged by:** [@himself65](https://github.com/himself65) **Base:** `canary` ← **Head:** `fix/expo-oauth-state-cookie` --- ### 📝 Commits (10+) - [`6cf6066`](https://github.com/better-auth/better-auth/commit/6cf60664b4af216d22595589052f86f11b0c116a) fix(expo): forward oauth_state to expo authorization proxy - [`ca44de0`](https://github.com/better-auth/better-auth/commit/ca44de02bebd6f74221f5a001fa267ddb8708f10) test(expo): add regression test for oauth_state proxy param - [`102c42f`](https://github.com/better-auth/better-auth/commit/102c42f3533f039a534e1fdf38b06881458b2570) fix(expo): refactor proxy URL construction to use URLSearchParams for oauthState - [`9218db6`](https://github.com/better-auth/better-auth/commit/9218db60cec7ea0ce4ae58cddaf963e5f7695346) Merge remote-tracking branch 'origin/canary' into fix/expo-oauth-state-cookie - [`b2e5f9d`](https://github.com/better-auth/better-auth/commit/b2e5f9d706880e21ea7d525651060639088ff566) test(expo): fix storeStateStrategy cookie regression test - [`3019b6f`](https://github.com/better-auth/better-auth/commit/3019b6f694a8de6ea051dcea1c4a3655f0469218) test(expo): reproduce cookie storeStateStrategy oauth proxy flow - [`f4a9f15`](https://github.com/better-auth/better-auth/commit/f4a9f15c3f446f971bc34a51fdb42fbcde4dd2b6) Merge branch 'canary' into fix/expo-oauth-state-cookie - [`6260e2b`](https://github.com/better-auth/better-auth/commit/6260e2be348cb44cea1de722e54aaa46cca11d06) refactor: use cookie helpers - [`4ada29c`](https://github.com/better-auth/better-auth/commit/4ada29c909034f26f6c1ef27d9b567e3e49c7e1e) test: add database strategy test case - [`d9dd6d4`](https://github.com/better-auth/better-auth/commit/d9dd6d4d5fde68da13d4da16cac451a13a74a61b) refactor: use safeJSONParse ### 📊 Changes **3 files changed** (+179 additions, -4 deletions) <details> <summary>View changed files</summary> 📝 `packages/expo/src/client.ts` (+45 -4) 📝 `packages/expo/src/routes.ts` (+14 -0) 📝 `packages/expo/test/expo.test.ts` (+120 -0) </details> ### 📄 Description ## Summary This PR fixes Expo native OAuth flows when `storeStateStrategy: "cookie"` is enabled. In the Expo flow, the `/expo-authorization-proxy` step runs in the system browser and did not have access to the encrypted `better-auth.oauth_state` cookie created during `/sign-in/*`. As a result, the OAuth callback failed with `State Mismatch. OAuth state cookie not found`. ## What changed / Why ### Expo proxy route (`packages/expo/src/routes.ts`) - Added an optional `oauthState` query param to `/expo-authorization-proxy`. - When provided, the proxy sets the `oauth_state` auth cookie (`better-auth.oauth_state` / `__Secure-better-auth.oauth_state`) with a 10 minute TTL (matching core), then redirects to the provider `authorizationURL`. - If not provided, the proxy preserves the existing behavior (signed `state` cookie derived from the provider authorization URL). ### Expo client (`packages/expo/src/client.ts`) - Extracts the stored `oauth_state` cookie value from the Expo cookie jar and appends it to the proxy URL as `oauthState=...` when present. - This bridges the native-stored cookie into the system browser context so the callback can successfully read and decrypt `oauth_state`. ### Tests - Added a regression test to ensure the proxy URL passed to `openAuthSessionAsync()` includes `oauthState=` when `storeStateStrategy` is configured as `"cookie"`. ## Context / Background Core Better Auth’s cookie-based OAuth state strategy stores the full state payload (incl. PKCE verifier) in an encrypted `oauth_state` cookie and the callback handler requires it to be present. Expo’s system browser navigation cannot include custom request headers, so the cookie must be set within the browser context before redirecting to the provider. ## Breaking changes / Deprecations - None. Existing behavior is preserved when `oauthState` is not provided. ## UI changes / Screenshots - None. ## Related Closes #6847 <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Fixes Expo OAuth when using cookie-based state by forwarding the encrypted oauth_state to the system browser via the authorization proxy. Prevents “State Mismatch. OAuth state cookie not found” during native sign-in. - **Bug Fixes** - Client reads oauth_state from the Expo cookie jar and adds it as oauthState to the proxy URL. - Proxy sets the oauth_state cookie with a 10-minute TTL, then redirects to the provider. - For database strategy, the proxy URL omits oauthState; tests cover both strategies. <sup>Written for commit 1c47c9cf84f306e3d488fd46f4c4781bd3653ff6. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. --> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-04-13 09:53:50 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#15221