2

I am trying to generate an HS256 signed JWT token via apex. I am using this token in the Authy registration process to enable 2FA for community users.

Per the authy documentation HS256 is the only algorithm supported for the registration flow i'm using.

I did a little digging and I found a JWT example apex class on github. I modified it to support the context object defined in the authy api documentation. The token seems to generate just fine but when I go to validate the signature it fails validation.

Here is the code I am using and how I am invoking it:

public class Authy_JWT {

public String alg {get;set;}
public String iss {get;set;}
public String exp {get;set;}
public String app_id {get;set;}
public String user_id {get;set;}
public String iat {get;set;}
public Map<String,String> claims {get;set;}
public Integer validFor {get;set;}
public String cert {get;set;}
public String pkcs8 {get;set;}
public String privateKey {get;set;}


public static final String HS256 = 'HS256';
public static final String RS256 = 'RS256';
public static final String NONE = 'none';


public Authy_JWT(String alg) {
    this.alg = alg;
    this.validFor = 300;
}


public String issue() {

    String jwt = '';

    JSONGenerator header = JSON.createGenerator(false);
    header.writeStartObject();
    header.writeStringField('alg', this.alg);
    header.writeEndObject();
    String encodedHeader = base64URLencode(Blob.valueOf(header.getAsString()));

    JSONGenerator body = JSON.createGenerator(false);
    body.writeStartObject();
    Long rightNow = (dateTime.now().getTime()/1000)+1;
    body.writeStringField('iss', this.iss);
    body.writeNumberField('iat', rightNow);
    body.writeNumberField('exp', (rightNow + validFor));
    if (claims != null) {
        for (String claim : claims.keySet()) {
            body.writeStringField(claim, claims.get(claim));
        }
    }
    body.writeFieldName('context');
    body.writeStartObject();
    body.writeStringField('custom_user_id', user_id);
    body.writeStringField('authy_app_id', app_id);
    body.writeEndObject();
    body.writeEndObject();
    system.debug('body ' + body.getAsString());
    jwt = encodedHeader + '.' + base64URLencode(Blob.valueOf(body.getAsString()));

    if ( this.alg == HS256 ) {
        Blob key = EncodingUtil.base64Decode(privateKey);
        Blob signature = Crypto.generateMac('HmacSHA256',Blob.valueof(jwt),key);
        jwt += '.' + base64URLencode(signature);  
    } else if ( this.alg == RS256 ) {
        Blob signature = null;

        if (cert != null ) {
            signature = Crypto.signWithCertificate('rsa-sha256', Blob.valueOf(jwt), cert);
        } else {
            Blob privateKey = EncodingUtil.base64Decode(pkcs8);
            signature = Crypto.sign('rsa-sha256', Blob.valueOf(jwt), privateKey);
        }
        jwt += '.' + base64URLencode(signature);  
    } else if ( this.alg == NONE ) {
        jwt += '.';
    }

    return jwt;

}


public String base64URLencode(Blob input){ 
    String output = encodingUtil.base64Encode(input);
    output = output.replace('+', '-');
    output = output.replace('/', '_');
    while ( output.endsWith('=')){
        output = output.subString(0,output.length()-1);
    }
    return output;
}

And here is how I am issuing the token from this class:

authy_JWT jwt = new authy_JWT('HS256');
jwt.privateKey = 'my_private_key';
jwt.iss = 'Test';
jwt.app_id = 'my_app_id';
jwt.validfor = 999999999;
jwt.user_id = userinfo.getuserid();
string token = jwt.issue();

If I try to use this jwt token in the authy registration process I receive an error that it's not in an accepted format.

When I try to validate the token at https://jwt.io/ I see the message invalid signature but the header and payload look right. What am I missing?

0

Have you tried the Auth.JWT class?

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();
}

I've used them successful with Heroku and also Spring Boot (source).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.