After upgrading Expo, the React Native Auth Session ceased to function

During my use of Expo SDK 48, my app successfully implemented Google and Facebook authentication with a web browser-based authentication method.

Functional 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);
        }

    }


}

However, after upgrading Expo to SDK 50 and expo-auth-session to 5.4.0, the functionality of AuthSession.startAsync() ceased to operate.

In my attempt to transition to the new approach outlined in the documentation here and this example, I encountered an error while fetching from the URL mentioned below:

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

Non-functional 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() {

            // The issue leading to promise rejection
            const getDiscovery = await fetchDiscoveryAsync(endpoint).then((discovery) => setDiscovery({ discovery })); 

           // No output is displayed in the 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 there still a way to utilize web browser-based authentication for Google and Facebook?

Edit:

I was able to obtain a discovery using https://accounts.google.com as an endpoint, resulting in the following JSON. However, my response remains 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 should I proceed with utilizing this discovery document? My objective is to retrieve the user's email from Google after they input their credentials, similar to how it functioned prior to the SDK updates.

Answer №1

Once I successfully tracked down Google's discovery document link and utilized the Uber authentication example, I was able to launch the web browser authentication to input my Google 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);
    }

}

Despite entering my Google account login information, I encountered an error:

Something went wrong trying to finish signing in

but I believe this issue should be addressed in a separate question.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The parameter type '{ email: string; }' in NGXS does not accept arguments of type 'string'

Struggling to retrieve data from an API using ngxs with this.store.dispatch(new GetUser(userEmail)) Attempted to use the user id stored in local storage as a string and convert it to a number but encountered a similar error (Argument of type 'string&a ...

Typescript is unable to comprehend that the initial item in an array of strings is considered to be a string

Here are the functions I am working with: const transitionGroup = ( propertyName: string, durationMultiple = 1, timingFunction = 'linear', delayMultiple = 0, ): string => { // ...more logic here return [propertyName, duration, tim ...

Develop a structured type that encompasses the stationary attributes of an object-oriented class

Provided are the following classes: class EnumerationDTO { designation: string; id: number; } class ExecutionStatusDTO extends EnumerationDTO { static readonly open: ExecutionStatusDTO = { id: 0, designation: 'Open' }; static readonl ...

Typescript MUI Autocomplete: Can you specify the parameter type of the PaperComponents function?

If you use MUI's Autocomplete, there is a property called PaperCompomponent that allows you to pass your own react component. This property is a function with properties as a parameter, which can then be used to pass on to your custom component. In T ...

Angular Material - Adding tooltips to Mat-table rows

I am currently working with Angular Material Mat-Table and I have a requirement to show a tooltip when hovering over any row. Essentially, I need to filter data from mGridDataSource based on the row id. Since I am new to Angular, I would greatly appreciate ...

What is causing the .responseToString function to be recognized as not a function?

Consider the following scenario with Typescript: interface IResponse { responseToString(): string; } export default IResponse; We have two classes that implement this interface, namely RestResponse and HTMLResponse: import IResponse from "./IRespo ...

Is there a more efficient method for invoking `emit` in Vue's Composition API from an external file?

Is there a more efficient way to access the emit function in a separate logic file? This is my current approach that is functioning well: foo.js export default (emit) => { const foo = () => { emit('bar') }; return { foo }; } When ...

Map does not provide zero padding for strings, whereas forEach does

Currently working on developing crypto tools, I encountered an issue while attempting to utilize the map function to reduce characters into a string. Strangely enough, one function works perfectly fine, while the other fails to 0 pad the string. What could ...

How can I apply unique "compilerOptions" settings to a specific file in tsconfig.json file?

Can I apply specific tsconfig options to just one file? Here is my current tsconfig.json: { ... "compilerOptions": { ... "keyofStringsOnly": false, "resolveJsonModule": true, "esModuleInterop": t ...

A collection of objects in TypeScript with a reference and the ability to add new objects using the

Recently, I've come across an issue in my code while working with custom objects and arrays of them. I have identified a scenario where the push() method works fine and another where it doesn't. Scenario 1 (working as expected): class MyObject{ ...

How to retrieve the language of an iOS device using React Native

Currently, I am attempting to retrieve the current locale from iOS devices. During the creation of my project and while using expo, I followed this specific guide. I have experimented with the following code: if (Platform.OS === "android") { langR ...

Using the VSCode debugger to place a breakpoint within a Typescript package that has been symlinked using `npm link`

I'm currently troubleshooting a NodeJS application and its associated typescript packages, which have been linked using `npm link`. The directory structure is as follows: /root/package-a # typescript package /root/package-b # another typescript packa ...

Ensuring a child element fills the height of its parent container in React Material-UI

Currently, I am in the process of constructing a React Dashboard using MUI. The layout consists of an AppBar, a drawer, and a content area contained within a box (Please correct me if this approach is incorrect)... https://i.stack.imgur.com/jeJBO.png Unf ...

The TypeScript compiler is indicating that the Observable HttpEvent cannot be assigned to the type Observable

Utilizing REST API in my angular application requires me to create a service class in typescript. The goal is to dynamically switch between different url endpoints and pass specific headers based on the selected environment. For instance: if the environmen ...

Stop MatDialog instance from destroying

In my application, I have a button that triggers the opening of a component in MatDialog. This component makes API calls and is destroyed when the MatDialog is closed. However, each time I open the MatDialog for the second time by clicking the button agai ...

Most effective methods for validating API data

Currently, I am working on developing an api using nestjs. However, I am facing some confusion when it comes to data validation due to the plethora of options available. For instance, should I validate data at the route level using schema validation (like ...

Trouble with React Context State Refreshing

Exploring My Situation: type Props = { children: React.ReactNode; }; interface Context { postIsDraft: boolean; setPostIsDraft: Dispatch<SetStateAction<boolean>>; } const initialContextValue: Context = { postIsDraft: false, setPostIs ...

Vue HeadlessUI Error: The function vue.defineComponent is not recognized

Trying to incorporate @headlessui/vue into my nuxt project has been a challenge. My attempt at using it looks like this: <template> <Menu> <MenuItems> <MenuItem>Item</MenuItem> </MenuItems> </Menu&g ...

Simple methods for ensuring a minimum time interval between observable emittance

My RxJS observable is set to emit values at random intervals ranging from 0 to 1000ms. Is there a way to confirm that there is always a minimum gap of 200ms between each emission without skipping or dropping any values, while ensuring they are emitted in ...

Utilizing an object property for @Input binding in Angular: A step-by-step guide

I am currently delving into Angular and Ionic Frameworks, honing my skills through practice. I'm encountering an issue with a basic @Input test, where I am trying to iterate through an array of Tab Pages and then display each tab using <ion-tab> ...