facebook / facebook-ios-sdk

Used to integrate the Facebook Platform with your iOS & tvOS apps.
https://developers.facebook.com/docs/ios
Other
7.73k stars 3.5k forks source link

The accessToken generated by SDK 17.0.0 is Invalid: `Invalid OAuth access token - Cannot parse access token ` #2365

Open alwayswith opened 1 month ago

alwayswith commented 1 month ago

Checklist before submitting a bug report

Xcode version

15.2

Facebook iOS SDK version

17.0.0

Dependency Manager

SPM

SDK Framework

Login

Goals

I had upgrade the sdk to 17.0.0, but access token generated by the sdk is invalid. I used the Graph API Explorer to debug the access token, It reports error. When I downgrade the sdk verstion to 16.3.1, the access token is valid.

Expected results

{ "id": "123456", "name": "Zac" }

Actual results

{ "error": { "message": "Invalid OAuth access token - Cannot parse access token", "type": "OAuthException", "code": 190, "fbtrace_id": "A5WdWSSFlOkAu4p8C-qJGqR" } }

Steps to reproduce

No response

Code samples & details

// get the access token
  let loginButton = FBLoginButton()
        loginButton.center = view.center
        view.addSubview(loginButton)

        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            if let token = AccessToken.current,
                !token.isExpired {
                print(token.tokenString)
            }
            // your code here
        }
y2ducky commented 1 month ago

I've encountered the same issue (I'm using Cocoapods). When using version 16.3.1, there are no errors, but upon upgrading to 17.0.0, I receive the "Invalid OAuth access token - Cannot parse access token" error when verifying the token in my server. In my testing, I've noticed that the tokenString value in version 17.0.0 is one character shorter than when testing with version 16.3.1. I'm not certain if this is related to the problem, but I thought it worth sharing.

KovtunOleg commented 1 month ago

Debug-Token api will also fail with the same error for access tokens generated with iOS v17.0.0.

jiwooong commented 1 month ago

I've encountered the same issue. when using v16.3.1, there are no errors, but after update v17.0.0 I receive the same error. "Invalid OAuth access token - Cannot parse access token"

HaejungAhn commented 1 month ago

same here🥲

goleoh commented 1 month ago

Same. But it happened only at the M2 machines like a macbook pro. And I didn't use the limited login, but the browser opens for limited login. The url is like "limited.facebook.com". At the old facebook sdk, the opened url is like "m.facebook.com/".

And the returned token doesn't begin with "GG". Our app is a game, and the access tokens begin with "GG" usually.

cxxer commented 1 month ago

Same. But it happened only at the M2 machines like a macbook pro. And I didn't use the limited login, but the browser opens for limited login. The url is like "limited.facebook.com". At the old facebook sdk, the opened url is like "m.facebook.com/".

And the returned token doesn't begin with "GG". Our app is a game, and the access tokens begin with "GG" usually.

same. And there is no button that can be used to jump to the FB client.

deepmode commented 1 month ago

These are what I discover when using version 17.0.0 SDK with iOS 17+ With AppTracking Transparency enable for your App -> receive a valid FB AccessToken With AppTracking Transparency disable for your App -> receive an invalid FB AccessToken

zimssa-mkkim commented 1 month ago

These are what I discover when using version 17.0.0 SDK with iOS 17+ With AppTracking Transparency enable for your App -> receive a valid FB AccessToken With AppTracking Transparency disable for your App -> receive an invalid FB AccessToken

we have the exact same issue

goleoh commented 1 month ago

I resolved this issue. The FB SDK 17.0 uses a limited login forcefully in ios 17 when the ATT is not on. So we should impletemt limited logins. In the limited login, you cannot use the access token at the graph api in your web server. You can use the authentication token which is a JWT. You can verify it and extract user information from it. So you should handle the both tokens - normal access token on the ATT enabled and authentication token on the ATT not enabled. Or you can use the limited logins only and use authentication tokens.

gonsee commented 1 month ago

I have just found this announcement. https://developers.facebook.com/blog/post/2024/03/28/changes-made-to-fb-login-sdk/

AGulev commented 1 month ago

I have just found this announcement. https://developers.facebook.com/blog/post/2024/03/28/changes-made-to-fb-login-sdk/

