expo: AuthSession returns dismiss result on Android standalone app

Summary

This issue is related to issue #6679 - AuthSession returns dismiss result even before the browser is opened (https://github.com/expo/expo/issues/6679).

Calling the AuthSession.startAsync for the first time returns the dismiss result immediately after calling. The browser opens correctly in the meantime, but any action done afterwards in the browser is ignored (login correctly, cancel, etc). This means that our users will go to the login screen, login successfully, but the app would understand this session as being dismissed.

This issue happens for an Expo managed app when it is installed as an Android standalone app. This is true for any Android device. Everything is fine, instead, by running the app inside Expo environment.

The result is that, when an app is running on Android as a standalone app, every authentication attempt returns dismiss.

Managed or bare workflow?

managed

What platform(s) does this occur on?

Android

SDK Version (managed workflow only)

40.0.1

Environment

Expo CLI 4.1.6 environment info:
    System:
      OS: Windows 10 10.0.19042
    Binaries:
      Node: 15.5.1 - C:\Program Files\nodejs\node.EXE
      npm: 7.3.0 - C:\Program Files\nodejs\npm.CMD
    SDKs:
      Android SDK:
        API Levels: 29, 30
        Build Tools: 28.0.3, 29.0.2, 29.0.3, 30.0.0, 30.0.1, 30.0.2, 30.0.3
        System Images: android-29 | Intel x86 Atom_64, android-29 | Google APIs Intel x86 Atom, android-29 | Google Play Intel x86 Atom, android-30 | Google APIs Intel x86 Atom
    IDEs:
      Android Studio: Version  4.1.0.0 AI-201.8743.12.41.6953283
    Expo Workflow: managed

Steps to Reproduce

You must build an Expo managed app for Android, and install it as standalone app on any Android device. Then simply call AuthSession.startAsync for the first time since the app has been loaded.

Reproducible Demo

I’ve developed two snack examples which could help to test and verify this issue.

You can find them here:

Please remember that, in order to experience this issue, this examples must be run as Android standalone apps.

In particular, while the first example could be run with Snack, this is not true for the second example. The second example requires app scheme and deep linking configuration (so it requires to customize app.json configuration file), so it must be downloaded and configured.

Let’s focus, for now, on the first (simple) snack demo.

When you’ll start this snack demo, you’ll see that the app renders a TestSignInScreen component, inside of which the authentication flow redirects to http://anything.com.

I believe that this could be enough to verify my issue, but just for completeness I’ve also prepared other two components, which are dedicated to Expo Azure AD authentication:

  • ExpoSignInScreen: authenticate to Azure AD using AuthSession.useAuthRequest
  • OpenIDSignInScreen: authenticate to Azure AD using AuthSession.startAsync.

In order to run the test with Azure AD, of course, you must first create a new Azure AD registration and specify your tenant and client configuration.

This configuration can be specified inside the following JSON object:

export const authConfig = {
    TENANT_ID: 'your-tenant-id',
    CLIENT_ID: 'app-client-id',
    REDIRECT_URI: `${Constants.linkingUrl}`,
    SCOPES: ['openid', 'profile', 'email', 'offline_access']
};

Explanation

This issue is related to issue #6679 AuthSession returns dismiss result even before the browser is opened (https://github.com/expo/expo/issues/6679).

Some important details were given by @LucaColonnello: https://github.com/expo/expo/issues/6679#issuecomment-570963717.

Issue #6679 is already closed, so I’m opening this new issue because I’m still experiencing this problem for Android standalone apps (Expo managed flow).

Calling the AuthSession.startAsync for the first time returns the dismiss result immediately after calling. The browser opens correctly in the meantime, but any action done afterwards in the browser is ignored (login correctly, cancel, etc). This means that our users will go to the login screen, login successfully, but the app would understand this session as being dismissed.

This issue happens for an Expo managed app when it is installed as an Android standalone app. This is true for any Android device. Everything is fine, instead, by running the app inside Expo environment.

