[SalesForce] JWT Bearer Token flow for Community: Invalid Token

I'm implementing a JWT Bearer Token within Salesforce (Apex) so my customers can passwordlessly login to a community. Not sure if it is a good practice, but I done some research and saw some use cases like this.

For the JWT Flow I am using the following code (followed this doc):

String token_endpoint = 'https://community-domain.cs101.force.com' + '/services/oauth2/token';
Auth.JWT jwt = new Auth.JWT();
jwt.setIss('3MVG9K...Hhe');
jwt.setSub('joe.doe@community-domain.com');
jwt.setAud('https://community-domain.cs101.force.com');

Auth.JWS jws = new Auth.JWS(jwt, 'Cert_Unique_Id');

Auth.JWTBearerTokenExchange bearer = new Auth.JWTBearerTokenExchange(token_endpoint, jws);
System.debug(bearer);

String accessToken = bearer.getAccessToken();

Considerations that may be imporatant:

  1. Joe Doe is a Customer User (Customer Community Plus License)

  2. Connected App has these scopes: api, web, full, refresh_token, offline_access

  3. Permitted users: Admin approved users are
    pre-authorized

  4. Working on a Sandbox

When I execute this code on anonymous console, I get:

Auth.JWTBearerTokenExchange.JWTBearerTokenExchangeException: Error 400: {"error":"invalid_grant","error_description":"authentication failure"}

And in the user login history I get "Status: Failed: Invalid Token".

I googled around a lot but I didn't find anyone having the exact same thing.

Has anyone gone through the same problem?

I tried using http://mydomain.my.salesforce.com as token_endpoint and http://test.salesforce.com as audience (on setAud). It works but it doesn't actually gives me access to community, only to Salesforce itself.

Best Answer

I ended up finding what was causing the error. In the doc that I mentioned in my question they were suggesting the use of https://community.force.com/customers as the token endpoint.

But in Configure Authentication Flows with OAuth (for Communities) doc they have it (kind of) better explained:

For example, to authenticate a user using an authorize URL like the following:

https://login.salesforce.com/services/oauth2/authorize?​response_type=token&client_id=your_app_id&redirect_uri=your_redirect_uri

Replace the login.salesforce.com host name with the full path to the community URL:

https://acme.force.com/customers/services/oauth2/authorize?​response_type=token&client_id=your_app_id&redirect_uri=your_redirect_uri

Some people may understand right away that full path means really the full path to the community, that in my case is https://community-domain.cs101.force.com/MyCustomerCommunity. As I was focusing on the example they are giving, I wasn't considering that customers in their case would be MyCustomerCommunity in mine.

Anyway, the solution was to replace customers by my community name, that is MyCustomerCommunity, turning token_endpoint and audience into

String token_endpoint = 'https://community-domain.cs101.force.com/MyCustomerCommunity' + '/services/oauth2/token';
// ...
jwt.setAud('https://community-domain.cs101.force.com/MyCustomerCommunity');

That way I was able to get an access token and even use it as session id (sid) in frontdoor.jsp in my community (what I also thought that wouldn't be possible as I didn't find it documented anywhere):

https://community-domain.cs101.force.com/MyCustomerCommunity/secur/frontdoor.jsp?sid=<token>

Hope it helps anyone in the future!

Related Topic