Search code examples
typescriptreact-nativeoauth-2.0expoexpo-auth-session

React Native Auth Session stopped working after Expo upgrade


I was using Expo SDK 48 and my app was working fine with Google and Facebook authentication using a web browser based authentication scheme.

Working code:


type AuthResponse = AuthSession.AuthSessionResult & {
    params: {
        access_token?: string;
        error?: string;
    };
    type: string;
}
...

const AuthProvider = ({ children }: any) => {

/* Google */
    const signInWithGoogle = async (navigation: any) => {

        try {

            const SCOPE = encodeURI("email profile");
            const RESPONSE_TYPE = 'token';

            const authUrl = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${GOOGLE_WEB_CLIENT_ID}&redirect_uri=${REDIRECT_URI}&response_type=${RESPONSE_TYPE}&scope=${SCOPE}`;

            const { type, params } = await AuthSession.startAsync({ authUrl }) as AuthResponse;
            console.log("type ==>> " + type);
            console.log("params ==>> " + JSON.stringify(params));


            if (type === 'success') {

                checkGoogleUserInfo(params.access_token, navigation);

            } else {
                return;
            }

        } catch (error) {

            console.log("Error retrieving token data from Google ==>> ", error);
        }

    }


}

After Expo has been upgraded to SDK 50 and expo-auth-session to 5.4.0, AuthSession.startAsync() stopped working.

I'm trying to use the new method from docs and follow this example but I'm getting following error while trying to fetch from the url on code below:

Possible unhandled promise rejection: SyntaxError: JSON Parse error: Unexpected character: <

Not working code:

const endpoint = "https://accounts.google.com/o/oauth2/v2/auth";
    const clientId: any = GOOGLE_WEB_CLIENT_ID;
    const redirectUri: any = REDIRECT_URI;
    const [discovery, setDiscovery] = useState({});

    useEffect(() => {

        async function loadDiscovery() {

            // here is the issue causing promise rejection
            const getDiscovery = await fetchDiscoveryAsync(endpoint).then((discovery) => setDiscovery({ discovery })); 

           // nothing is displayed in console
            console.log("get getDiscovery >>>>>> " + JSON.stringify(getDiscovery)); 
        }

        loadDiscovery();
    }, []);



    const [request, response, promptAsync] = useAuthRequest({ clientId, scopes: ['email', 'profile'], redirectUri }, discovery);

    useEffect(() => {

        console.log(discovery);

        if (!discovery) {
            console.log("no discovery");
            return;
        }

        if (response?.type === "error") {
            console.log("response type error: " + (response.params.error || "something went wrong"))
            return
        }

        if (!discovery || (response?.type !== "success")) {
            console.log("no discovery and no response type");
            return;
        }


        const code = response.params.code;
        if (!code) {
            console.log("no code");
            return;
        }


    }, [response, discovery]);

Is that still possible to use web browser based authentication with Google and Facebook?

Edit:

I finally got a discovery using https://accounts.google.com as an endpoint and the result is the json below. But my response is still null.

{
    "discovery": {
        "discoveryDocument": {
            "issuer": "https://accounts.google.com",
            "authorization_endpoint": 
            "https://accounts.google.com/o/oauth2/v2/auth",
            "device_authorization_endpoint": 
            "https://oauth2.googleapis.com/device/code",
            "token_endpoint": "https://oauth2.googleapis.com/token",
            "userinfo_endpoint": 
            "https://openidconnect.googleapis.com/v1/userinfo",
            "revocation_endpoint": "https://oauth2.googleapis.com/revoke",
            "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",
            "response_types_supported": [
                "code",
                "token",
                "id_token",
                "code token",
                "code id_token",
                "token id_token",
                "code token id_token",
                "none"
            ],
            "subject_types_supported": [
                "public"
            ],
            "id_token_signing_alg_values_supported": [
                "RS256"
            ],
            "scopes_supported": [
                "openid",
                "email",
                "profile"
            ],
            "token_endpoint_auth_methods_supported": [
                "client_secret_post",
                "client_secret_basic"
            ],
            "claims_supported": [
                "aud",
                "email",
                "email_verified",
                "exp",
                "family_name",
                "given_name",
                "iat",
                "iss",
                "locale",
                "name",
                "picture",
                "sub"
            ],
            "code_challenge_methods_supported": [
                "plain",
                "S256"
            ],
            "grant_types_supported": [
                "authorization_code",
                "refresh_token",
                "urn:ietf:params:oauth:grant-type:device_code",
                "urn:ietf:params:oauth:grant-type:jwt-bearer"
            ]
        },
        "authorizationEndpoint": 
        "https://accounts.google.com/o/oauth2/v2/auth",
        "tokenEndpoint": "https://oauth2.googleapis.com/token",
        "revocationEndpoint": "https://oauth2.googleapis.com/revoke",
        "userInfoEndpoint": 
        "https://openidconnect.googleapis.com/v1/userinfo"
    }
}

How can I use this discovery document? I'm trying to get user email from Google after user enter his credentials as it was working before SDK changes.


Solution

  • After I finally find Google's discovery document link and using Uber authentication example, I can open the web browser authentication to enter Google's credentials:

    const discovery = {
        authorizationEndpoint: 'https://accounts.google.com/o/oauth2/v2/auth',
        tokenEndpoint: 'https://oauth2.googleapis.com/token',
        revocationEndpoint: 'https://oauth2.googleapis.com/revoke'
    };
    
    
    const AuthProvider = ({ children }: any) => {
    
         const [request, response, promptAsync] = useAuthRequest({
            clientId,
            scopes: ['email', 'profile'],
            redirectUri,
            responseType: 'code',
        },
            discovery
        );
    
        useEffect(() => {
    
            console.log("request >>>>>>>>>>>>>>>>>> " + JSON.stringify(request));
            console.log("response >>>>>>>>>>>>>>>>>> " + JSON.stringify(response));
            console.log("discovery >>>>>>>>>>>>>>>>>> " + JSON.stringify(discovery));
    
        }, [response]);
    
        ...
    
        /* Google */
        const signInWithGoogle = async (navigation: any) => {
    
        try {
    
            promptAsync();
       
        } catch (error) {
    
           console.log("Error retrieving token data from Google ==>> ", error);
        }
    
    }
    
    

    I'm still facing an error after enter Google account login:

    Something went wrong trying to finish signing in

    but I think this should be discussed on another question.