Salesforce OAuth 2.0 JWT Bearer flow

Salesforce OAuth 2.0 JWT Bearer flow

In this post we are going to learn how to implement the Salesforce OAuth 2.0 JWT Bearer flow in Salesforce. OAuth 2.0 JWT Bearer flow is used for server to server integration scenarios. This flow uses a certificate to sign the JWT request and doesn’t require explicit user interaction. However, this flow does require prior approval of the client app.

What is JWT Bearer flow?

Secure server-to-server integration without real time user involvement. Client specifies user in a JSON web token (JWT) or SAML format XML assertion and proves its own identity by appending a signature. JWT Bearer token flow is Ideal for application which access sfdc only through API as there is no UI involved. For example ETL tools or middleware.

This flow uses a certificate to sign the JWT request and doesn’t require explicit user interaction. However, this flow does require prior approval of the client app.

JWT Structure

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 {"alg":"RS256"}
  2. Payload – This contains claims information which is an object containing information about user and additional data. Claims are set using parameters- {"Iss,aud,sub,exp"}
  3. Signature – Signature consists of 3 parts and the structure is given below
<headerbase64encodedurl>.<claimsbase64encodedclaims>.<signature(uses algorithm like RS 256)>

JWT flow example in Salesforce

Lets start with JWT uses. In this example we will how we call another Salesforce org API using JWT flow in Salesforce.

Step 1) Creating private key and X509 certificate

First step using OAuth JWT Bearer Token flow is creating the Certificate. To create the certificate you need open SSL. Download it you are using window.

Step 1.1) Set OPENSSL_CONF path

set OPENSSL_CONF=C:\openssl\share\openssl.cnf 

Step 1.2) Generate an RSA private key

Execute below command and in output it will return your Server.pass.Key file. Server.Key is our private key.

openssl genrsa -des3 -passout pass:x -out server.pass.key 2048

Step 1.3) Create a key file from the server.pass.key file

Execute below command and in output it will return your Server.Key file. Server.Key is our RSA private key.

openssl rsa -passin pass:x -in server.pass.key -out server.key

Step 1.4) Request and generate the certificate

Below command will request for certificate.

openssl req -new -key server.key -out server.csr

Once you will execute above command it will ask you some question and information

  • Country Name : Provide any value for example : US
  • State or province Name [Some-State]: Georgia
  • Locality Name( eg. city)[] : Alpharetta
  • Organization Name (eg. company) : Apex Hours
  • Organization Unit Name : AH
  • Comman Name [] : You can keep it blank
  • Email Address [] : [email protected]

Step 1.5) Generate the SSL certificate

openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt

Step 2: Create JWT Connected App in Salesforce

Now you have certificate file (server.crt) with you. It time to create a connected app in Salesforce.

Step 2.1) Creating connected app in Salesforce

Go to Setup -> Search for connected app -> then click on New (connected app). Then provide below details.

  1. Fill Name and Email
  2. Click on “Enable OAuth Setting
  3. Call back URL “http://localhost:1717/OauthRedirect
  4. Click on “Use Digital Signature” and upload the certificate created in Step 1.5.
  5. Select the following OAuth Scopes
    1. Manage user data via APIs (api)
    2. Manage user data via Web browsers (web)
    3. Perform requests at any time (refresh_token, offline_access)
  6. Click on Save
Connected app for JWT FLOW.

Once you are done you will get Cliend_Id and Client_secret.

Step 2.1) Prior approval of the client app

There are different option to do that.

  • Option 1: Admin approves from connected app in Salesforce.
  • Option 2: Pre-Approve the connected app with the User-Agent OAuth Flow
  • Option 3: Run the auth:jwt:grant CLI command

Why we need this step. If you will not perform it then you will get below error while authenticate the user.

 {"error":"invalid_grant","error_description":"user hasn't approved this consumer"}

Option 1: Admin approves from connected app in Salesforce

Will use Salesforce console for this.

  1. Go to Setup -> click on Manage Apps -> Connected Apps
  2. Click ‘Edit’ against your app
  3. Click on Permitted Users and select “Admin approved users are pre-authorized
  4. Save.
  5. Now it time to provide access to profile or permission set. Go to setup -> Manage Users -> Profiles
  6. Select the profile which you want to access for example Integration profile.

Option 2: Pre-Approve the connected app with the User-Agent OAuth Flow

Copy and paste this link in the browser

https://<your instance>.salesforce.com/services/oauth2/authorize?response_type=token&client_id=<consumer key>&redirect_uri=http://localhost:1717/OauthRedirect

Login to Salesforce and authorize the connected app. Click Allow button.

Option 3: Run the auth:jwt:grant CLI command

Specify the client identifier from your connected app (also called the consumer key), the path to the private key file (server.key), and the JWT authentication username. When you authorize a Dev Hub org, set it as the default with the –setdefaultdevhubusername parameter. For example

sfdx auth:jwt:grant --clientid 04580y4051234051 \
--jwtkeyfile /Users/jdoe/JWT/server.key --username [email protected] \
--setdefaultdevhubusername --setalias my-hub-org

Step 3) Create JWT Token

Now our certificate is ready and uploaded in Salesforce connected App. It time to create a JWT token and test it out. As we discussed early we have three parameter in JWT Structure.

  • Headers
  • Payload
  • Signature

You can use https://jwt.io/ for simple testing. For that you need to provide all above values.

Headers: Algoritham & Token Type

{"alg":"RS256"}

Payload: Data

{
"Iss : "Clint_ID From Step 2.1.1",
"sub": "UserName",
"aud": "LoginURL https://login.salesforce.com",
"exp": "CurrentTimeStemp+2min"
}

 You can use different website to get timestamp. You can use unixtimestamp.com to get current value.

Signature

Now it time to pass verify signature details.

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  Private Key
)

Copy Private Key value from Server.Key ( Step 1.2). Once all look good you will Signature Verified in bottom of the screen with JWT Token.

JWT Token.

Step 4) Get access_token using JWT by POSTMAN

There are different way to test our JWT flow to get access_token. For this demo we will use POSTMAN to get access_token using JWT token which we created in last step 3.

Generate JWT Token from POSTMAN.
  • Method: POST
  • URL: https://login.salesforce.com/services/oauth2/token
  • Header Param
    • grand_type : urn:ietf:params:oauth:grant-type
    • assertion : USE JWT TOKEN created in STEP 3 using jwt.io.

Congratulation now you have access_token to execute any Salesforce API. Learn more about JWT Bearer flow for our old session JWT / SAML Assertion Bearer Flows. This how complete flow will look like.

Salesforce OAuth 2.0 JWT Bearer flow

JWT Flow using Apex

So far we created the JWT flow using jwt.io app. Let see how to use the same with Salesforce Apex class. For example you want to call one salesforce org API from another Salesforce org. For that you need to import the certificate in target org and you need Java Keystore (JKS) file format.

Generate a Java Keystore (JKS) which is importable in Salesforce

Salesforce only supports the Java Keystore (JKS) format for importing private key pairs in a Salesforce org.

Convert .CRT to Salesforce Keystore JKS file

We already have sever.crt file by using our step1. Let see how we can convert the same

  1. Go to same folder where you created server.key file(By using step 1). Clone the server.key file and save as server.pem.
  2. Now execute this command : openssl pkcs12 -export -in server.crt -inkey server.pem -out testkeystore.p12
  3. Now execute keytool command to create jks file. keytool -importkeystore -srckeystore testkeystore.p12 \ -srcstoretype pkcs12 \ -destkeystore servercert.jks \ -deststoretype JKS
    1. It will ask you to create password. remember it we will it soon.
  4. Now Salesforce dont support default alias 1. So change the alias name with this command keytool -keystore /servercert.jks -changealias -alias 1 -destalias salesforcetest

Import the Java Keystore (JKS) in

Now it time to import the Certificate in Salesforce

  1. Go to setup-> Search for Certificate and Key management
  2. Then Click on Import From Keystore.
  3. Then upload the servercert.jks file and provide the password which you to create the “servercert.jks” file.

Create a Apex class to execute the JWT Flow

Here Is JWT Apex Class without named credential.

Auth.JWT jwt = new Auth.JWT();
jwt.setSub('[email protected]');
jwt.setAud('https://login.salesforce.com'); 
jwt.setIss('connected app client id');