We got the same problem, so it's by design (not a bug)? If so, this

I resolved this issue. The FB SDK 17.0 uses a limited login forcefully in ios 17 when the ATT is not on. So we should impletemt limited logins. In the limited login, you cannot use the access token at the graph api in your web server. You can use the authentication token which is a JWT. You can verify it and extract user information from it. So you should handle the both tokens - normal access token on the ATT enabled and authentication token on the ATT not enabled. Or you can use the limited logins only and use authentication tokens.

isn't a workaround but a proper solution for new SDK?

beerana-meta commented 1 month ago

Hello - We recently made changes to Facebook Login SDK for iOS. As a result of these changes, we recommend that you use Limited Login. If you are unable to implement Limited Login, the Graph API, iOS would need to be used to support these permissions. See more details here.

jjw0722 commented 1 month ago

Hello - We recently made changes to Facebook Login SDK for iOS. As a result of these changes, we recommend that you use Limited Login. If you are unable to implement Limited Login, the Graph API, iOS would need to be used to support these permissions. See more details here.

Hello. then how to solve our problem? We validate access token using our web server after facebook login. but now our app can't validate JWT iOS 17 device because we don't integrate limited login yet.

So you mean after all, in a system like us that verifies tokens through a web server, you can't do it the way it is, but you have to integrate limited logins or manually adjust the graph API?

Additionally, if you look at the official Facebook guide document, there is an article below, and even if you use that method, it returns an invalid access token. Why is that?

"A graph request will fail because there is no access token. To get an access token, either reuse the classic login method (defaults tracking to enabled), or call FBSDKLoginManager logInFromViewController:configuration:completion: with a configuration that specifies that tracking is enabled. Be aware that when you do this, users are tracked."

ByBogon commented 1 month ago

Hello - We recently made changes to Facebook Login SDK for iOS. As a result of these changes, we recommend that you use Limited Login. If you are unable to implement Limited Login, the Graph API, iOS would need to be used to support these permissions. See more details here.

Hello. then how to solve our problem? We validate access token using our web server after facebook login. but now our app can't validate JWT iOS 17 device because we don't integrate limited login yet.

So you mean after all, in a system like us that verifies tokens through a web server, you can't do it the way it is, but you have to integrate limited logins or manually adjust the graph API?

Additionally, if you look at the official Facebook guide document, there is an article below, and even if you use that method, it returns an invalid access token. Why is that?

"A graph request will fail because there is no access token. To get an access token, either reuse the classic login method (defaults tracking to enabled), or call FBSDKLoginManager logInFromViewController:configuration:completion: with a configuration that specifies that tracking is enabled. Be aware that when you do this, users are tracked."

which means web server can't validate JWT IOS 17 device through Facebook api?

Is there some way for web server to validate JWT IOS 17 device through Facebook api? OR should web server just decode and verify JWT IOS 17 device (authentication token) by this doc?

jjw0722 commented 1 month ago

Hello - We recently made changes to Facebook Login SDK for iOS. As a result of these changes, we recommend that you use Limited Login. If you are unable to implement Limited Login, the Graph API, iOS would need to be used to support these permissions. See more details here.

Hello. then how to solve our problem? We validate access token using our web server after facebook login. but now our app can't validate JWT iOS 17 device because we don't integrate limited login yet. So you mean after all, in a system like us that verifies tokens through a web server, you can't do it the way it is, but you have to integrate limited logins or manually adjust the graph API? Additionally, if you look at the official Facebook guide document, there is an article below, and even if you use that method, it returns an invalid access token. Why is that? "A graph request will fail because there is no access token. To get an access token, either reuse the classic login method (defaults tracking to enabled), or call FBSDKLoginManager logInFromViewController:configuration:completion: with a configuration that specifies that tracking is enabled. Be aware that when you do this, users are tracked."

which means web server can't validate JWT IOS 17 device through Facebook api?

Is there some way for web server to validate JWT IOS 17 device through Facebook api? OR should web server just decode and verify JWT IOS 17 device (authentication token) by this doc?

How to verify the Authentication Token on the web server is shown in the official guide document. What I said above is that the official guide document says that you can get an access token by setting the loginFromViewController API to tracking enabled as above, but even if you do that, you will return the invalid token and ask why.

You can get a JWT-type token from our web server and verify it, but I thought it could be solved by modifying the API without adding additional logic.

ByBogon commented 1 month ago

@jjw0722

Yep I got your point. Just wondered whether I can use graph api to verify JWT, which is impossible.

So just to triple check, when web server validates JWT, gotta follow what the doc says, right?

davehpcnt commented 1 month ago

Facebook JWT tokens are not cached when the app is closed. Is there a way to get the token without displaying the limited login screen on Facebook? FBSDKLoginKit.AccessToken.current?.tokenString -> nil?

Yehsam23 commented 1 month ago

If ATT is not agreed upon, limited access will be imposed. Is there any way to retrieve the ID for business through JWT Token at this time?

arkku commented 1 month ago

Let's say I already have a nonce, how do I exchange it for a JWT token instead of the (broken) access_token?

zimssa-mkkim commented 1 month ago

When it's a limited login you can't use access token because graph api will not work with it. Instead, you can use FBSDK AuthenticationToken.currentAuthenticationToken and verify it with JWK verifiers.

leonle69 commented 4 weeks ago

It's been over 3 weeks now and it hasn't been resolved. I am quite stressed about this issue. any idea how to resolve it?

rgg-egunnery commented 3 weeks ago

Has anyone gotten limited login to work with non tester accounts? Works fine for our test accounts but when trying to use it with public accounts, the new loginManager.logIn(configuration: configuration) just returns a cancellation. Not using anything that needs the app to be approved

kevin-zqw commented 3 weeks ago

When iOS ATT is disabled, even if I log in using the regular mode, the Facebook SDK will fall back to limited mode and return an invalid token.

Do you know how to determine if it's a limited login from the FBSDKLoginManagerLoginResult return result?

mvpscottjon commented 3 weeks ago

image

There two token in it

If you use limit login, you only can get AuthenticationToken(Sorry I change to this. I said accessToken was wrong)

image

If you use enable tracking to login, you can get AuthenticationToken and accessToken(no matter ATT is enable or not...a little weird...) image

rgg-egunnery commented 3 weeks ago

@mvpscottjon have you tried your .limited config with public accounts or just test accounts?

mvpscottjon commented 3 weeks ago

@mvpscottjon have you tried your .limited config with public accounts or just test accounts?

Yes, I also test using public accounts.

But I change my comment, is it right for you?

alexwind-lin commented 3 weeks ago

@mvpscottjon have you tried your .limited config with public accounts or just test accounts?

Yes, I also test using public accounts.

But I change my comment, is it right for you?

i can only get a cancelled result when using .limited on public accounts

loving-irene commented 3 weeks ago

image

There two token in it

  • one is accessToken which needs graphAPI to get user info
  • another one is AuthenticationToken(aka JWT token) which needs JWT decode to get user info

If you use limit login, you only can get AuthenticationToken(Sorry I change to this. I said accessToken was wrong)

image

If you use enable tracking to login, you can get AuthenticationToken and accessToken(no matter ATT is enable or not...a little weird...) image

thanks,it's ok for me. test account is ok for limit config,and normal account also ok. but there is a strange question,one account isn't work you can try multiple account(test,normal,develop) if you have same question

KeatoonMask commented 3 weeks ago

I started using Limited Login as meta recommends.

After a successful login, the global AuthenticationToken instance is properly populated.

Later, I get the token of the auth response from the AuthenticationToken.current?.tokenString.

Everything looks good. When decoding such token, the info is correct but it has an invalid signature.

{
"error": {
"message": "Bad signature",
"type": "OAuthException",
"code": 190,
"fbtrace_id": "ArYahP8jTA10WI-HkYMWrno"
}
}

Any idea of what I'm missing?

vyshakh commented 3 weeks ago

I had similar issue, I tried to verify the jwt token using jwt and matching public key from https://www.facebook.com/.well-known/oauth/openid/jwks/ and it shows signature is valid. use the full json object as public key. { "kid": "ec11d50341c08e82899650e6afcc6668f2a0a420", // this should match with your jwt header kid "kty": "RSA", "alg": "RS256", ... } @KeatoonMask

loving-irene commented 3 weeks ago

