2

We are setting up a Salesforce Community and our community members should be able to automatically login using the credentials from an extenal platform when clicking on a link from this platform. I'm trying to generate a JWT token (Please find below the class I've made to generate the JWT) and I've created a connected app to get a consumer key.

public inherited sharing class JWTAuthFlow {

public static string createJWT(String iss, String sub, String aud, String endpoint){
    // Static Resource to get the Key that will be used for Crypto-Sign
    StaticResource jwtPrivate = [SELECT Id, Body FROM StaticResource WHERE Name = 'JWT_Private_key' LIMIT 1];
    String privateKey = jwtPrivate.Body.toString();
    
    //Create Body of the Json that contains enpoint URL, Consumer Key, Type, ect
    JSONGenerator jsonHeader = JSON.createGenerator(false);
    jsonHeader.writeStartObject();
    jsonHeader.writeStringField('alg', 'RS256');
    jsonHeader.writeStringField('typ', 'JWT');
    jsonHeader.writeEndObject();
    String encodedHeader = EncodingUtil.base64Encode(Blob.valueOf(jsonHeader.getAsString()));
    
    JSONGenerator jsonBody = JSON.createGenerator(false);
    jsonBody.writeStartObject();
    jsonBody.writeStringField('iss', iss);
    jsonBody.writeStringField('sub', sub);
    jsonBody.writeStringField('aud', aud);
    jsonBody.writeNumberField('exp', Datetime.now().addMinutes(3).getTime());
    jsonBody.writeEndObject();
    String encodedBody = EncodingUtil.base64Encode(Blob.valueOf(jsonBody.getAsString()));
    String jwtRequest = encodedHeader + '.' + encodedBody;
    
    // Sign JWT Request
    Blob key = EncodingUtil.base64Decode(privateKey);
    Blob rs256sig = Crypto.sign('RSA-SHA256', Blob.valueOf(jwtRequest), key);
    String signature = EncodingUtil.base64Encode(rs256sig);
    signature = signature.replace('+', '-');
    signature = signature.replace('/', '_');
    String signedJwtRequest = jwtRequest + '.' + signature;
    
    // Create the payload
    String payload = 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer';
    payload += '&assertion=' + signedJwtRequest;
  
    // Create a HttpRequest and send it to server
    Http httpObj = new Http();
    HttpRequest req = new HttpRequest();
    HttpResponse res;
    req.setEndpoint(endpoint);
    req.setMethod('POST');
    req.setHeader('Content-Type', 'application/x-www-form-urlencoded');
    req.setBody(payload);
    res = httpObj.send(req);
    
    if(res.getStatusCode() == 200){
        Map<String, Object> mapTokenResponse = (Map<String, Object>)JSON.deserializeUntyped(res.getBody());
        return (String)mapTokenResponse.get('access_token');
    }
    
    return res.getBody();
}

}

I have the following error when I try to generate a JWT: 'Unrecognized base64 character:-' (I can see from system.debug that it generates the header and the body, but not the signature).

I don't know what I'm doing wrong ?

It's the first time I'm working with JWT so I don't know if I've missed something. The developer from the external app is waiting for the token, and the returnURL (the path). Is there something else I need to give him ?

1 Answer 1

3

A Named Credential can issue a JWT out of the box (see this answer for more details on how it works)

HttpRequest req = new HttpRequest();
req.setEndpoint('callout:MyJWT/some_path');
req.setMethod('POST');
Http http = new Http();
HTTPResponse res = http.send(req);
System.debug(res.getBody());

If you need more flexibility with payload, use Auth.JWT class:

Auth.JWT jwt = new Auth.JWT();
// add standard claims
jwt.setIssuer('https://blah.my.salesforce.com');
...
// add custom claims
Map<String,String> claims = new Map<String,String>();
claims.put('foo','bar');
jwt.setAdditionalClaims(claims);

// Generate token
Auth.JWS signedToken = new Auth.JWS(jwt, 'MyKeyPairName');
String serializedToken = signedToken.getCompactSerialization();
System.debug(serializedToken);
4
  • 1
    For what it's worth, Auth.JWS only works if the keypair is in Salesforce's certificate and key management.
    – Derek F
    Oct 23, 2020 at 17:04
  • Thanks identigral ! So if I understand well, since the external platform also uses JWT, I'll chose the JWT Token option then ? If I need to add additional claims, I won't be able to use Named Credential though right ? I'll only have to use Auth.JWT ? And I didn't have to create a Connected App ?
    – chloe
    Oct 23, 2020 at 17:16
  • @chloe Yes, you got it. Connected app is for calling into Salesforce, you don't need it for calling out from Salesforce to an external non-Salesforce service.
    – identigral
    Oct 23, 2020 at 18:40
  • Named credential can issue JWT OOTB as long you can import certificate (Java KeyStore format) into Salesforce otherwise you cannot use this method
    – compski
    May 3, 2023 at 3:08

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .