Setting up Security
Service Overview
To complete the Zeleo integration, we need a service to send Events and/or receive Consequences to and from Zeleo. On the previous page, we used the configuration tool inside Zeleo to teach it how to interact with your service, and now you need to create the service itself.
Note: If you are integrating your own company’s product into Zeleo, you might not need a separate service; you could just have your servers talk directly with Zeleo.
Language and Technology Selection
Interaction with Zeleo is entirely REST-based, so your service can be in any language you prefer and on any stack you are comfortable with. Hosting is also something you can select on preference; we have internally used AWS Lambda, Docker Containers, and even Heroku and Openshift PAAS services successfully. We will avoid the actual service hosting details and stack implementation in this document as much as possible, and for now we will use Java for any code examples. We like Java here at Zeleo.
Security Fields
The first thing you will want to set up in your service is the JWT authorization code. Remember when we set up our Zeleo Application, there was a section where we entered three fields, and a fourth was generated for us. For example:
If you didn’t before, you will need to enter the Issuer, Audience, and Subject and save to generate the Token. Right now the values you enter don’t matter too much, but we will use the Issuer, Audience, and Subject in a future Zeleo Application marketplace when we have enough to necessitate that interface. Either way, the values cannot be null and you will need to match them exactly with your service. We recommend you enter the following:
- Audience: The intended audience(s) for the service. For instance ‘Software Development Company’, ‘IT Deparatment’, ‘Marketing Company’, etc. Use a semicolon to split them if entering more than one Audience.
- Issuer: This is either you or your company depending who will be taking responsibility for the service.
- Subject: Somewhat similar to the Audience, this is what is generally known as the category for your Zeleo Application. As before, use a semicolon to add more than one Subject.
Okay- so now we have the four values, and we need to make sure our code has them as well. There are a few ways to do this, but please make sure that the Token is not available for any hackers to find. That is your authorization to interact with Zeleo, and anyone could fire events as you with that token. If you are using AWS, they offer the ability to add protected Environment Variables that is a good way to expose the security settings to the service, and the way we handle this internally. You also could hard code the values into your code if you don’t expect to change the security values at all. If you do persist them to a database, please make sure at least the Token field is encrypted.
There are two other strings you will need in your service as well:
- HOST: This is where you fire the events. This is going to be https://www.zeleo.io/ and as such can be just hard coded, but it’s good practice to separate it out into a variable.
- CLOCK_SKEW: This is the 5th and final ingredient in the JWT token generation and decryption. This sets the length of time the token is valid and will create an invalid token if it is different than our service. You are going to want this to be 60 seconds.
Here’s an example from one of our AWS Lambda Functions:
final private static String SERVER_KEY = System.getenv("JWT_TOKEN");
final private static String ISSUER = System.getenv("JWT_ISSUER");
final private static String AUDIENCE = System.getenv("JWT_AUDIENCE");
final private static String SUBJECT = System.getenv("JWT_SUBJECT");
final private static int CLOCK_SKEW = 60; // seconds
final private static String HOST = System.getenv("ZELEO_HOST");
Now that we have all the ingredients, we’ll show you how to generat and decrypt your JWT tokens.
JWT Encryption
JWT, or the JSON Web Token is a relatively new way to secure REST endpoints, but is becoming more popular. As you can see on this page, there are libraries for practically all languages, though we will use Java for our examples here.
The main thing you will need to know when creating or decrypting the JSON tokens is we have decided to put the payload inside the JWT token for a little extra security. When you create the token to fire an event, you will want to add that JSON document to the Claims of the JWT token with the key of json.
Also of note, the token generated by our server is Base64 encoded. We also are using AES128 encryption for our JWT token.
This is where it necessarily will become a bit language specific. We will add some examples in other languages to our blog as we write them, but this is possible in any language and stack. This example uses the jose4j library for JWT in Java.
This is a functon we use to create a JSON token to fire an event.
/**
* Generates a JWT token for use by the Zeleo platform.
*
* @param json The JSON string we want to send to Zeleo.
* @return JWT token
*
* @throws JoseException Thrown if there are any failures during generation.
*/
public String generateJWTToken(final String json) throws JoseException {
final Key key = new AesKey(Base64.decode(SERVER_KEY));
final Map<String, List<String>> claimsMap = new HashMap<>();
claimsMap.put("json", Arrays.asList(json));
final JwtClaims claims = new JwtClaims();
claims.setIssuer(ISSUER);
claims.setAudience(AUDIENCE);
claims.setExpirationTimeMinutesInTheFuture(CLOCK_SKEW);
claims.setGeneratedJwtId();
claims.setIssuedAtToNow();
claims.setNotBeforeMinutesInThePast(2);
claims.setSubject(SUBJECT);
// Each claims key can point to a List of claims. We want to flatten out the object.
claimsMap.keySet().stream().forEach(k -> claims.setStringListClaim(k, claimsMap.get(k)));
final JsonWebEncryption jwe = new JsonWebEncryption();
jwe.setPayload(claims.toJson());
jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.A128KW);
jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256);
jwe.setKey(key);
return jwe.getCompactSerialization();
}
JWT Decryption
If your service is accepting any Consequences from Zeleo, you will need to be able to decrypt a JWT token as well from your WebHook endpoints. It’s pretty much the same deal as encryption, only in reverse.
First, make sure you have the fields set up from the Security Fields section of this document. Given you have those values, you need to use your library (again using jose4j on Java in this example) to decrypt the JWT token and grab from the JWT Claims the object with the key json. Again, we are using AES128 encryption for our JWT token.
/**
* Extract the data from a JWT Token.
*
* @param token The token to extract.
* @return The extracted json from the token.
*
* @throws InvalidJwtException If the JWT token is invalid.
*/
private String extractJWTToken(final String token) throws InvalidJwtException {
final Key key = new AesKey(Base64.decode(SERVER_KEY));
final JwtClaims claims;
final JwtConsumer jwtConsumer = new JwtConsumerBuilder()
.setRequireExpirationTime()
.setAllowedClockSkewInSeconds(CLOCK_SKEW)
.setExpectedIssuer(ISSUER)
.setExpectedAudience(AUDIENCE)
.setExpectedSubject(SUBJECT)
.setDecryptionKey(key)
.setEnableRequireEncryption()
.setDisableRequireSignature()
.setSkipSignatureVerification()
.build();
claims = jwtConsumer.processToClaims(token);
final Map<String, List<Object>> claimsMap = claims.flattenClaims();
final String json = claimsMap.get("json").get(0).toString();
return json;
}
And that’s really it. When you receive anything from Zeleo, the token will be in the body of the POST and you decrypt it using the fields specified.
Conclusion
This is the most complex part of integrating with Zeleo- congrats! With this security in place, it’s really just a matter of sending, parsing, and receiving JSON documents. We’ll get into those details in the next few pages.