Search code examples
react-nativeauth0

React native get refresh token with Social login with Auth0


I have the following code from my react native app where i've used auth0 to login users using their Facebook, Google account, Apple.

  const authProviders = {
    google: {
      name: "google-oauth2",
      icon: <GoogleIcon />,
      color: cs["apple"],
    },
    facebook: {
      name: "facebook",
      icon: <FacebookIcon />,
      color: cs["facebook"],
    },
    apple: {
      name: "apple",
      icon: <AppleIcon />,
      color: cs["apple"],
    },
  };

  const onSocialAuth = async (type: auth) => {
    const connection = type.name;
    dispatch(setProvider(connection));
    const result = await LoginAs(connection);

    if (result.type === "success" && Platform.OS === "ios") {
      const accessToken = result.url
        ? result.url.match(/access_token=([^&]+)/)
        : null;
      const token = accessToken ? accessToken[1] : null;
      const { payload } = await dispatch(
        userSocialLogin({ access_token: token ?? "" })
      );
      if (payload?.data?.token) {
        await storeAsyncData(ENUM.PREVIOUS_AUTH_PROVIDER, type.name);
        dispatch(completeLogin(payload?.data?.token));
        authContext.setAuth({
          isUserLoggedIn: !!payload?.data?.token,
          token: payload?.data?.token,
        });
      } else {
        alert("Something went wrong while logging with " + connection);
      }
    } else {
      // alert('Failed to log in');
    }
  };


  return (
        <View style={{ flexDirection: "column", gap: 10, alignItems: "center" }}>

          {providers?.includes('google-oauth2') &&<OutlinedButton
            loading={loading && provider === authProviders.google.name}
            buttonStyle={{
              gap: 10,
              paddingVertical: verticalScale(10),
              flex: 1,
            }}
            onPress={() => {
              onSocialAuth(authProviders.google);
            }}
            icon={<GoogleIcon />}>
            Google
         </OutlinedButton>}
    //other login methods

        </View>
  );
};

Method to handle login

export const LoginAs = async (connection: string) => {
  const auth0RedirectUri =
    Platform.OS === "ios"
      ? `${process.env.EXPO_PUBLIC_AUTH0_REDIRECT_URI}/ios/com.hello.myapp/callback`
      : `${process.env.EXPO_PUBLIC_AUTH0_REDIRECT_URI}/android/com.hello.myapp/callback`;
  const auth0Domain = process.env.EXPO_PUBLIC_AUTH0_AUTH_DOMAIN; //env
  const clientId = process.env.EXPO_PUBLIC_AUTH0_CLIENT_ID; //production
  const audience = process.env.EXPO_PUBLIC_AUTH0_AUTH_AUDIENCE;
  const url =
    `${auth0Domain}/authorize?` +
    `response_type=token&` +
    `client_id=${clientId}&` +
    `redirect_uri=${encodeURIComponent(auth0RedirectUri)}&` +
    `scope=openid%20profile%20email&` +
    `audience=${encodeURIComponent(audience || "")}&` +
    `connection=${connection}`;
  const browserOptions = {
    preferEphemeralSession: false,
    createTask: true,
  };
  const result = await WebBrowser.openAuthSessionAsync(
    url,
    auth0RedirectUri,
    browserOptions
  );
  return result;
};

with the above code I get the token but no refresh token.

 {"error": null, "type": "success", "url": "com.hello.myapp://hello.us.auth0.com/ios/com.hello.myapp/callback#access_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImFNT05hNEdCd2xQRjFCaURwQWxJRSJ9.eyJpc3MiOiJodHRwczovL2Z1bmRpc3VhdC51cy5hdXRoMC5jb20vIiwic3ViIjoiZ29vZ2xlLW9hdXRoMnwxMDM3NTU4NjgxNjg2Nzk4NDkwNzMiLCJhdWQiOlsiaHR0cHM6Ly91YXR2Mi1hcGkuZnVuZGlzLmZpLyIsImh0dHBzOi8vZnVuZGlzdWF0LnVzLmF1dGgwLmNvbS91c2VyaW5mbyJdLCJpYXQiOjE3Mzc5NjMxMDYsImV4cCI6MTczNzk3MDMwNiwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBlbWFpbCIsImF6cCI6IkhQSnl4eUk2ak9CZnVzYmVPZTZXY3pNRk5Cb0xra25YIn0.BqXY4zO6e6PdE_rWs4WLLf3E8pIPTlRnD2eXn4Sp1Fw5y8-nbaq6brC9YK3Be9fE2OpCszn3oRNY1q-RuWDuYP_3vcRF_rGOFbZH_oTNMms9AosnFkG_1C7GN4PFfl7pLhkWBU5Uwy_fhyU1LcI8HoBE8vsXOCSWG4KsUQhCyTBQF_hbdw52px3hdWcuyJfXYBpxIb1D9-tnk3WVZ3SyIP47aUwKGeGxqxt4FHAFLgZOxyCaGDfSXthhYZSHXYmMHnlrveQsVFz216zEkKvVjJ4NKOjEixh_3mHbhgVhPVbblzZw9LJFwjP5DaZxLHGc_oqE7yZLRRDeef7XZh3YFQ&scope=openid%20profile%20email&expires_in=7200&token_type=Bearer"}