I started using Limited Login as meta recommends.

After a successful login, the global AuthenticationToken instance is properly populated.

Later, I get the token of the auth response from the AuthenticationToken.current?.tokenString.

Everything looks good. When decoding such token, the info is correct but it has an invalid signature.

{
"error": {
"message": "Bad signature",
"type": "OAuthException",
"code": 190,
"fbtrace_id": "ArYahP8jTA10WI-HkYMWrno"
}
}

Any idea of what I'm missing?

you can not use graph api to get user info,backgroud also need implement limit login,just decode JWT,you can get user info,doc here https://developers.facebook.com/docs/facebook-login/limited-login/token/validating

vyshakh commented 3 weeks ago

Python sample code to verify the token

import jwt
from jwt.algorithms import RSAAlgorithm

IDjwt = 'jwt client token'
# header = jwt.get_unverified_header(IDjwt)
# print(header) # Find matching RSA public key json using header kid .  https://www.facebook.com/.well-known/oauth/openid/jwks/
key_json = {"kid": "ec11d50341c08e82899650e6afcc6668f2a0a420", .....}'
public_key = RSAAlgorithm.from_jwk(key_json)
decoded = jwt.decode(IDjwt, public_key, algorithms='RS256', audience="YOUR_FACEBOOK_CLIENT_ID") # handle the exceptions
print(decoded) # this will display all required details like profile, email etc...
OksanaFedorchuk commented 3 weeks ago

@vyshakh

{ "error": { "message": "Bad signature", "type": "OAuthException", "code": 190, "fbtrace_id": "ArYahP8jTA10WI-HkYMWrno" } }

If you allow .tracking in the method initialising login configuration, but still receive this error, the problem may be that the tracking was not allowed on the level of application (native popup asking for tracking permission), and limited login is being used. Try uninstalling the application, install again and then allow for tracking in this popup. Then, when using apple login method with allowed tracking, user will be directed on regular login (not limited), and you will receive correct access token.

greymag commented 3 weeks ago

I'm trying to implement v17.0 support, but I'm running into 2 issues:

1) How do I determine if it was a Limited or Standard Login, in a situation when Facebook automatically falls back to Limited (as described by @kevin-zqw here)? In both cases I have AccessToken and AuthenticationToken, but the AccessToken in the case of Limited Login is invalid when verified as written above.

2) AuthenticationToken has an expiration date on 1 hour. If I open the app after that, I still get a non-nil AuthenticationToken.current but claims is now nil, because the token was expired and didn't refresh automatically. How can I refresh this token? Or is there no way for me to do this?

Does anyone know how to handle these problems?

arthurhammer commented 3 weeks ago

@greymag

  1. AuthenticationToken has an expiration date on 1 hour. If I open the app after that, I still get a non-nil AuthenticationToken.current but claims is now nil, because the token was expired and didn't refresh automatically. How can I refresh this token? Or is there no way for me to do this?

The only way to obtain a new authentication token is to authenticate the user again by presenting the Facebook Login screen.

About: 1. Good question. I agree there should be an API for this. As a workaround, you might have to track if App Tracking Transparency is enabled manually. Or try to use the access token to make requests and if it fails use the authentication token.

greymag commented 3 weeks ago

The only way to obtain a new authentication token is to authenticate the user again by presenting the Facebook Login screen.

That's what I thought. Thanks for the answer.

About: 1. Good question. I agree there should be an API for this. As a workaround, you might have to track if App Tracking Transparency is enabled manually. Or try to use the access token to make requests and if it fails use the authentication token.

Sure, we can do that, but such things are very bad practice, and quite unreliable (will break if the internal SDK logic changes). I wonder if there is a better way?

Ariandr commented 3 weeks ago

Hi @beerana-meta

So, using SDK 17.x there is no way to return to the robust behavior of SDK 16.x with a non-limited login? Switching to SDK 17.x requires you to start looking for workarounds trying to determine if the login was limited or not?

Now in SDK 17.x even with tracking: .enabled provided in Configuration it will return invalid AccessToken which cannot be used by the server, because it's invalid?

So the only way to not break everything and not to rewrite server-side code is to use older versions of the SDK?

yishair-investing commented 3 weeks ago

I had similar issue, I tried to verify the jwt token using jwt and matching public key from https://www.facebook.com/.well-known/oauth/openid/jwks/ and it shows signature is valid. use the full json object as public key. { "kid": "ec11d50341c08e82899650e6afcc6668f2a0a420", // this should match with your jwt header kid "kty": "RSA", "alg": "RS256", ... } @KeatoonMask

@vyshakh @KeatoonMask We have a similar issue, we are generating the JWT with limited login but when pasting into JWT.io with the correct JWKS it still shows as invalid signature. The 'kid' matches. Anyone else have this issue, or know how to solve it?

vyshakh commented 3 weeks ago

@yishair-investing I missed audience in the previous sample that I shared. I've tested below code and worked

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)}")
yishair-investing commented 3 weeks ago

@vyshakh thanks, I already have audience in the payload. I can't even get my JWT to verify online using a debugger. :(

gongmingqm10 commented 2 weeks ago

We also had the same issue and share here how we solve this issue to authenticate the mobile App generated facebook JWT Token.

Authentication Flow

In our authentication flow, we have our IOS App call the Facebook SDK generate the JWT token string via the code below code:

let tokenString = AuthenticationToken.current?.tokenString

As mentioned in the official document: https://developers.facebook.com/docs/facebook-login/limited-login/ios

The fact of the above tokenString is a JWT token format. And our mobile App will pass the generated token to our backend server via API. Our server will need to validate the token, and also extract the social ID + user profile information based on the token

So the problem comes to after the Facebook IOS SDK token migrated to the latest way, how our backend server should authenticate the JWTToken and extract the relevant profiles

Understand JWT token info

JWT token itself is a base64 encoded string. It's a must to have to validate if the JWT token is really generated from our IOS App. If you use the jwt.io to parse the generated Facebook JWTToken. The content will mostly be like below:

1714113343593

To note for some key information here:

Retrieve the Public Key first

kid: Defines the public key ID. We need to use Facebook's public key to validate the token itself. Public keys can be obtained from the API https://limited.facebook.com/.well-known/oauth/openid/jwks/. And you need to use the kid to get the correct key from the above API response.

Attach the GET response of the above JWKS API below:

{
   "keys": [
      {
         "kid": "ec11d50341c08e82899650e6afcc6668f2a0a420",
         "kty": "RSA",
         "alg": "RS256",
         "use": "sig",
         "n": "-rJ0HvlxiqOcwfpP6LsAYo0aaGNmohEBFr1JuWCGVvnPb3Z5Akd5w_bxQMRlOMot15IyrhWFonWCFr9H02f9E9GOEaroAj0zxQnCXcuGWb1BFN6RfoGNFpee1MqSDV3ikSIsSI3JL-z_1uBtDsQ1AtbYMKsB572v64bapW4WjDjekz0pQ-ePizVWm9mNNQxkA_fh3p1hW3KssXgnasWbKJODT5I6hnzd4whxj22oLE8xJCYTFeouk86teKI-nvK-LmaxoetBhnDn3QS5pN_oiIfDqjKPXGazeG2qwGAE8VPeISPvzYIstGbEh3NCzFEoB7a7APF1nLEo7Lco9aWjYQ",
         "e": "AQAB"
      },
      {
         "kid": "27f14bf9e3e9d1c7ba691c9ee91a5fd92eb3d40c",
         "kty": "RSA",
         "alg": "RS256",
         "use": "sig",
         "n": "wPGO-HmGoGeygkxUDtAgQu5vZTElhbHrbNUhIae0dUwpTDHHYJKY6nskTYLN7Sqs4x3zXUJr6SilbBIUAfKRqhDkDPWXeas1oxZEqMaD2aWikTPh0BJCc-_YD8TSLoUnQAacEig7342yYAVQzgX-UxY4tSkeA06qbTD459xoeOEPnQEF6ODhxJGnakJYKqRUnsSDo4wJ65ISKx8ctNXtxXKcZP9rvNfc7InOaJyKRoD0UlzBHGU5NeFN6UJFi1FjgeCZYOS7VjR6fXmRezvWqK1NUZxno8vAayifYPqt-1lN59Vvzfv2nrusAX4r4V1v8c5jsu2pspcJRBVoFVRAjw",
         "e": "AQAB"
      }
   ]
}

