Skip to content

@okta/okta-react-native incompatible with React Native 0.85+ / Expo SDK 56 (New Architecture) — fetch resolution failure and iOS URLSession redirect interception #485

Description

@ghasemikasra39

Describe the bug?

We use @okta/okta-react-native@2.17.0 in a production app and after upgrading to Expo SDK 56 (React Native 0.85, New Architecture enabled), email/password login via signIn() is completely broken due to two separate issues:

Issue 1: @okta/okta-auth-js — fetch fails in React Native 0.85 New Architecture

In fetchRequest.js, fetch is resolved as:

var fetch = window.fetch || crossFetch;

In React Native 0.85 with New Architecture (bridgeless mode), window.fetch is undefined and crossFetch does not function correctly in the RN environment. The POST request to /api/v1/authn receives no response.

Root cause: The New Architecture uses a JSI-based runtime where window globals behave differently. The existing fix from PR #360 (adding "react-native" field to package.json for correct ponyfill resolution) does not fully resolve this under the new bundler/runtime behavior.

Suggested fix: Use globalThis.fetch instead of window.fetch, or add RN environment detection that uses the global fetch directly. Alternatively, document the httpRequestClient option more prominently as the recommended approach for React Native.

Issue 2: Native iOS SDK — URLSession.shared is intercepted by Expo

The authenticate(withSessionToken:) method in the native OktaOidc iOS SDK uses URLSession.shared (via OKTURLSessionProvider.session()RedirectBlockingURLSessionDelegate.shared). Expo SDK 56 registers a URLProtocol interceptor on the shared session for network debugging (ExpoRequestInterceptorProtocol in expo-dev-launcher).

When the /authorize endpoint responds with a 302 redirect to a custom URL scheme (e.g. com.example.myapp://callback?code=...), the interceptor attempts to follow the redirect but fails because it's not an HTTP scheme. The native SDK then receives nil as the response, resulting in:

Error: Sign in was not authorized
code: '-1000'
detail: Authorization Error: Authentication Error: No response

This is the same issue reported in #450 and confirmed in okta-oidc-ios PR #362.

Note: This affects both dev and production builds if any library registers a URLProtocol on URLSession.shared.

Suggested fixes:

  • Use a dedicated URLSession(configuration: .default) instead of .shared so that third-party URLProtocol registrations don't affect the SDK.
  • Alternatively, use ASWebAuthenticationSession for the authenticate(withSessionToken:) flow as well, since it natively handles custom-scheme redirects.

Our current workaround

We created a yarn patch that modifies the signIn function in @okta/okta-react-native/index.js so that after a successful signInWithCredentials, instead of calling authenticate({ sessionToken }), it calls signInWithBrowser({ sessionToken, noSSO: true }). This works but has the drawback of briefly showing a system browser sheet during login.

What is expected to happen?

  • signIn({ username, password }) should complete the full authentication flow (credentials → session token → token exchange) without errors on React Native 0.85+ with New Architecture enabled.
  • The fetch call to /api/v1/authn should succeed regardless of the JS runtime's window global state.
  • The native iOS token exchange (authenticate(withSessionToken:)) should successfully handle the 302 redirect to a custom URL scheme without being intercepted by third-party URLProtocol handlers registered on URLSession.shared.
  • The SDK should be compatible with Expo SDK 56 and React Native's New Architecture without requiring patches.

What is the actual behavior?

Issue 1: fetch failure

Calling signIn({ username, password }) triggers a request to /api/v1/authn via okta-auth-js. The request silently fails — no response is returned, no network error is thrown. The Promise never resolves. In some configurations, fetch evaluates to false or undefined, causing a TypeError: fetch is not a function crash.

Issue 2: iOS token exchange failure

After signInWithCredentials succeeds and returns a valid sessionToken, the subsequent call to authenticate({ sessionToken }) fails on iOS with:

Error: Sign in was not authorized
code: '-1000'
detail: Authorization Error: Authentication Error: No response

The Okta server logs show a successful /authorize call with a 302 redirect to the custom scheme. The native SDK never receives the redirect response because Expo's URLProtocol interceptor consumes it. Android is not affected by Issue 2.

Reproduction Steps?

  1. Create a React Native app with Expo SDK 56 (React Native 0.85) and New Architecture enabled.
  2. Install @okta/okta-react-native@2.17.0.
  3. Configure Okta with a custom redirect URI using a custom URL scheme (e.g. com.example.myapp://callback).
  4. Call createConfig(...) with valid Okta configuration.
  5. Call signIn({ username: '...', password: '...' }) with valid credentials.

For Issue 1: The fetch call inside okta-auth-js to /api/v1/authn fails silently or throws TypeError: fetch is not a function.

For Issue 2 (iOS only): Even if Issue 1 is patched (e.g. by providing a custom httpRequestClient), the native authenticate({ sessionToken }) step fails with error code -1000 and message "No response". This is reproducible on both Simulator and physical device. Android completes the flow successfully with the same configuration.

Minimal reproduction path for Issue 2:

  • The issue is triggered specifically when expo-dev-launcher (or any library) registers a URLProtocol on URLSession.shared.
  • Removing expo-dev-launcher from the build resolves the iOS issue, but this is not a viable solution for development builds.

Additional Information?

No response

SDK Version

SDK Version

@okta/okta-react-native: 2.17.0
react-native: 0.85.x
expo: ~56.x

Build Information

Build Information

  • iOS deployment target: 15.1
  • New Architecture: enabled
  • Bridgeless mode: enabled
  • Hermes: enabled

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions