Secure calls from Salesforce to MuleSoft with JWT

A couple of my last articles focuses on connectivity between Salesforce and MuleSoft. This will be the next article in the series. In many cases, we would like to set up as secure communication as possible. We can start by enabling secure HTTP traffic, then sending a basic authorization header. How about sending a signed token? In this article, you will find details on how to set up the JWT validation policy on the MuleSoft side. Alongside you will see how to Setup Salesforce to send such token. Let’s get started!

What is JWT?

JWT stands for JSON Web Token. This is an open standard of sending a compact JSON object alongside the signed signature. The signature can be computed using either the HMAC algorithm or the private key (RSA). Below you can find all the parts of the token. That is header, payload, and signature. All parts are encoded using the Base64 algorithm.

Parts of the JWT token
Parts of the JWT token

The signature part is computed based on the header and payload base64 content concatenated with the dot sign. You can see the resulting token, based on the following example, down below:

Sample JWT token with header, payload and signature separated by the dot character
Sample JWT token with header, payload and signature separated by the dot character

Salesforce Setup

Create certificate

The first step, on the Salesforce side, is to generate the public certificate and private key. Using this pair, we will encrypt and decrypt the JWT token. Go to the Service Setup and then look for Certificate and Key Management. Once you are there, click Create Self-Signed Certificate button. You will be asked for a label, unique name, and key size. Below you can see my sample setup. I called the certificate MuleSoftJWT.

MuleSoftJWT certificate created in Salesforce

Share the file

Go back to the list view. For Salesforce usage we do not need any additional steps here. However, the MuleSoft team will need a public key in order to decrypt the JWT token. Select Export to Keystore. You will be asked for a password to secure the keystore. After you specify it, click the button Export and download the jks file.

MuleSoft team, to set up JWT validation needs only a public key. So how to extract it from the keystore file? You may use whatever tool you like, I use Keystore Explorer. When you open your jks file, you may notice that all the pairs have been exported. Of course, not need to worry. We won’t pass this file to the other team. What we need to do, is to export public key for a specific certificate – in my case mulesoftjwt.

Exporting public key from the Salesforce keystore file
Exporting public key from the Salesforce keystore file

In consequence, I got mulesoftjwt.pub file with public certificate like down below. MuleSoft use it to decrypt the JWT token.

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsP250w+fYmncd3cQOzGE
KZLhhCCE5NWM2T5Q5s3X8YOESRu4o0rErd3h5ioVKurD6/2n4pMq1H4vBZOad6y2
bP3nVFH1i5UaJ2HI81tEZ74DJRJrCG7Pm1qnATzNCvObda9ejCsD8W5c6pQIe4xz
HswZWy7Ios9KW+9YNKKNuzKDbXBBnNgRWwF49kvKzElnZHdXSXigbAOvk4gvuonQ
DhhAd6nmAJ0HDyJzq/BPo8ZdMOqDIbSLOCRNAnC1md4KFa9rvLCONdY2uExsZlRc
2FPd60OLQSVSb6AMyQ7bwHAEBZmrknTDPY6GgpoIcW2CX/cU/7iZEPImM7J707RW
9QIDAQAB
-----END PUBLIC KEY-----

JWT Validation policy

API Policy configuration

Below you will find the options that are available in the JWT Validation policy configuration. My demo application has some assumptions, that will be reflected in the selected configuration.

JWT origin

You can specify here if we should expect JWT token in authorization header or in the custom one. When you pick HTTP Bearer Authentication Header you should send header called Authentication and the value Bearer [token].

Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik11bGVTb2Z0 ...

On the other hand, Custom Expression gives you greater flexibility and allows for custom extraction. The default expression to retrieve the token is to look for jwt header. Unlike the previous setting, here you just specify the token without the ‘Bearer’ keyword.

In my case, I have dedicde to pick the default choice, that is HTTP Bearer Authenitcation Header.

JWT Signing Method

Here you specify how will be encrypted the token. You have three choices: RSA, HMAC, and None. Salesforce requires RSA, which expects key pair. In my case, I will leave this value as default.

The other option is to use HMAC function, to encrypt the token with known secret password. The last option, None, is of course not recomended.

JWT Signing Key Length

Once you decided for either RSA or HMAC in the signing method, you need to specify the length of the key to be used. The options are three such as 256 (default), 384, and 512. In my case the default option is sufficient.

JWT Key origin

