OAuth 2.0 JWT Client Credentials Flows using out of the box Salesforce Named and External Credentials
A step by step guide on how to setup the new OAuth 2.0 flow to connect to the NHS API using a JWT for Client Authentication.
In the Salesforce Winter ‘24 release the “Client Credentials with JWT Assertion Flow” has been been added as an out of the box OAuth 2.0 flow in Named / External Credentials.
For most use cases this eliminates the need of any custom Auth Providers like I described in a previous article . This is good news because the more standard security features we can leverage out of the box, the better.
In this article I am going to give a step by step walkthrough on how to setup this flow to connect to an external API. I will be using the NHS API as an example throughout this setup.
I am unaware of any affiliation between NHS and Salesforce. I choose the the NHS API purely for test purposes only. This API has very detailed error messages on the authorization flow which is ideal for debugging.
Limitations
During my testing the only limitations I could find are the lack of “Per User” principal support and the inability to send custom headers to the token endpoint.
This means that if your token endpoint requires a custom header or each of your users needs to authenticate individually (with a different JWT Subject for example), You’ll still have to implement a custom solution.
Important
Security is no easy subject: Before implementing this (or any) solution, always validate what you’re doing with a certified security expert and your certified implementation partner.
Prerequisites
Before you can start setting up this flow there are are few things you’ll need to have ready.
- Read the introduction section of this article to have a bit of background of what we’re trying to replace with OOTB functionality. (Optional)
- Some knowledge on this type of OAuth 2.0 Flow, JWT, JWS and JWKS is recommended as I will not explain these concepts.
- You need to have a certificate with the private key that is used for signing the JWT with a JWS, imported in the Salesforce Certificate Key Store.
- Alternatively you can use a Self Signed Certificate for testing purposes.
- The public key needs to be shared with the authorisation server and setup according to the identity provider’s standards. This is usually a JWKS. Have the setup documentation from your target system at hand.
- You’ll need all the authorization server details that are required to setup the connection and create the JWT. Usually these are: The token endpoint, client id, subject, audience, issuer, kid and signing algorithm.
00 :: Preparation
- Import your Signing Certificate into Salesforce or create a self signed certificate. Make sure the public key / certificate is shared and implemented in the external system.
- Importing can be done in the “Certificate and Key Management” section of the Setup menu
01 :: Setup the External Credential
- Go to Setup >> Security >> Named Credentials and click on the External Credential tab. Press the New button in the top right corner.
- Give a Label and a Name. In my example this is “NHS_Sandbox”
- For Authentication Protocol picklist select the value “OAuth 2.0”. In the Authentication Flow Type picklist set the value to “Client Credentials with JWT Assertion”.
- This now reveals the Common claims section and the Identity Provider URL field. In the IDP URL field populate the token endpoint.
- Populate the common claims section with the values as per your identity provider’s documentation.
- This is the Issuer, Subject, Audience and the JWT Expiration in seconds.
- The expiration is usually 300 seconds, but can be more or less.
- Note that here we cannot yet add the KID field yet. This will come later.
- The NHS API specifies that the Issuer and Subject both are the Api Key that they provide you. (Note that these are not my real keys obviously)
- The audience is matched to the endpoint, but can be anything. Again check what is required
- The last step is to select your Signing Certificate and the Signing Algorithm. In my example I have a certificate called “nhs” and the required algorithm is “RS512".
- When done press Save
02 :: Update the KID
- Most JWT CC implementations require you to share your certificate’s public key in a JWKS format. This is a JSON file that contains the public keys where each public key has an identifier called the key identifier or “kid” for short.
- The “kid” is specified in the similar named JWT claim.
- When you save your external credential this kid claim is automatically populated with the name of your certificate. If this is not correct, it should be changed.
- In your newly created External Credential, scroll down to the JWT Claims section and press the “Edit” button.
- You can update the claim values accordingly. I have updated my kid to the value “test-1” to match my keys.json JWKS file.
- NOTE: Event though I have selected RS512 as a Signing Algorithm, (I think due to a bug) it still sets the algorithm as RS256. We have to update this manually to RS512 each time after we have updated the claims, keep this in mind because it can cause you headaches later.
- The NHS API requires a “jti” parameter in the claim header, so I will add that in here as well. This is a unique message Id to prevent replay actions. I created a random enough function for multiple user logins. If you need a truly random UUID you’ll need a custom solution.
RIGHT(TEXT(SQRT(ATAN2(VALUE(REVERSE(TEXT(UNIXTIMESTAMP(NOW())))),VALUE(TEXT(UNIXTIMESTAMP(NOW())))))),36)
- Again if you use RS512, tripe check this is set correctly and if not edit the external credential and update it.
03 :: Custom Headers
If your API calls require custom headers they can be setup here. Note that any custom header setup at External Credential level will add the headers for ALL named credentials related to the External Credential. If you need headers for only a specific Named Credential, the must NOT be added here.
Note: These header will not be added to callouts to the token endpoint. Only to
- The NHS API requires to always have the “apikey” header to API requests, so I am going to add that parameter and matching value here.
- The sequence can be used to order multiple headers
04 :: Setup a Principal
To setup access to Named / External Credentials, Salesforce requires you to create a principal. This is a custom named parameter you setup in the Principals section of your newly created External Credential
- Scroll the the Principals section
- Click the New button
- I am going to create a new principal specifically for a System User. I call this one “System_User_Principal”. We will need this in our profile or permission set to grant users access to use this credential.
- Even though it does not seem required, the Client ID value does require a value. If your provider requires a client Id it can be added here, alternatively put in a dummy value.
- This flow allows for only one principal.
- Once created the Authentication Status will be set to “Configured”. If this is not the case, you most likely have left the Client ID field blank.
05 :: Setup a Named Credential
- Go to Setup >> Security >> Named Credentials and click on the Named Credential tab. Press the New button in the top right corner. Note do NOT use the button for “New Legacy”
- Give a Label and a Name. In my example this is “NHS_Sandbox” to match the External Credential
- In the URL Field set the Base URL for your API endpoint. In each callout you make, this will be set as the starting point. So if your URL is “https://sandbox.api.service.nhs.uk/hello-world/hello/application” the base URL is the part that never changes in my case “https://sandbox.api.service.nhs.uk”
- The Client Certificate field can be left blank
- If you are planning on using this Named Credential for Apex Callouts set the Enabled for Callouts to “true”. This is true by default.
- In the Callout Options section select “Generate Authorization Header”. As this will automatically add the OAuth2 standard Authorization header, what is sort of the point of using Named Credentials.
- If your headers or body contain any formula fields (This could be add additional info to your endpoint), Make sure to set the Allow Formulas check boxes accordingly. If you don’t use formulas, keep them unticked.
- If you have managed packages that require to access this Named Credential you can specify the namespaces. I use a utility for my REST calls, so I will add “lwt, utl” (lightweight / util packages). Use this with caution and only if you trust a managed package.
- Press Save
06 :: Setup security
The last step is to provide users access to this external credential. Keep in mind to always use the principle of least access. Meaning that only the Integration User should have access
- Go to Setup >> Users > Permission Sets and click the New button
- Give your Permission Set a Label and a Name. In my case; surprise, surprise: “NHS Sandbox” to match.
- In the section Apps, click on External Credential Principal Access, Click the Edit button and assign your newly created Principal to the permission set.
- The name will be a combination of the Label of the External Credential combined with the name of the principal. When enabled, press Save.
- Click Manage Assignments at the top of the permission sets and assign the test user (currently logged in and remove when no longer needed) and the target integration user.
08 :: Test your configuration
Now you are ready to test. Open an execute anonymous window in the developer console and execute the below code snippet. (Update accordingly to your endpoint and named credential name)
HttpRequest request = new HttpRequest();
request.setEndPoint('callout:NHS_Sandbox/hello-world/hello/application');
request.setMethod('GET');
HttpResponse response = new HTTP().send(request);
System.debug(response.getBody());
And that should be you all ready to go :-)
Final note
At the time of writing I work for Salesforce. The views / solutions presented here are strictly MY OWN and NOT per definition the views or solutions Salesforce as a company would recommend. Again; always consult with your certified implementation partner before implementing anything you’ve read on the internet.