The result is that, when an app is running on Android as a standalone app, every authentication attempt returns dismiss.

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 41
  • Comments: 133 (12 by maintainers)

Commits related to this issue

Most upvoted comments

Still an issue even with newest SDK 45.

Here’s how I got it to work with Google: Go to your app.config.js and add a custom intent filter: android.intentFilters:

[
  {
    action: "VIEW",
    category: ["BROWSABLE", "DEFAULT"],
    data: {
      scheme: `com.yourcool.app.auth`,
    },
  },
]

On your OAuth config page create a new client for Android. Fill the fields as usual, but instead of typical package name (com.yourcool.app) put your custom intent filter - com.yourcool.app.auth.

Use the newly created intent filter in your auth code with makeRedirectUri function:

  const [request, response, promptAsync] = Google.useAuthRequest({
    expoClientId: "yourid",
    iosClientId: "yourid",
    androidClientId: "yourid",
    redirectUri: Platform.select({
      // iOS handles redirectUri perfectly fine on it's own
      ios: undefined,
      // Due to Expo's bug, we need to manually encode the redirectUri
      // https://github.com/expo/expo/issues/12044
      android: makeRedirectUri({
        // intent filter set up in app.config.js
        // must be the same as "package name" in Google Cloud Console
        native: `${Constants.manifest?.android?.package}.auth://`,
      }),
    }),
  });

Notes:

  1. Do not use the same expo.scheme and android.package as it’s gonna lead to the double app bug mentioned before. Simple word is enough for expo.scheme.
  2. If you’re using Play Store’s App Signing remember to use the fingerprint available on Play Console, not the one returned by expo credentials:manager (applies to builds published on Google Play).

I think the conversation is a bit confused now. However the thing is that AuthSession is not working on Android because by default the expo app is adding multiple conflicting intent receivers to the AndroidManifest.xml and the wrong one is picking up the oauth callbacks.

I suggest some expo contributor / developer to go back to the comments around here https://github.com/expo/expo/issues/12044#issuecomment-958184919 and to possibly inspect the situation. As a bit of an outsider I believe the intent receiver mentioned there could be removed but I am not 100% sure why it was put there in the first place. I tried asking in the comment after but got no response.

I may try to propose my fix as a PR and see how it goes

Still an issue for me, commenting since this issue is marked stale.

Just tested with new Expo SDK 42 and issue persists.

I just don’t understand how can this be open since a year. If expo auth session isnt working on android standalone why not mention it in the documentation itself. GoogleSignIn package seemed to have worked for android standalone but has been deprecated and it also doesn’t work with Expo Go/ Web to debug which makes adding simple functionality like google sign in in the app a huge pain. Can there be an official estimate or workaround this issue? This is surely a significant problem.

Managed to successfully workaround the issue in Android Standalone by applying 2 changes to our app:

  1. Use a expo.scheme different than expo.android.package (I am using a single word without symbols or special characters)
  2. Comment out usage of the native prop from AuthSession makeRedirectUri() and let Expo Auth Proxy fully manage the redirect uri (even in standalone mode) like
const useProxy = Platform.select({ web: false, default: true });
const redirectUri = makeRedirectUri({ useProxy });

NOTE: This workaround only works if you apply both changes above.

Hope this help better understand the root issue.

New to mobile development and used expo as my first project. This bug really bothers me for 2-3 weeks. Luckily this is an open source project and be able to sort it out and get my app working on standalone android. Believe this bug is caused by the api call/event order in react native. Like other’s, have other busy stuff to work on and did not dig further into react-native itself. Nevertheless, the change needed is: in webbrowser.ts, change _onAppStateChangeAndroid method, change from: _onWebBrowserCloseAndroid() To: setTimeout(()=>_onWebBrowserCloseAndroid(), 0);

I’ve created a pull request for this for convenience. https://github.com/djisprucetech/expo/pull/1 Cheers, Don

After updating to Expo v45 and realizing that our Google login implementation of expo-google-app-auth would need to be replaced with this recommended package we made the switch. As noted multiple times in this issue thread, everything works except standalone Android (which returns Dismiss). There does not seem to be any consensus on how to work around this, but given that Expo v44 will be depreciated at some point and the older expo-google-app-auth will cease to be an option it would seem that something has to. Does anyone have a valid solution that 100% of the time works in Android standalone and does not lead to some other set of problems? Tried many of the suggestions above without success. Open to using another package if there is one that anyone can recommend that is supported in Expo managed projects…

Solved this by ditching expo build:android for eas build -p android and using original code in documentation

@Trunksome Got it, I’ll try it out this week and let you know. At the same time, I’m shocked that no one from expo dev team has clarified the current state, plan of action or commented about estimates given this is a pressing issue for many and thread is also quite active. We can together definitely find a solution, lets get this resolved asap.

@sankparatkar Totally agree, also, let me tell you that I tried to use GoogleSignIn and it actually worked when I built the APK, however, when I published the app on the Play Store it didn’t work. I don’t understand why.

In the same boat as @danielkv. About to release and have been stuck on this for a week now (the issue is happens on the Android Standalone app).

A bit off topic but related with issue where two app handlers where shown on redirect back.

This is because when expo.scheme has same value as expo.android.package, there will be two intents registering handlers for same scheme:

Intent 1 for host.exp.exponent.MainActivity

<intent-filter>
    <data android:scheme="com.org.app"/>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>

Intent 2 for net.openid.appauth.RedirectUriReceiverActivity

<intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <category android:name="android.intent.category.BROWSABLE"/>
    <data android:path="oauthredirect" android:scheme="com.org.app"/>
</intent-filter>

Note that intent 2 filter does define a path but since it doesn’t have a host, path is ignored leaving it wide open for any URI with that scheme. Meaning any URI triggered by “com.org.app” scheme can be picked up by any of the handlers.

Not sure if intent 2 is supposed to be there since we don’t use appAuth, but we don’t seem to have any control over it.

I’m still seeing this issue on Expo 50.

I have tried all the above listed solutions including the app.json intent configuration and using eas build. It works fine on expo but when I build apk , my response says success but the authentication field is just null and does not contain my access token.

Hi. Have you solved this problem?

I haven’t seen a solution yet.

For me, to solve this problem was to put the value of expo.scheme from the file app.json with the same value of android.package.

For example, if android.package has the value com.urlproject.app, expo.scheme need the same value.

//app.json
{
  "expo": {
    "name": "testapp",
    "slug": "testapp",
    "scheme": "com.urlproject.app",
    },
    [...]
    "android": {
      "package": "com.urlproject.app",
    },
  }
}

It has been pointed out by others but I want to stress it again. Google mandates the usage of a deeplink with a scheme matching the package name i.e. com.redacted.mobileapp:/. Expo generates an android manifest with

    <activity android:name="net.openid.appauth.RedirectUriReceiverActivity">
        <intent-filter>
            <action android:name="android.intent.action.VIEW"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <category android:name="android.intent.category.BROWSABLE"/>
            <data android:scheme="com.redacted.mobileapp" android:path="oauthredirect"/>
        </intent-filter>
    </activity>

