Whenever you work with Json Web Tokens (JWT’s) generating them for testing is always a hassle as they usually are required to expire quite quickly (like in the order of minutes). To make that easier I wrote a small utility in node.js to generate JWT’s compatible with the Salesforce OAuth 2.0 JWT Bearer Flow.
Code is on Github at https://github.com/lekkimworld/salesforce-jwt-generator
If working with JWT’s for use with the inbound OAuth 2.0 JWT Bearer Token Flow you need to import a public key and a certificate to validate the signature of the JWT when calling into Salesforce. This is done on the Connected App and the import supports binary DER-format and the plain text PEM-format.
Once you’ve created your Connected App, check “Enable OAuth Settings” and under “Use digital signatures” import the certificate (PEM or DER format) to use to validate the signature of the JWT.
Using the actual flow requires you set the Consumer Key as the issuer (“iss”), the username of the user to act as, as the subject (“sub”) and the login-url as the audience (“aud”). The login-url will be https://login.salesforce.com, https://test.salesforce.com or a community url.
To make it easier to work with and test I’ve created a node.js console app that allows you to generate JWT’s that are compatible with Salesforce. The code is on Github at https://github.com/lekkimworld/salesforce-jwt-generator.
Exchanging the JWT for an access_token is as below setting the grant_type to urn:ietf:params:oauth:grant-type:jwt-bearer and specifying the signed JWT using the assertion-parameter:
POST /services/oauth2/token HTTP/1.1
Lately I’ve been playing around with Azure and integrating Salesforce and Azure. One of the integration patterns calls for using Json Web Tokens (JWT) that you can the exchange for an access token in Azure. There is a catch however…
Since Azure requires that the thumbprint of the certificate be added to the header of the JWT (using the key “x5t”) we cannot use the built in support for JWT in Named Credentials as there are no provisions for custom header key/values. The JTW/JWS classes in Apex cannot be used either as we cannot customize the header there either. Building upon https://github.com/salesforceidentity/jwt I’ve created https://github.com/lekkimworld/azurejwt-apex that bridges the gap.
This allows you to build and sign a JWT that you may exchange for an access token using your tenants OAuth token endpoint v.2 in Azure. Example Apex code is like this:
// declarations (because I'm old school)
final String azureClientId = '88d888a5-0cf4-473a-b9a0-7c88e6fc888e';
final String azureTenantId = 'b34feb2b-132f-4322-af1d-c888f5d888d0';
final String azureCertThumbprint = '4rElsDFTysrbKhB0zTsrRNSxT6s=';
final String azureScopes = '5384888d-868f-442b-b1b3-8688807de914/.default';
// create JWT with certificate from keys mgmt and set the x5t in the header to the
// thumbprint of the cert as expected by Azure
AzureJWT jwt = new AzureJWT();
jwt.cert = 'JWT_Callout_Certificate';
jwt.iss = azureClientId;
jwt.sub = azureClientId;
jwt.aud = 'https://login.microsoftonline.com/' + azureTenantId + '/oauth2/v2.0/token';
jwt.x5t = azureCertThumbprint;
// invoke the flow and obtain an access_token
final String access_token = AzureJWTBearerFlow.getAccessToken(azureClientId, azureTenantId, azureScopes, jwt);
// use the access token against a Function App in Azure
HttpRequest req = new HttpRequest();
req.setHeader('Authorization', 'Bearer ' + access_token);
Http http = new Http();
HTTPResponse res = http.send(req);
In the https://github.com/lekkimworld/azurejwt-apex Github repo you will find the two Apex classes from the above example together with the example code.
The certificate thumbprint (bold above) isn’t the regular SHA-1 thumbprint but is a special hexdump/base64 encoded edition. To make it even more interesting the thumbprint displayed in Azure Portal is not the thumbprint we need. The thumbprint/hash may be computed this like (gleaned from https://stackoverflow.com/a/52625165):
echo $(openssl x509 -in yourcert.pem -fingerprint -noout) | sed 's/SHA1 Fingerprint=//g' | sed 's/://g' | xxd -r -ps | base64