OAuth 2.0 JWT Bearer Flow for Server-to-Server Integration

In a server-to-server integration, there is no need for an user to interactively log in to provide authorization.  In this case OAuth 2.0 JSON Web Token (JWT) bearer flow can provide the authorization.

Communities support all available authentication flows, except for the username-password OAuth authentication flow and the SAML assertion flow (https://help.salesforce.com/articleView?id=networks_auth_configure_oauth.htm&type=0)

Hence , we can use a flow like web server or user agent flow or use JWT flow. (https://help.salesforce.com/articleView?id=remoteaccess_oauth_jwt_flow.htm&type=0)

When we talk about JSON Web Token, it is consist of 3 parts

  1. Headers – Which contains the algorithm which will be used to sign the request

  2. Payload – The actual data and information about the users

  3. Signature – Signature consists of 3 parts and the structure is given below.

 

Payload consist of 4 parts

  • issuer "iss", this is the consumer key for your connected app

  • audience "aud", this is always https://login.salesforce.com for production, or https://test.salesforce.com for sandboxes (or whatever your Salesforce community url is, if you have one)

  • subject "sub", this is the username (user@yourcompany.tld) of the user you want to execute requests as

  • expiration "exp", this is the expiration time of the JWT itself, and provides a way to tolerate differences in client & server time. This is the unix timestamp (seconds or milliseconds since unix epoch) + a little more time to allow for the JWT to make it to Salesforce. Really, any timestamp 1 minute or more in the future should work fine here. It has nothing to do with how long the access token is valid for

 

We are going to do this from within Salesforce and in this case we can use Salesforce JWT class (https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_class_Auth_JWT.htm#apex_class_Auth_JWT).

We need to specify aud, iss, sub , exp and rest of thing will take care by the JWT Auth class.


Auth.JWT jwt = new Auth.JWT();
jwt.setAud(aud);
jwt.setIss(iss);
jwt.setSub(sub);
// Expiration time of the JWT itself
// Best to make this a long instead of an int
//The validity must be the expiration time of the assertion within 3 minutes,
//expressed as the number of seconds from 1970-01-01T0:0:0Z measured in UTC.
Long exp = DateTime.now().addMinutes(5).getTime();
System.debug('>>>> ' + exp);
jwt.setValidityLength(Integer.valueof(exp));

// Storing the certificate in Salesforce is a requirement for using the JWS class
Auth.JWS jws = new Auth.JWS(jwt, 'Name of the Certificate');

String tokenEndpoint = 'https://bolt.salesforce.com/services/oauth2/token';
Auth.JWTBearerTokenExchange bearer = new Auth.JWTBearerTokenExchange(tokenEndpoint, jws);
String accessToken = bearer.getAccessToken();

System.debug(' Token ' + accessToken);

Steps to create a Self-Signed Certificate.

Step 1 : The self-signed certificate

Go to Setup | Security | Certificate and Key Management | Create Self-Signed Certificate, define a label, a unique name,check Exportable Private Key and click on save.








Then open the newly created certificate and click on Download Certificate to download it.







Step 2 : The Connected App

Go to Setup | Apps | App Manager | New Connected App and check Use digital signature to upload the certificated you downloaded at step 1.
















Fill all the other required information to create the connected app and don’t forget to allow community user profiles to access it and set the policy to Admin approved users are pre-authorized. Then copy the Consumer Secret, we’ll need it later.

Set Oauth scopes as needed.











Click on Manage to set the policy and select the profiles. At the end make sure API Enabled check box is checked on the profile.














Step 3 : The JWT token

The last step is about getting an access token that authenticate the connected community/standard user. To do so, we’ll use the OAuth 2.0 JWT Bearer Token Flow. JWT flow is a server to server authentication flow. Use the private key to sign the json and post the JWT to the OAuth token endpoint, which in turn processes the JWT and issues an access_token (but no refresh_token).

We can use a query the username based on the user id otherwise we can store username in config/metedata to retrieve it. Then use the method below to get an access token. You’ll need to update siteBase and consumerKey parameters.

Sample code -


public class B2BeCommerce_JWT {

    public static String getAccessToken(String username){
        String siteBase = 'https://***.force.com';
        String consumerKey = '3MV..***..0Ln8ievjdl';
        Auth.JWT jwt = new Auth.JWT();
        jwt.setSub(username);
        jwt.setIss(consumerKey);
        jwt.setAud(siteBase);
        Auth.JWS jws = new Auth.JWS(jwt, 'B2BeCommerceCert');
        String tokenEndpoint = siteBase+'/services/oauth2/token';
        Auth.JWTBearerTokenExchange bearer = new
        Auth.JWTBearerTokenExchange(tokenEndpoint, jws);
        String accessToken = bearer.getAccessToken();
        return accessToken;
    }

}

As the logged-in community user, we can now perform apex DML actions (and not simply as the integration user).

Comments

Popular posts from this blog

Introduction to Salesforce Agent Script: Build Predictable AI Agents

Anti-Patterns in Salesforce Agentforce: What to Avoid When Building AI Agents

MCP vs. Traditional APIs: Choosing the Right Integration Approach