thebergamo / react-native-fbsdk-next

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Access Token is invalid

SamLafte opened this issue · comments

Hi all, I can successfully get the access token from the code below:

const handleFacebookSignIn = async () => {
    const result = await FacebookLoginManager.logInWithPermissions([
      "public_profile",
      "email",
    ]);
    if (result.isCancelled) {
      return;
    }
    const loginResult = await AccessToken.getCurrentAccessToken();
  }

and I can also get the user's data including their email address with the code below:

  const getData = () => {
    const inforRequest = new GraphRequest("/me", null, (error, result) => {
      console.log(error || result);
    });
    new GraphRequestManager().addRequest(inforRequest).start();
  }

The problem is I need to verify the access token in my server to verify and extract user's email out of it, whenever I try to verify the accessToken by sending it to https://graph.facebook.com/me?fields=email&access_token=${token} I receive the below error:

{"error":{"message":"Invalid OAuth access token data.","type":"OAuthException","code":190,"fbtrace_id":"ACyLy8i7Jqqk3XK87GDKCF6"}}

When I debug the access token I see the below:
Screenshot 2024-04-21 at 7 47 31 PM

Am I missing something or it's some kind of a bug?

  • expo ~48.0.15
  • react-native-fbsdk-next ^13.0.0

I believe this is related to this: #514 and this: #520 (comment)

this is an upstream change with version 17 of the Facebook iOS SDK: https://github.com/facebook/facebook-ios-sdk/issues/2375 and it sounds like it is a deliberate behavior change by Facebook, if you do not have tracking permissions you automatically get opted into limited login.
So you have a few options:

downgrade (harder with privacy manifest requirements)
get tracking permissions to use classic login
support limited login
ask for a change on the iOS sdk on the above issue
either way I don't think there is anything that can be done on this library.

One consequence of being opted into limited login is that the accessToken that is returned for limited login no longer gives access to graph api and the data (I believe) is instead included in the accessToken. Sorry to say nothing to be done on this sdk, if you have issues with this please take them to this issue in Facebook sdk: facebook/facebook-ios-sdk#2384

@brainbicycle yeah that's correct, so the only option left for ios server-side token verification is AuthenticationToken.getAuthenticationTokenIOS()
Even the related facebook docs are not clear on how to verify this and I couldn't find any example to follow

I had similar issue, and ended-up sending this token to api and verify using jwks. Sample python code below

def facebook_login_with_jwt(jwt_token):
    try:
        jwks_url = 'https://www.facebook.com/.well-known/oauth/openid/jwks'
        jwks_client = jwt.PyJWKClient(jwks_url)
        header = jwt.get_unverified_header(jwt_token)
        public_key = jwks_client.get_signing_key(header["kid"]).key
        return jwt.decode(jwt_token, key=public_key, algorithms=["RS256"], audience="YOUR_FACEBOOK_CLIENT_ID")
    except jwt.ExpiredSignatureError:
        # Handle expired token
        raise AssertionError("Expired token. Please try again.")
    except jwt.InvalidTokenError:
        # Handle invalid token
        raise AssertionError("Invalid facebook login")
    except Exception as e:
        # Handle other exceptions
       raise Exception(f"Error: {str(e)}")

thank you so much @vyshakh I could successfully do the same thing for js and I'll paste it here for reference

import jwt from 'jsonwebtoken';
import axios from 'axios';
import jwkToPem from 'jwk-to-pem';

export const validateFacebookJwtTokenAndExtractEmail = async (
  jwtToken: string,
  expectedNonce: string,
  iosAppId: string,
) => {
  try {
    const decodedToken = jwt.decode(jwtToken, { complete: true }) as jwt.Jwt;
    // @ts-ignore
    const nonce = decodedToken.payload.nonce;
    // @ts-ignore
    const issuer = decodedToken.payload.iss;
    // @ts-ignore
    const audience = decodedToken.payload.aud;

    if (audience !== iosAppId) {
      console.log('ios app id is not valid');
    } else if (nonce !== expectedNonce) {
      console.log('ios authorisation token nonce is not valid');
    } else if (issuer !== 'https://www.facebook.com') {
      console.log('ios authorisation token issuer is not facebook');
    } else {
      const response = await axios.get(
        'https://limited.facebook.com/.well-known/oauth/openid/jwks/',
      );

      const jwks = response.data;
      const publicKey = jwks.keys.find(
        (key: any) => key.kid === decodedToken.header.kid,
      );

      const result = jwt.verify(jwtToken, jwkToPem(publicKey));

      // @ts-ignore
      return result.email;
    }
  } catch (error: any) {
    console.log('could not verify the ios authorisation token', error.message);
  }
};

@SamLafte thank you for the implementation of the Authorization Token validation 👍
I've noticed the Authorization Token has way more short expiration period than Access Token: has you found any way to re-new the Authorization Token to keep user requests authorized longer?

commented

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.