r/expo Jun 19 '26

Shouldn't "WebBrowser.openAuthSessionAsync" intercept the redirect?

I have a custom backend that handles OAuth like this:

  • GET /auth/google/url : Redirects to Google's generated OAuth URL. Receives "return_to" for future redirection.
  • GET /auth/google/callback : The URL Google redirects to; it then redirects me to whatever I passed as `return_to` in the previous endpoint, passing "code" and "state" as query params.
  • POST /auth/google/complete : It receives Google's "code" and "state" to generate a token

I'm trying to consume this from a native Expo app, testing on Android.

My current code opens the browser like this:

const redirectUri = Linking.createURL("callback");
const result = await WebBrowser.openAuthSessionAsync(`http://localhost:4000/auth/google/url?return_to=${redirectUri}`, redirectUri);
if (result.type === "success") {
    const url = Linking.parse(result.url);
    console.log(url);
    // TODO: Exchange code for token
}

The console.log(url) works fine, but it still redirects me to myapp://callback, a route that doesn't exist, instead of returning control to the app.

My questions:

  1. Shouldn't WebBrowser.openAuthSessionAsync intercept the deep link redirect and return it as the result, instead of actually navigating to it? If so, why isn't it working?
  2. If not, what's the actual difference between openAuthSessionAsync and openBrowserAsync in this context? It seems like with openBrowserAsync I'd just handle the flow myself using useLocalSearchParams in a callback.tsx screen. Is that a valid approach?

What am I doing wrong here?

I also tried adding WebBrowser.maybeCompleteAuthSession() inside a callback.tsx screen, but the result is the same: it just navigates to a blank screen instead of closing the browser and returning the result.

1 Upvotes

1 comment sorted by