Example of my JWT token, I should use the 1st public key above:

{
         "kid": "ec11d50341c08e82899650e6afcc6668f2a0a420",
         "kty": "RSA",
         "alg": "RS256",
         "use": "sig",
         "n": "-rJ0HvlxiqOcwfpP6LsAYo0aaGNmohEBFr1JuWCGVvnPb3Z5Akd5w_bxQMRlOMot15IyrhWFonWCFr9H02f9E9GOEaroAj0zxQnCXcuGWb1BFN6RfoGNFpee1MqSDV3ikSIsSI3JL-z_1uBtDsQ1AtbYMKsB572v64bapW4WjDjekz0pQ-ePizVWm9mNNQxkA_fh3p1hW3KssXgnasWbKJODT5I6hnzd4whxj22oLE8xJCYTFeouk86teKI-nvK-LmaxoetBhnDn3QS5pN_oiIfDqjKPXGazeG2qwGAE8VPeISPvzYIstGbEh3NCzFEoB7a7APF1nLEo7Lco9aWjYQ",
         "e": "AQAB"
}

Validate the JWT Token

Using the public key to validate your JWT token. Depends on your backend API language, you can use the security module to do the token validation based on the RSA algorithm. I attached a simple version of how I validate that in my Java API project:


import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.util.Base64Utils;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Nullable;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class FacebookJwtLoginShare {

    private static final String JWKS_URL = "https://limited.facebook.com/.well-known/oauth/openid/jwks/";

    private FacebookJwtLoginShare() {}

    public static JSONObject verify(String jwtToken, String facebookAppId) {
        String[] identityTokens = jwtToken.split("\\.");
        String header = new String(Base64Utils.decodeFromUrlSafeString(identityTokens[0]), StandardCharsets.UTF_8);
        String body = new String(Base64Utils.decodeFromUrlSafeString(identityTokens[1]), StandardCharsets.UTF_8);
        JSONObject headerData = new JSONObject(header);
        JSONObject bodyData = new JSONObject(body);

        String publicKeyId = (String) headerData.get("kid");
        return verify(jwtToken, facebookAppId, publicKeyId) ? bodyData : null;
    }

    private static boolean verify(String jwtToken, String appId, String kid) {
        try {
            PublicKey publicKey = getPublicKey(kid);
            if (publicKey == null) {
                return false;
            }
            JwtParser jwtParser = Jwts.parser().setSigningKey(publicKey);
            jwtParser.requireAudience(appId);
            Jws<Claims> claim = jwtParser.parseClaimsJws(jwtToken);
            if (claim != null) {
                return true;
            }
        } catch (Exception e) {
            log.warn("Facebook jwtToken parseClaims failed", e);
        }
        return false;
    }

    @Nullable
    private static PublicKey getPublicKey(String kid) {
        try {
            JSONObject object = getKidObject(kid);
            String n = object.getString("n");
            String e = object.getString("e");
            String keyType = object.getString("kty");
            BigInteger modulus = new BigInteger(1, Base64.decodeBase64(n));
            BigInteger publicExponent = new BigInteger(1, Base64.decodeBase64(e));
            RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, publicExponent);
            KeyFactory kf = KeyFactory.getInstance(keyType);
            return kf.generatePublic(spec);
        } catch (Exception e) {
            log.error("Facebook login get PublicKey error", e);
        }
        return null;
    }

    /**
     * @param kid KeyID extracted from the JWT header
     */
    private static JSONObject getKidObject(String kid) {
        RestTemplate restTemplate = new RestTemplate();
        String response = restTemplate.getForObject(JWKS_URL, String.class);
        String json = replaceBlank(response);
        JSONObject jsonObject = new JSONObject(json);
        JSONArray jsonArray = jsonObject.getJSONArray("keys");
        for (Object o : jsonArray) {
            JSONObject object = (JSONObject) o;
            if (object.get("kid").equals(kid)) {
                return object;
            }
        }
        return null;
    }

    public static String replaceBlank(String str) {
        String dest = "";
        if (str != null) {
            Pattern p = Pattern.compile("\\s*");
            Matcher m = p.matcher(str);
            dest = m.replaceAll("");
        }
        return dest;
    }
}

