dotdigital Engagement Cloud

The dotdigital Engagement Cloud developer hub

Official dotdigital Engagement Cloud APIs documentation

Creating a JWT

A tutorial for creating a function or class that generates a JWT for authorising your app to receive push notifications from Engagement Cloud

Our mobile SDKs use JSON Web Tokens (JWT) to authenticate your app. These are a commonly used method for providing authentication and authorisation using one simple token.

In your code, you'll need to create a function that generates a valid JWT using the JWT authorisation details provided by Engagement Cloud in your push notification profile, and a cryptographic nonce which we pass. We will refer to this as the challenge function.

Required JWT Claims

Whichever provider or method you use to create a JWT, the JWT's claims must include the following with values provided in the Authentication fields in your push notification profile you setup in Engagement Cloud:

JWT Claim
Value

iss

The Issuer value in your Engagement Cloud push notification profile
e.g. https://api.comapi.com/defaultauth

aud

The Audience value in your Engagement Cloud push notification profile
e.g. https://api.comapi.com

sub

A unique id for the user, such as a GUID
e.g. f0cf444d-4237-4ece-9882-8e6ccc0a3b7d

nonce

The nonce we pass to your authentication function to prove it was issued by you

Signing the JWT

JWTs are digitally signed to prove it was issued by a trusted party. You must sign the JWT with the value of the Shared secret field from your push notification profile and the nonce we pass your function to prove it was issued by you.

Nonces

A cryptographic nonce must also be used by your JWT provider when creating the token to ensure that tokens always appear different even if they contain the same claims, this is a common security practice with cryptography. The SDK passes this nonce as an argument to your challenge function during initialisation of the SDK where you will create or retrieve a JWT for your app user.

Important security note

We recommend that you do not hard code your shared secret on the client side. Instead, this value should be stored on the server side.

Sample Code

The following example code demonstrates how to create a challenge function and a self issued JWT for both Android and iOS:

Android JWT code sample

JWT provider: Java JWT.

Any class that you create to generate a JWT token must extend the ComapiAuthenticator class.

Any logic that creates the JWT token must be inside the onAuthenticationChallenge() method.
This method takes two parameters: AuthClient and ChallengeOptions.

Get the value of the nonce by calling the ChallengeOptions.GetNonce() method

When your JWT token is returned, pass it to the authenticateWithToken() method of the AuthClient object.

package com.example.testapp;

import android.content.SharedPreferences;
import android.text.TextUtils;
import android.util.Base64;

import com.comapi.ComapiAuthenticator;
import com.comapi.internal.network.AuthClient;
import com.comapi.internal.network.ChallengeOptions;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

public class ChallengeHandler extends ComapiAuthenticator {


    @Override
    public void onAuthenticationChallenge(AuthClient authClient, ChallengeOptions challengeOptions) {

      try {

        byte[] data;
        //<Shared secret> string must be the same as the value of the 'Shared secret' field in your push notification profile in Engagement Cloud. 
        data = "<Shared secret>".getBytes("UTF-8");

        String base64Secret = Base64.encodeToString(data, Base64.DEFAULT);

        Map<String, Object> header = new HashMap<>();
        header.put("typ", "JWT");

        Map<String, Object> claims = new HashMap<>();
        claims.put("nonce", challengeOptions.getNonce());
        //<ID claim> string must be the same as the value of the 'ID claim' field in your push notification profile in Engagement Cloud.
        claims.put("sub", "<ID claim>");
        //<Audience> string must be the same as the value of the 'Audience' field in your push notification profile in Engagement Cloud.
        claims.put("aud", "<Audience>");
        //<Issuer> string must be the same as the value of the 'Issuer' field in your push notification profile in Engagement Cloud.
        claims.put("iss", "<Issuer>");
        claims.put("iat", System.currentTimeMillis());
        claims.put("exp", System.currentTimeMillis() + TimeUnit.DAYS.toMillis(30));

        final String token = Jwts.builder()
          .setHeader(header)
          .setClaims(claims)
          .signWith(SignatureAlgorithm.HS256, base64Secret)
          .compact();

        //Pass the JWT token to the SDK.
        authClient.authenticateWithToken(token);

      } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
        //Authorisation failed.
        authClient.authenticateWithToken(null);
      }
    }
}

iOS JWT code sample

Here's an example implementation of a token generator in Objective-C and Swift using JWT