The AuthSession implementation listens for deeplinks using Linking.addEventListener('url', ... Unfortunately the oauth redirect deeplink will be intercepted by net.openid.appauth.RedirectUriReceiverActivity and the Linking event will never be triggered.

If I expo eject -p android things start to work because the manifest does not contain net.openid.appauth.RedirectUriReceiverActivity

Why do we have this RedirectUriReceiverActivity there ?

@danielkv I totally understand your pain, I spent over 40 hours trying to get a good solution myself.

Hi @brentvatne, thank you too! Actually I don’t know if I will be able to investigate it, because I’m also involved in another app development. Let’s keep in touch. Please let me know also when you have any update about this matter.

I’m having this issue both using AuthSession.useAuthRequest (https://docs.expo.io/versions/latest/sdk/auth-session/#useauthrequest) and also using AuthSession.startAsync (https://docs.expo.io/versions/latest/sdk/auth-session/#authsessionstartasyncoptions).

I am currently using the following package versions:

  • "expo": "~40.0.1"
  • "react": "16.13.1"
  • "react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz" (0.63.4)
  • "expo-auth-session": "~3.0.0"
  • "expo-web-browser": "~8.6.0"
  • "expo-linking": "~2.0.1".

I tried both to log AppState.currentState and the patch suggested by @LucaColonnello, but everything was no use.

Please, can you help me?

I’ll try to explain here some details…

My app authentication flow is based on Microsoft Azure Active Directory Authentication (https://docs.expo.io/guides/authentication/#azure).

We can start with some details about my Azure AD configuration:

image

image

Here is how i tried to authenticate using AuthSession.useAuthRequest and AuthSession.startAsync.

AuthSession.useAuthRequest

In this paragraph I’ll show how I tried to authenticate with useAuthRequest hook and promptAsync. Everything works on iOS and on Android dev environment. I’m having the type='dismiss' issue, instead, on Android production environment.

import * as React from 'react';
import { useEffect } from 'react';
import * as WebBrowser from 'expo-web-browser';
import { useAutoDiscovery, useAuthRequest } from 'expo-auth-session';

import { authConfig, AzureEndpointUrl } from '../constants/Authentication';
import { handleAuthenticationResponse } from '../core/authentication/Authentication.base';

import { Text, TouchableOpacity, View, SafeAreaView } from '../components/Themed';
import { styles } from '../layout/Styles';

WebBrowser.maybeCompleteAuthSession();

export default function ExpoSignInScreen({navigation, onResponseReceived, onSignIn}) {
  // Authentication request config
  const requestConfig = {
    clientId: authConfig.CLIENT_ID,
    scopes: authConfig.SCOPES,
    redirectUri: authConfig.REDIRECT_URI
  };

  // OpenID discovery document
  const authDiscoveryDocument = useAutoDiscovery(AzureEndpointUrl);

  // Authentication Request
  const [request, response, promptAsync] = useAuthRequest(
    requestConfig,
    authDiscoveryDocument
  );

  // Authentication response management
  useEffect(() => {
    if (response) {
      console.log(response);
      handleAuthenticationResponse(response, onResponseReceived, onSignIn);
    }
  }, [response]);

  return (
    <SafeAreaView style={[styles.verticalContainer, styles.contentContainer]}>
      ...
      <View style={styles.verticalContainer}>
        <TouchableOpacity style={styles.button} disabled={!request} onPress={() => { promptAsync({ useProxy: false }); }}>
            <Text style={styles.buttonText}>Sign In</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.button} disabled={!request} onPress={() => { promptAsync({ useProxy: true }); }}>
            <Text style={styles.buttonText}>Sign In (use proxy)</Text>
        </TouchableOpacity>
        ...
      </View>
    </SafeAreaView>
  );
}

AuthSession.startAsync

In this sectionI’ll show how I tried to authenticate with AuthSession.startAsync. I tried both to leave the returnUrl as default and also to pass it.

So, using the utilities

import { startAsync } from 'expo-auth-session';

export async function startAuthRequestAsync(requestUrl) {
    return await startAsync({ authUrl: requestUrl });
};

export async function startAuthRequestAndReturnAsync(requestUrl, returnUrl) {
    return await startAsync({ authUrl: requestUrl, returnUrl: returnUrl });
};

and

// Sign-in and get and ID token
export function getIdTokenUrl(authorizationEndpoint, client_id, redirect_uri, scope, state, nonce) {
    return authorizationEndpoint + `?client_id=${client_id}&response_type=id_token&redirect_uri=${encodeURIComponent(redirect_uri)}&response_mode=fragment&scope=${encodeURIComponent(scope)}&state=${state}&nonce=${nonce}`;
}

// Sign-in and get both an ID token and an authorization code
export function getIdTokenAndAuthorizationCodeUrl(authorizationEndpoint, client_id, redirect_uri, scope, state, nonce) {
    return authorizationEndpoint + `?client_id=${client_id}&response_type=${encodeURIComponent('id_token code')}&redirect_uri=${encodeURIComponent(redirect_uri)}&response_mode=fragment&scope=${encodeURIComponent(scope)}&state=${state}&nonce=${nonce}`;
}

// Sign-in and get both an ID token and an access token
export function getIdTokenAndAccessTokenUrl(authorizationEndpoint, client_id, redirect_uri, scope, state, nonce) {
    return authorizationEndpoint + `?client_id=${client_id}&response_type=${encodeURIComponent('id_token token')}&redirect_uri=${encodeURIComponent(redirect_uri)}&response_mode=fragment&scope=${encodeURIComponent(scope)}&state=${state}&nonce=${nonce}`;
}

// Sign Out
export function getSignOutUrl(endSessionEndpoint, post_logout_redirect_uri) {
    return endSessionEndpoint + `?post_logout_redirect_uri=${encodeURIComponent(post_logout_redirect_uri)}`;
}

we have

import * as React from 'react';
import { AppState } from 'react-native';
import Constants from 'expo-constants';
import * as WebBrowser from 'expo-web-browser';
import { useAutoDiscovery } from 'expo-auth-session';

import { authConfig, AzureEndpointUrl } from '../constants/Authentication';
import { getScopes, handleAuthenticationResponse } from '../core/authentication/Authentication.base';
import { startAuthRequestAsync, startAuthRequestAndReturnAsync } from '../core/authentication/Authentication.Expo';
import { getIdTokenAndAccessTokenUrl } from '../core/authentication/Authentication.Implicit';

import { Text, TouchableOpacity, View, SafeAreaView } from '../components/Themed';
import { styles } from '../layout/Styles';

WebBrowser.maybeCompleteAuthSession();

export default function OpenIDSignInScreen({navigation, onResponseReceived, onSignIn}) {
  // Authentication request config
  const requestConfig = {
    clientId: authConfig.CLIENT_ID,
    scopes: authConfig.SCOPES,
    redirectUri: authConfig.REDIRECT_URI
  };

  // OpenID discovery document
  const authDiscoveryDocument = useAutoDiscovery(AzureEndpointUrl);

  let response = null;

  const getIdTokenAsync = async () => {
    const requestUrl = getIdTokenAndAccessTokenUrl(authDiscoveryDocument.authorizationEndpoint, authConfig.CLIENT_ID, authConfig.REDIRECT_URI, getScopes(authConfig.SCOPES), 'Aa123456', 'Aa78910');
    response = await startAuthRequestAsync(requestUrl);

    if (response) {
      console.log(response);
      handleAuthenticationResponse(response, onResponseReceived, onSignIn);
    }
  };

  const getIdTokenAndDefaultReturnAsync = async () => {
    const requestUrl = getIdTokenAndAccessTokenUrl(authDiscoveryDocument.authorizationEndpoint, authConfig.CLIENT_ID, authConfig.REDIRECT_URI, getScopes(authConfig.SCOPES), 'Aa123456', 'Aa78910');
    response = await startAuthRequestAndReturnAsync(requestUrl, `${Constants.linkingUrl}expo-auth-session`);

    if (response) {
      console.log(response);
      handleAuthenticationResponse(response, onResponseReceived, onSignIn);
    }
  };

  const getIdTokenAndReturnAsync = async () => {
    const requestUrl = getIdTokenAndAccessTokenUrl(authDiscoveryDocument.authorizationEndpoint, authConfig.CLIENT_ID, authConfig.REDIRECT_URI, getScopes(authConfig.SCOPES), 'Aa123456', 'Aa78910');
    response = await startAuthRequestAndReturnAsync(requestUrl, authConfig.REDIRECT_URI);

    if (response) {
      console.log(response);
      handleAuthenticationResponse(response, onResponseReceived, onSignIn);
    }
  };

  return (
    <SafeAreaView style={[styles.verticalContainer, styles.contentContainer]}>
      ...
      <View style={styles.verticalContainer}>
        <TouchableOpacity style={styles.button} onPress={getIdTokenAsync}>
            <Text style={styles.buttonText}>Sign In</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.button} onPress={getIdTokenAndDefaultReturnAsync}>
            <Text style={styles.buttonText}>Sign In (with default return)</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.button} onPress={getIdTokenAndReturnAsync}>
            <Text style={styles.buttonText}>Sign In (with return)</Text>
        </TouchableOpacity>
        ...
      </View>
    </SafeAreaView>
  );
}