Finally I will call this function as FacebookJwtLoginShare.verify("facebook_jwt_token_from_your_app", "your_facebook_id")

Note: The below facebookAppId is one extra authentication you can decide to add on. This will check against make sure the jwtToken is generated from your own mobile Application.

Read Info from JWT Token

From the decoded JWT token, you can find the most user profile info inside. But note:

Use the Data

At last, paste part of our codebase how I retrieve the login user info based on the extracted JSONObject:

    private SocialUser facebookJwtLogin(String jwtToken) {
        JSONObject jwtPayload = FacebookJwtLoginTemplate.verify(jwtToken, facebookAppId);
        if (jwtPayload == null) {
            log.warn("Facebook social login jwt verify failed");
            return null;
        }
        FacebookUser facebookUser = new FacebookUser();
        String socialId = getSocialIdFromJwtPayload(jwtPayload);
        facebookUser.setId(socialId);
        facebookUser.setAvatar("https://graph.facebook.com/" + socialId + "/picture?type=large");
        if (jwtPayload.has("email")) {
            facebookUser.setEmail(jwtPayload.getString("email"));
        }
        if (jwtPayload.has("family_name")) {
            facebookUser.setLastName(jwtPayload.getString("family_name"));
        }
        if (jwtPayload.has("given_name")) {
            facebookUser.setFirstName(jwtPayload.getString("given_name"));
        }
        if (jwtPayload.has("name")) {
            facebookUser.setName(jwtPayload.getString("name"));
        }
        return facebookUser;
    }

Hope the above information works and solve your pain. If you're not familiar with how the backend API should process it. Suggest to involve one of your backend engineers to digest the above info.

ankitpunchh commented 2 weeks ago

There was no information in the release note that token will stop working if ATT is disabled. Can we use the older version and have the privacy file locally in the app ?

Ariandr commented 2 weeks ago

@ankitpunchh Yep, you can

ankitpunchh commented 2 weeks ago

Hi @Ariandr , Can you help me to understand a bit more. Is it ok to have it locally instead using from the SDK ? Do you have any link or document to support it ?

Ariandr commented 2 weeks ago

@ankitpunchh You can just create that manifest inside your app and list all the reasons the 3-d party SDK is supposed to list. https://apnspush.com/create-privacy-manifest

But if you already had the app deployed, it won't even be a cause for rejection. It's a cause for rejection when you add new SDKs, if I'm not mistaken.

FernandoReynoso commented 2 weeks ago

Facebook JWT tokens are not cached when the app is closed. Is there a way to get the token without displaying the limited login screen on Facebook? FBSDKLoginKit.AccessToken.current?.tokenString -> nil?

@davehpcnt Did you manage to solve this issue? We migrated to limited login and now we are getting login screen every time, even when permissions were already approved by the user. I don't think this is a good UX.

Nathan-Molby commented 1 week ago

@ankitpunchh You can just create that manifest inside your app and list all the reasons the 3-d party SDK is supposed to list. https://apnspush.com/create-privacy-manifest

But if you already had the app deployed, it won't even be a cause for rejection. It's a cause for rejection when you add new SDKs, if I'm not mistaken.

I hate to be the bearer of bad news, but this is not a good idea. The privacy manifest in Facebook version 17.0 describes the required reason APIs that Facebook uses in 17.0, not in 16.0. It is very likely that they have changed the required reason APIs that they use between 16.0 and 17.0, and as such, the privacy manifest would not be accurate.

Its unclear what the repercussions are for including an inaccurate privacy manifest, but you probably don't want to find out.

Ariandr commented 1 week ago

@Nathan-Molby I don't think there could be any repercussions, since privacy-nutrition labels were there for years and if you got something wrong there, it wasn't a problem. You could be asked to clarify something, but not more.

Ariandr commented 1 week ago

@Nathan-Molby Comes right from Apple. image

Nathan-Molby commented 1 week ago

@Ariandr also straight from Apple. Our apps should not be including the privacy manifest from the SDKs -- the SDKs should include that themselves.

Screenshot 2024-05-03 at 10 35 13 AM

Obviously I can't speak to what the repercussions would be for doing this, but I'm not going to risk it.