RSA requires a public key, HMAC secret password. Both of these can be provided as a Text in the next configuration option. In the case of RSA, you have one additional advantage. That is, you can specify a remote location, where the public key will be read from. This gives you more flexibility, for example in key rotation, as the policy won’t need to be changed. It will read a new key from the remote location specified in JWKS (JSON Web Key Set).

In my case, the default Text option is good enough.

JWT Key

As I decided to use Text to provide the key, I have the JWT Key option. In case of the public key we need to copy and paste it into the text box, but without first and the last name and without line breaks. The above-presented key should look now like the one here:

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsP250w+fYmncd3cQOzGEKZLhhCCE5NWM2T5Q5s3X8YOESRu4o0rErd3h5ioVKurD6/2n4pMq1H4vBZOad6y2bP3nVFH1i5UaJ2HI81tEZ74DJRJrCG7Pm1qnATzNCvObda9ejCsD8W5c6pQIe4xzHswZWy7Ios9KW+9YNKKNuzKDbXBBnNgRWwF49kvKzElnZHdXSXigbAOvk4gvuonQDhhAd6nmAJ0HDyJzq/BPo8ZdMOqDIbSLOCRNAnC1md4KFa9rvLCONdY2uExsZlRc2FPd60OLQSVSb6AMyQ7bwHAEBZmrknTDPY6GgpoIcW2CX/cU/7iZEPImM7J707RW9QIDAQAB

Additional validations

The next couple of options are additional validations, that can be incorporated. The first is Client ID validation. You may expect from the callers to provide registered client_id value. This means, that the Client ID will need to be put by Salesforce to claims. Later on, the policy will validate if there is a registered application with provided id value.

As I require from all my application to provide client id and client secret, I have decided to enable that option. Once I did this, I need to provide the Client ID Expression. In other words, DataWeave expression from where to look for Client ID value. I am fine with the default value:

#[vars.claimSet.client_id]

Further, we can enable audience validation. It is sent in the payload part of the JWT token as aud property. Who the audience is? It is the intended recipient of the token. I have decided on two possible values that are api.patrykbandurski.com and pb-lb.anypointdns.com. These two values are the entry point to my MuleSoft applications. So I decided that these are the perfect choice for my case.


Next extra validation is an expiration claim check (exp property). It means that, if the exp property is not present the request will fail. I have decided for security reason to expect this claim.


After this, you have a choice to validate if not before claim is present (nbf property). I have decided to remove this check, as tokens are instantly valid after issuing them. I do not have a case where the token will be valid after some time after creation.


What is more, you can define your own claims in the Validate Custom Claim option. You can specify here required and non-required claims with possible values. In my case, it is not needed, but just for the reference. Values expected here should be present in the payload part of the JWT token.

Configuration summary

Below you can see the summary of my choice. Do not bother with JWKS Url and JWKS Caching TTL (minutes). They are presented here, although not used. The same is with Token expression.

Selected configuration of JWT validation policy
Selected configuration of JWT validation policy

It is important to remember that such a policy may impact how your RAML should look like. Below you can see, my API extended with a snippet for JWT validation – lines 6 – 20.

#%RAML 1.0
title: SFDC JWT XAPI
version: v1
mediaType: 
  - application/json
traits:
  jwt:
    headers:
      authorization:
        description: Bearer 
        type: string
    responses:
      400:
        description: Token was not provided.
      401:
        description: Bad or expired token. To fix, you should re-authenticate the user.
      403:
        description: The client id validation failed.
      503:
        description: Error communicating with JWKS server.

types:
  lead: !include types/lead.raml

/leads:
  get:
    responses:
      200:
        body: lead[]

  post:
    body: lead
    is:
      - jwt
    responses:
      201:
        headers:
          location:
            type: string

Salesforce token generation

Salesforce has class called JWT that helps generating a token. Sample code you can find on my GitHub here. Let’s break the code down.

Auth.JWT jwt = new Auth.JWT();
jwt.setSub('integration@salesfroce.com');
jwt.setAud('api.patrykbandurski.com');

Map<String, Object> claims = new Map<String, Object>();
claims.put('client_id', 'fdc5b21bfb8f4af89900b11db3b9eb59');

jwt.setAdditionalClaims(claims);

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

String token = jws.getCompactSerialization();
System.debug(token);