I've also enable offline access in my Auth0 api settings.

I've also tried passing offline_access in scope but no success on getting refresh token.

Output of console.log of response when offline_access is added in scope:

{
    "url": "com.h3llo.fundisapp://hello.us.auth0.com/android/com.hello.myapp/callback#access_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImFNT05hNEdCd2xQRjFCaURwQWxJRSJ9.eyJpc3MiOiJodHRwczovL2Z1bmRpc3VhdC51cy5hdXRoMC5jb20vIiwic3ViIjoiZ29vZ2xlLW9hdXRoMnwxMTQzMDY4NDQ4MzIxNTkxODA3MjYiLCJhdWQiOlsiaHR0cHM6Ly91YXR2Mi1hcGkuZnVuZGlzLmZpLyIsImh0dHBzOi8vZnVuZGlzdWF0LnVzLmF1dGgwLmNvbS91c2VyaW5mbyJdLCJpYXQiOjE3MzgxMjQ5MjYsImV4cCI6MTczODEzMjEyNiwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBlbWFpbCBvZmZsaW5lX2FjY2VzcyIsImF6cCI6IkhQSnl4eUk2ak9CZnVzYmVPZTZXY3pNRk5Cb0xra25YIn0.VHj5hKbFvwhIyMejYA0_Cab01jlMRPg2G5RVRH8lMxkNYugrUmBDqa8JKOjjGxth4waMci6pB_c456XriPKj44Eh2UqjLgcXtVlL3txwCDWNQ-2JY9z74JAnZksGX4gtuG9vKhKR-T80vhQtVnZ3i3YASSuj3h2RF9Rf3Gvm1vRg3QE1lrErZ93HQso_CgLD6lp__XwRewAN_TQJpSVI7VNPOUizpIoKHhkCIl_ki9bHl-uvF63fwXF4X1uMAZT7umqoCgly4B0_04ZBamzyLiMH6pI6qxcFh81BKupDyHuUs7mCbIYHLPQ-bxmz_gabmu_SxhRGk_RzYj_iBkMOkQ&scope=openid%20profile%20email%20offline_access&expires_in=7200&token_type=Bearer",
    "type": "success"
}

I'm using machine to machine's client ID in mobile app, since i'm not using auth0 sdk for react native.


Solution

  • export const LoginAs = async (connection: string) => {
      const auth0RedirectUri =
        Platform.OS === "ios"
          ? `${process.env.EXPO_PUBLIC_AUTH0_REDIRECT_URI}/ios/com.hello.myapp/callback`
          : `${process.env.EXPO_PUBLIC_AUTH0_REDIRECT_URI}/android/com.hello.myapp/callback`;
      const auth0Domain = process.env.EXPO_PUBLIC_AUTH0_AUTH_DOMAIN; //env
      const clientId = process.env.EXPO_PUBLIC_AUTH0_CLIENT_ID; //production
      const audience = process.env.EXPO_PUBLIC_AUTH0_AUTH_AUDIENCE;
      const state = Math.random().toString(36).substring(7);
      const url =
        `${auth0Domain}/authorize?` +
        `response_type=code&`+
        `client_id=${clientId}&` +
        `redirect_uri=${encodeURIComponent(auth0RedirectUri)}&` +
         `scope=${encodeURIComponent('openid profile email offline_access')}&`+
        `audience=${encodeURIComponent(audience || "")}&` +
        `state=${state}&` +
        `connection=${connection}`;
      const browserOptions = {
        preferEphemeralSession: false,
        createTask: true,
      };
      const result = await WebBrowser.openAuthSessionAsync(
        url,
        auth0RedirectUri,
        browserOptions
      );
    
      if (result.type === 'success') {
        const urlParams = new URL(result.url).searchParams;
        const code = urlParams.get('code');
        
        if (!code) {
          throw new Error('No code received from Auth0');
        }
        // Exchange the code for tokens
        const tokenResponse = await fetch(`${auth0Domain}/oauth/token`, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            grant_type: 'authorization_code',
            client_id: clientId,
            client_secret: process.env.EXPO_PUBLIC_AUTH0_CLIENT_SECRET,
            code:  code,
            redirect_uri: auth0RedirectUri,
          }),
        });
    
        const tokens = await tokenResponse.json();
        console.log("Tokens:", tokens); // Includes access_token, id_token, refresh_token
        return tokens;
      }
      return {result: "false"};
    };
    

    I managed to get the refresh token with the above approach. I had to get code first instead of token then use the code to get the tokens