Everything works on iOS and on Android dev environment. I’m having the type='dismiss' issue, instead, on Android production environment.

Finally, I tried again starting the authentication session with WebBrowser.openAuthSessionAsync, but it was useless. The result was the same as before.

Please, can you help me to overcome this issue?

Issue

I was getting {type:dismiss} in the response on Android Preview APK and also in the development build.

Environment

I am using Expo SDK 50 and expo-auth-session for azure authentication.

Fix

I fixed it by removing every capital letter from the redirect. I was getting issue on aimatrix://Login. Fixed it by changing it to aimatrix://login

const redirectUri = makeRedirectUri({
    scheme: "aimatrix",
    path: "login",
  });

@raphaelobinna @gvanderclay @remziz4

After more than a year of the expo google login not working, expo deprecated the package and advised people to use the react-native-google-signin here if you intend to go towards that road, it requires you to build custom native code here. I didn’t what to do that , so i built a simple react application that made use of gapi-script for google login. Then routed the google button click on my app to the page using WebBrowser.openAuthSessionAsync. The react app redirected back to my app using the google login token which I got access to using Linking.addEeventlistener

Hello, what it was happening to me was that in my android object inside App.json i had my “package” like this (one upperCase)


  "android": {
      "package": "com.Azael",
      "versionCode": 38,
      "icon": "./assets/images/general/appIcon.png",
      "softwareKeyboardLayoutMode": "pan"
    },