The above code sample results from the choices I made configuring the JWT validation policy. In line three, you can see that I have a specified audience to be equal to my DLB address. How to setup MuleSoft to expose DLB – find it here. You can also opt-out from lines 4,5 and 7 if you do not want to send additional claims. As you remember I need to do this, as I want to receive a client_id claim. Line ten uses Auth.JWS to generate a token with specified data and encrypted signature using the MuleSoftJWT token. Do you remember why MuleSoftJWT? This is the name of the certificate I used in the Salesforce Certificate and Key Management.

I run the code in Developer Console and here is the JWT token that has been generated:

Decoded JWT Token sent from Salesforce to MuleSoft (jwt.io)
Decoded JWT Token sent from Salesforce to MuleSoft (jwt.io)

I use the jwt.io web page to see decoded JWT token. You can see the public header and payload part. As you can see it comprise of all necessary parts. The last part is the signature (the light blue). In order to validate I use public key MuleSoftJWT. Once I pasted it, the Signature Verified summary has appeared. We can expect now, that MuleSoft will accept the token.

MuleSoft API test

Okay, it is the perfect time to test our API. I start with deploying the application dev-sfdc-jwt-xapi on CloudHub. Based on the provided RAML I prepare URI http://dev-sfdc-jwt-xapi.us-e2.cloudhub.io/api/leads. I get the token generated in Salesforce and put it into the Authorization header. Below you can see a whole cURL command:

curl --location --request POST 'http://dev-sfdc-jwt-xapi.us-e2.cloudhub.io/api/leads' \
--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik11bGVTb2Z0SldUIn0.eyJzdWIiOiJpbnRlZ3JhdGlvbkBzYWxlc2Zyb2NlLmNvbSIsImF1ZCI6ImFwaS5wYXRyeWtiYW5kdXJza2kuY29tIiwiaWF0IjoxNTk0MzA1OTk1LCJuYmYiOjE1OTQzMDU5NjUsImV4cCI6MTU5NDMxNjI5NSwianRpIjoiZWZhODYzNzEtMTc1Ny00MjdiLTg0N2EtOTRiYzdmYzVhZmExIiwiY2xpZW50X2lkIjoiZmRjNWIyMWJmYjhmNGFmODk5MDBiMTFkYjNiOWViNWUifQ.bjKVKrLlVfvbStBShoMv2d1o2N7of3_sl0XgWctaReloe5A8Ho8qqTjYznH11VEwYom6NPW5pwt1puVIzyKLY2Np0OOehJEp5By3_xtjpqIC3g51Ds57zQZOnXYwTBywYs7VQVt19ekOrI_Wy0-tfGId6GIpq-Hjkqb6vwH_PlYdI3EFy5RYqMXzZLjcfxUV80KEnGAfTR0QjcV8w6wUO9ej7mVR6kLa3xxKMHgWamKwsfw4vWl2uhxt0STeAkcozJMGknv8ObmUVAVyl9N5QW3l5HmOzKJ8tM7b2cu50KA7b5sBIiZSsvAZmBUxpxLjapC3bt6myVR_lQb7IdRC4w' \
--header 'Content-Type: application/json' \
--data-raw '{
  "name": "Anipoint Samson",
  "email": "anipoint.samson@dom.com",
  "company": "Dom LTD",
  "title": "Master of",
  "website": "dom.com"
}'

I received 201 response. Grand success. JWT token has ben succesfully validated by the API Manager. We are ready to go!

Advice

Do you receive 401? Not sure what is happening? Set com.mulesoft.extension.policies.jwt to the DEBUG level. Once you do this, you should receive step by step information in your log file. For a successful call, you would find following entries
Token was parsed successfully, ready to validate the signature of the token, token signature successfully validated, validating aud claim, validating exp claim, validating nbf claim, JWT Token was successfully validated.

Summary

JWT Validation policy may be intimidating at first, but all in all, the configuration options are simple. You have great flexibility in preparing custom configuration for your clients. The presented above example should be useful in most cases. Of course, teams must cooperate to set up properly the connection, but the most crucial part is to set up a key pair and what is expected from the MuleSoft side.

Not to be over-optimistic, not all options are straightforward and may lead to confusion. That is why you should remember about the package that debugs the JWT validation policy and may help diagnose the problem with the setup. I hope that the presented configuration helps you with your further configuration and speed your development a little bit.

Cheers!

Secure calls from Salesforce to MuleSoft with JWT

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top