#import "CMPAuthenticationManager.h"
#import <JWT/JWT.h>

@implementation CMPAuthenticationManager

+ (NSString *)generateTokenForNonce:(NSString *)nonce profileID:(NSString *)profileID issuer:(NSString *)issuer audience:(NSString *)audience secret:(NSString *)secret {
    NSDate *now = [NSDate date];
    NSDate *exp = [NSCalendar.currentCalendar dateByAddingUnit:NSCalendarUnitDay value:30 toDate:now options:0];
    
    NSDictionary *headers = @{@"typ" : @"JWT"};
    NSDictionary *payload = @{@"nonce" : nonce,
                               @"sub" : profileID,
                               @"iss" : issuer,
                               @"aud" : audience,
                               @"iat" : [NSNumber numberWithDouble:now.timeIntervalSince1970],
                               @"exp" : [NSNumber numberWithDouble:exp.timeIntervalSince1970]};
    
    NSData *secretData = [secret dataUsingEncoding:NSUTF8StringEncoding];
    id<JWTAlgorithm> algorithm = [JWTAlgorithmFactory algorithmByName:@"HS256"];
    
    NSString *token = [JWTBuilder encodePayload:payload].headers(headers).secretData(secretData).algorithm(algorithm).encode;
    return token;
}

@end
  
/* Note that this should preferably be generated by your backend, the app should only retreive the token through an HTTP call */
import JWT

class JWTokenGenerator {
    
    struct AuthHeaders {
        static let HeaderType = "JWT"
    }
    
    static func generate(tokenFor nonce: String, profileId: String, issuer: String, audience: String, secret: String) -> String {
        let now = Date()
        let exp = Calendar.current.date(byAdding: .day, value: 30, to: now)!
        
        let base64SecretKey = secret.data(using: .utf8)!
        
        let headers = ["typ" : NSString.init(string: AuthHeaders.HeaderType)] as [AnyHashable : Any]
        
        let claims = ["nonce" : NSString.init(string: nonce),
                      "sub" : NSString.init(string: profileId),
                      "iss" : NSString.init(string: issuer),
                      "aud" : NSString.init(string: audience),
                      "iat" : NSNumber(value: now.timeIntervalSince1970),
                      "exp" : NSNumber(value: exp.timeIntervalSince1970)] as [AnyHashable : Any]
        
        let algorithm = JWTAlgorithmFactory.algorithm(byName: "HS256")
        
        let e = JWTBuilder.encodePayload(claims)!
        
        let h = e.headers(headers)!
        let s = h.secretData(base64SecretKey)!
        let b = s.algorithm(algorithm)!
        let token = b.encode

        return token!
    }
}

/* Note that this should preferably be generated by your backend, the app should only retreive the token through an HTTP call */

JavaScript JWT code sample

JWT provider: jsrassign library.

Get the value of the nonce by using the nonce property of the first parameter.

When your JWT token is returned, pass it to the answerAuthenticationChallenge() function (second parameter of your challengeHandler() function).

function challengeHandler (options, answerAuthenticationChallenge) {
    // Header
    var oHeader = { alg: 'HS256', typ: 'JWT' };
    // Payload
    var tNow = KJUR.jws.IntDate.get('now');
    var tEnd = KJUR.jws.IntDate.get('now + 1day');
    var oPayload = {
      	//<ID claim> string must be the same as the value of the 'ID claim' field in your push notification profile in Engagement Cloud.
        sub: "<ID Claim>",
        nonce: options.nonce,
      	//<Audience> string must be the same as the value of the 'Audience' field in your push notification profile in Engagement Cloud.
        aud: "<Audience>",
      //<Issuer> string must be the same as the value of the 'Issuer' field in your push notification profile in Engagement Cloud.
        iss: "<Issuer>",
        iat: tNow,
        exp: tEnd,
    };
    var sHeader = JSON.stringify(oHeader);
    var sPayload = JSON.stringify(oPayload);
  	//<Shared secret> string must be the same as the value of the 'Shared secret' field in your push notification profile in Engagement Cloud. 
    var sJWT = KJUR.jws.JWS.sign("HS256", sHeader, sPayload, {utf8: "<Shared secret>"});
    answerAuthenticationChallenge(sJWT);
}

Updated 9 days ago

Creating a JWT


A tutorial for creating a function or class that generates a JWT for authorising your app to receive push notifications from Engagement Cloud

Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.