and it was causing that Google Auth do not knew very well to where redirect, my fix was very straigth forward, i just changed the package to all lowercase, like this:


    "android": {
      "package": "com.azael",
      "versionCode": 38,
      "icon": "./assets/images/general/appIcon.png",
      "softwareKeyboardLayoutMode": "pan"
    },

and everything worked fine, but trust on me on this one and do not waste more time, if your package inside android object inside expo has even one uppercase you’re screwed, i had to create a WHOLE NEW APP in order to make it work, but well… it was a fix.

hope this helps.

EDIT: this might be one of the solutions for a specific problem, it was my case, ❤️

I think I’ve got a New Year’s wonder for you guys. I managed to make it work by chance. It seems that the request result is dismissed, but, at the same time, all the needed params are returned in the URL query string after the startAsync call.

So I have a wrapper around the main component, that has the following logic inside

import { Linking } from 'react-native';

  useEffect(() => {
    const handleDeepLinking = async (url: string | null): Promise<void> => {
      if (!url) return;
      const correctUrl = url.includes('#') ? url.replace('#', '?') : url;
      const urlObject = new URL(correctUrl);
      const accessToken = urlObject.searchParams.get('access_token');
      const refreshToken = urlObject.searchParams.get('refresh_token');
      if (!accessToken || !refreshToken) return;
      ...
    };
    const listener = (event: { url: string }) => {
      void handleDeepLinking(event.url);
    };
    const subscription = Linking.addEventListener('url', listener);
    void Linking.getInitialURL().then((url) => handleDeepLinking(url));
    return () => {
      subscription.remove();
    };
  }, []);

This worked for me. It’s a great workaround until they fix this issue. The only change that I made was run the useEffect when the response and request change, additionally the query parameter that I use was code instead of access_token and refresh_token, but I think its depends on the provider.

const [request, response, signInWithAzureAsync] = useAuthRequest({...});

useEffect(() => {
....
}, [response, request]);

Package versions:

"expo": "^47.0.0",
"expo-auth-session": "^3.8.0",
"expo-linking": "~3.3.0",

I think I’ve got a New Year’s wonder for you guys. I managed to make it work by chance. It seems that the request result is dismissed, but, at the same time, all the needed params are returned in the URL query string after the startAsync call.

