3
votes

I'm using the Auth0 java-jwt library to generate JWT tokens, however, once generated I'm not able to verify the tokens.

This is the code I use to generate the token:

final JWTSigner signer = new JWTSigner(secret);
final HashMap<String, Object> claims = new HashMap<String, Object>();
claims.put("user", user);
claims.put("email", user.getEmail());
final String jwt = signer.sign(claims);
return jwt;

This is my secret and the token (it verifies correctly in https://jwt.io/):

secret:
sfnd984f94j3fjn

token: eyJ0eXBlIjoiSldUIiwiYWxnIjoiSFMyNTYifQ.eyJ1c2VyIjp7ImlkIjoyLCJlbWFpbCI6InNhbnRob3NoQHh5ei5jb20iLCJwYXNzd29yZCI6IiQyYSQxMCRHSlNNRGtRRUEvRVNsRENJcVlud0R1Ly45YWRqRWRQalVvSWVKUmlsSmpSeHh6N2s2Q01xQyIsImZpcnN0X25hbWUiOiJzYW50aG9zaCIsImxhc3RfbmFtZSI6Imt1bWFyIiwic3RhdHVzIjoxLCJ0aXRsZSI6IkFzc29jIiwicm9sZXMiOlt7ImlkIjoxLCJyb2xlIjoiVVNFUiJ9XX0sImVtYWlsIjoic2FudGhvc2hAeHl6LmNvbSJ9.0SHNCgUWOijpYv7xcNoPiCwg_OFZQnsdi5l7YhCsSjU

When I use the same JWT token to verify it using the Auth0 method it fails (always ends up with a signature exception):

try {           
    final JWTVerifier verifier = new JWTVerifier(secret);
    final Map<String, Object> claims= verifier.verify(jwt);
    final String email = (String)claims.get("email");
    user =  userService.loadUserByEmail(email);
} catch (UsernameNotFoundException e) {
    // Invalid Token
} catch (SignatureException e) {
    System.out.println(e.toString());
} catch (IOException e) { 
} catch (Exception e) { 
}

I debugged the library and I think this is where the issue is (in JWTVerifier class):

void verifySignature(String[] pieces, String algorithm) 
    throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {

    Mac hmac = Mac.getInstance(algorithm);
    hmac.init(new SecretKeySpec(decoder.decodeBase64(secret), algorithm));
    byte[] sig = hmac.doFinal(
        new StringBuilder(pieces[0]).append(".").append(pieces[1]).toString().getBytes());

    if (!Arrays.equals(sig, decoder.decodeBase64(pieces[2]))) {
        throw new SignatureException("signature verification failed");
    }
}

This looks wrong to me because the pieces[1] and pieces[0] are decoded with the hmac.doFinal where as the pieces[2] is just plain base64 decoded.

Is my assumption right. Is this a bug in the library or am I getting something wrong?

1

1 Answers

1
votes

The version of JWTVerifier being used assumes that the secret you pass is Base64url encoded so it automatically decodes it before using it as the key to validate the signature.

Given sfnd984f94j3fjn is the actual secret and your version of JWTVerifier automatically Base64url decodes whatever you pass it, you need to encode sfnd984f94j3fjn in Base64url and pass the encoded version to JWTVerifier.

Something similar to this:

import org.apache.commons.codec.binary.Base64;

// ...

Base64 encoder = new Base64(true);

encoder.encodeBase64("sfnd984f94j3fjn".getBytes());

You can see the automatic decoding of the secret happening on the following line of the version of JWTVerifier class you're using:

hmac.init(new SecretKeySpec(decoder.decodeBase64(secret), algorithm));

An update on this, the latest version of the library does not seem to assume a Base64url encoding.