//Create the object that signs the JWT bearer token
Auth.JWS jws = new Auth.JWS(jwt,’Certificate keystore name’);
String token = jws.getCompactSerialization();
String tokenEndpoint ='https://login.salesforce.com/services/oauth2/token';

Auth.JWTBearerTokenExchange bearer = new Auth.JWTBearerTokenExchange(tokenEndpoint, jws);

//Get the access token
String accessToken = bearer.getAccessToken();
system.debug('Access Token-->'+accessToken);

APEX CODE WITHOUT NAMED CREDENTIALS

We know above class is not secure and having all securite details in apex. Which is not a best practice in Salesforce. For same we recommend use named credentials like below

JWT Named Credentials

And update your apex class like below.

String service_limits='/services/data/v48.0/sobjects/Account/listviews/';

HttpRequest req = new HttpRequest();
req.setEndpoint('callout:JWT_Demo'+service_limits);
req.setMethod('GET');

Http http = new Http();
HTTPResponse res = http.send(req);
System.debug(res.getBody());
System.debug(res.getstatuscode());

Salesforce OAuth 2.0 JWT Bearer flow Video

Check below recording to learn complete JWT flow in Salesforce using video.

Summary

OAuth JWT Bearer token flow is Ideal for application which access sfdc only through API as there is no UI involved. For example ETL tools or middleware. Digital certificates are required in this flow. Upload certificate (X509 ) to connected app which will be used to authenticate JSON web tokens. No refresh token is returned in this flow. So if access token expires then send request to generate access token again.

Amit Chaudhary

Amit Chaudhary

Amit Chaudhary is Salesforce Application & System Architect and working on Salesforce Platform since 2010. He is Salesforce MVP since 2017 and have 17 Salesforce Certificates. He is a active blogger and founder of Apex Hours.

Share this article

13 Comments

  • You use JWT.io when you are requesting for access token. Is it possible that we directly upload our certificate and key in postman and get the token?

  • I am getting the unsupported grant type error in Postman , I tried even setting the header with Content-Type : application/x-www-form-urlencoded , no luck

    {
    “error”: “unsupported_grant_type”,
    “error_description”: “grant type not supported”
    }

    Any suggestions , pls ?

    • Please try grant_type as “urn:ietf:params:oauth:grant-type:jwt-bearer”

  • Getting the unsupported grant type error in Postman/code with the same steps.

    {
    “error”: “unsupported_grant_type”,
    “error_description”: “grant type not supported”
    }

    Any suggestions pls

  • Make sure the grant_type has complete value as “urn:ietf:params:oauth:grant-type:jwt-bearer”

  • @JB
    I have the correct grant type and got my key, but I still get the same error.
    {
    “error”: “unsupported_grant_type”,
    “error_description”: “grant type not supported”
    }
    I’m also using the end point: https://test.salesforce.com/services/oauth2/token

    Do you have any other additional setup on your postman post request?

  • This blog is really helpful for setup OAuth flow.
    We’re an ISV provider and we’ve coded in our managed package which creates the Static Resource file on the user’s Salesforce environment. Recently Salesforce security team asked us to not use the UserInfo.getSessionId() to create a Static Resource file and suggested using the OAuth, (try JWT flows and look into admin pre-authorization).
    If we follow the steps above using JWT flow and creating Connected Apps in our developer org, can it be part of the managed package and does the end user also need to do all the above steps in their Salesforce environment?

  • Very useful blog. Thanks a lot. There are two typos in this blog and due to that I got couple of errors while testing it in Postman.
    In Payload, it should be “iss” (lower case i), not “Iss. (double quotes is also missing)
    In the Postman testing example, the param should be grant_type, not grand_type

  • Hi Amit,

    If I use the Named Credential and HttpRequest process to get an Access token for one of my experience sites, I get the response body in html format and the entire site html format body is returned.
    How do we get the Access token using Named Credential and HttpRequest process?

    Thanks

  • I am trying to connect 2 experience cloud sites. Once I get the Access Token, I want to use that and redirect user to 1 experience cloud site from another one.

Leave a reply

Subscribe for Email

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 3,994 other subscribers

Our Supporter

RECENT POSTS

Apex Hours

Apex Hours is one stop platform to learn Salesforce skills and technology

Join our Newsletter and get tips and tricks how to explore the salesforce for free!