So I have a wrapper around the main component, that has the following logic inside

import { Linking } from 'react-native';

  useEffect(() => {
    const handleDeepLinking = async (url: string | null): Promise<void> => {
      if (!url) return;
      const correctUrl = url.includes('#') ? url.replace('#', '?') : url;
      const urlObject = new URL(correctUrl);
      const accessToken = urlObject.searchParams.get('access_token');
      const refreshToken = urlObject.searchParams.get('refresh_token');
      if (!accessToken || !refreshToken) return;
      ...
    };
    const listener = (event: { url: string }) => {
      void handleDeepLinking(event.url);
    };
    const subscription = Linking.addEventListener('url', listener);
    void Linking.getInitialURL().then((url) => handleDeepLinking(url));
    return () => {
      subscription.remove();
    };
  }, []);

I have tried all the above listed solutions including the app.json intent configuration and using eas build. It works fine on expo but when I build apk , my response says success but the authentication field is just null and does not contain my access token.

@knorsen did you try my method above? i have it working for stand alone google app. Its on the play store. So it works

I’m having the same issue on SDK 45: Browser returns {“type”: “dismiss”} with no Google data or token.

No problems on iOS standalone or Android/iOS Expo Go. This problem only exists on Android Standalone. I have a scheme set up (myapp://) that is different than my package name that I need to keep for expo-linking to work. Is there any way to make this work with my custom scheme (myapp://)?

i’m having same issue for standalone Android build, Facebook login, using supabase oAuth works fine on iOS and on expo [on android]

      const returnUrl = makeRedirectUri({
        path: '/Login/splash'
      })

      const payload = await startAsync({
        authUrl: `https://xxx.supabase.co/auth/v1/authorize?provider=${type}&redirect_to=${returnUrl}`,
        returnUrl,
      })

the payload is not returning any data

Haven’t tested yet on sdk 45, but I hope it’s been fixed

Hi, does it work with new Expo 45?

We are also experiencing the same issue when authenticating with Google - the scheme is required to match the android.package name but this exposes the bug of a duplicate handler (one interfering with the auth process).

Has anyone found a successfully way of dealing with this? @davibe / @markthelaw did you have any success/issues removing the extra handler?

Thanks all!

Any updates on this? This has been an issue for almost a year now. That’s not a big issue in itself, but shouldn’t it say in the docs that you should not use AuthSession if you run Android standalone?

@danielkv Thank you for your response. But I’m not developer so I can’t implement something, but if the codes I shared is correct then I miss the configuration which the developers share a expo guide link to follow up.

But with my little experience try my best to config. But can’t get it working. So actually don’t know how to get it working, really need help.

Thank you.

Sounds like you need to push back and tell them this is a developer’s job. We can’t teach you to code on a github issue thread, not time or place. FYI, @umarummas I also ended up using the old Expo GoogleSignIn api, which is working properly, when app is in Android StandAlone, but this really should be fixed on expo-auth-session side as GoogleSignIn is a deprecated library.

RedirectUriReceiverActivity was introduced in https://github.com/expo/expo/commit/792b608c5ed0d5cd9d4b0698e037374167e1581b by @janicduplessis Is it used today ? in which scenarios ?

I reproduced this issue in Expo Go. I cannot use proxy so I have used Linking for android case

  const codeHandler = useCallback(async (event: Linking.EventType) => {
    const parsed = Linking.parse(event.url);
    const parsedCode = parsed.queryParams?.code;
    if (parsedCode) {
      setCode(c => c ?? parsedCode);
    }
  }, []);

  useEffect(() => {
    Linking.addEventListener('url', codeHandler);

    return () => Linking.removeEventListener('url', codeHandler);
  }, [codeHandler]);

@brentvatne @lukmccall please note inside expo go or local metro server everything works fine. This issue only occurs when running in standalone app .apk or .aab from expo build:android and for that reason it’s really hard to debug, definitely exceeds my android skill level 😦