Authenticate from Salesforce to Heroku with JWT
Heroku PAAS is an easy and flexible way to extend Salesforce functionality.
Its easy to call out to a Heroku REST service build with a method of your choice: Java, JavaScript, Ruby etc.
The usual sticky point between two platforms is the identity of the caller.
In a lot of cases the only requirement is "a valid Salesforce user", eventually with some additional claims added.
For this scenario a JWT Bearer authentication (RFC 7519) is a good pick.
When you look around, most examples revolve around a JWT token being issued after some means of authentication from the same system that was authenticated against.
This scenario is different: The Salesforce server issues the JWT token and code on Heroku validates the token as authorization.
The beauty of the approach: no extra calls need to be made to get this working.
What you will need:
- A valid certificate, in this case self signed is good enough (it won't be used by a browser)
- OpenSSL installed on your computer
- A Heroku account
Preparing the certificate
In Salesforce setup open Certificate and Key management.
Create or pick a key. Note down the name. For this article I will use YourCertNameHere. Open the key and click on Download Certificate.
The cert will be downloaded in crt format. We use this file to extract the public key that Heroku will use to verify the signature. To get the key use:
openssl x509 -pubkey -noout -in YourCertNameHere.crt
The String we need is between the BEGIN and END lines, excluding the lines. You can store it into a file or create a Heroku environment variable.
Since it is a public key, you don't need to guard it as fiercly as your private keys.
The APEX code
Before making a call-out to Heroku, you need to componse the signed JWT token. That's done in just a few lines:
public static String getJWTBearer(String subject, String keyName) {
Auth.JWT jwt = new Auth.JWT();
jwt.setSub(subject);
Auth.JWS myJws = new Auth.JWS(jwt, keyName);
return myJws.getCompactSerialization();
}
You might opt to add additional claims beside the subject, when your use case does require that.
The header value gets added to the Authorization
header as Bearer
authorization.
Java code
I'm using the jjwt library which is available on Maven Central.
It makes it simple to retrieve a claim. An expired claim or an invalid signature will throw an error, so wrap it into a try/catch.
import java.security.Key;
import java.security.KeyFactory;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
public class JwtVerifier {
public static Claims getClaims(String key, String token) throws Exception {
byte[] byteKey = Base64.getMimeDecoder().decode(key);
X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(byteKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
Key realKey = kf.generatePublic(X509publicKey);
return Jwts.parser().setSigningKey(realKey).parseClaimsJws(token).getBody();
}
}
The only catch in the code was the need for MimeDecoder
instead of a standard Decoder
for Base64 decoding.
The subject, typically the user, can be retrieved using claims.getSubject()
Next stop, for another blog entry: the NodeJS equivalent.
As usual YMMV!
Posted by Stephan H Wissel on 23 March 2018 | Comments (0) | categories: Heroku Identity Management Java JWT Maven Salesforce