3
votes

i am using Cognito in Amazon to authenticate my mobile users, once they complete the login, Cognito provides a set of tokens, i am using the id token in my backend. I have followed the steps on the section Using ID Tokens and Access Tokens in your Web APIs on https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html i am stuck on the 6 step.

As far as i have seen, i get the modulus and the exponent from Amazon in String and i must build a PublicKey with those, to validate the JWT signature.

I dont know how to build the PublicKey using this two parameters in String.

2
What library are you using to validate JWT tokens? Post an example of the data provided by Amazon - pedrofb
I am using connect2id.com/products/nimbus-jose-jwt to process the jwt, the info contained in the token is explained in docs.aws.amazon.com/cognito/latest/developerguide/… - CarlosJavier
It would be more practical if you include the code or the information necessary to solve your question instead of a link to the documentation. Summarizing: AWS provides you a JWK key and you need a java PublicKey to verify JWT. Is that so? - pedrofb
@pedrofb by now the problem is only theorical as i have not idea how to implement the code, following the recommended library connect2id.com/products/nimbus-jose-jwt/examples/… i now i must do something like JWSVerifier verifier = new RSASSAVerifier((RSAPublicKey) publicKey); but i need to build the publicKey. Amazon provides me with modulus and exponent in encoded string, how do i build the public key using this parameters is what i dont know. - CarlosJavier

2 Answers

5
votes

I finally found a work around, there is an example in the aws forums https://forums.aws.amazon.com/message.jspa?messageID=728870 but the code is in Kotlin. I just port it to java and making some tests i finally validate my JWT signature:

byte[] decodedModulus = Base64.getUrlDecoder().decode(yourModulus);

byte[] decodedExponent = Base64.getUrlDecoder().decode(yourExponent);

BigInteger modulus = new BigInteger(1, decodedModulus);
BigInteger exponent = new BigInteger(1, decodedExponent);

RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(modulus, exponent);
KeyFactory keyFactory;

keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);

JWSVerifier verifier = new RSASSAVerifier((RSAPublicKey) publicKey);
Boolean verify = parsedToken.verify(verifier);}

Hope it helps to anyone with the same trouble.

2
votes

The way I have programmed is this,

    // Parse the Cognito Keys and get the key by kid
    // Key is just a class that is used for parsing JSON to POJO
    Key key = this.keyService.getKeyByKeyId(JWT.decode(token).getKeyId());

    // Use Key's N and E
    BigInteger modulus = new BigInteger(1, Base64.decodeBase64(key.getN()));
    BigInteger exponent = new BigInteger(1, Base64.decodeBase64(key.getE()));

    // Create a publick key
    PublicKey publicKey = null;
    try {
        publicKey = KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(modulus, exponent));
    } catch (InvalidKeySpecException e) {
        // Throw error
    } catch (NoSuchAlgorithmException e) {
        // Throw error
    }

    // get an algorithm instance
    Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) publicKey, null);

    // I verify ISS field of the token to make sure it's from the Cognito source
    String iss = String.format("https://cognito-idp.%s.amazonaws.com/%s", REGION, POOL_ID);

    JWTVerifier verifier = JWT.require(algorithm)
            .withIssuer(iss)
            .withClaim("token_use", "id") // make sure you're verifying id token
            .build();

    // Verify the token
    DecodedJWT jwt = verifier.verify(token);

    // Parse various fields
    String username = jwt.getClaim("sub").asString();
    String email = jwt.getClaim("email").asString();
    String phone = jwt.getClaim("phone_number").asString();
    String[] groups = jwt.getClaim("cognito:groups").asArray(String.class);

I am using this repo to verify and parsing of the tokens,

    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.4.1</version>
    </dependency>

Make sure you are importing the following,

    import com.auth0.jwt.JWT;
    import com.auth0.jwt.JWTVerifier;
    import com.auth0.jwt.algorithms.Algorithm;
    import com.auth0.jwt.exceptions.JWTDecodeException;
    import com.auth0.jwt.interfaces.DecodedJWT;

    import java.math.BigInteger;
    import java.security.KeyFactory;
    import java.security.NoSuchAlgorithmException;
    import java.security.PublicKey;
    import java.security.interfaces.RSAPublicKey;
    import java.security.spec.InvalidKeySpecException;
    import java.security.spec.RSAPublicKeySpec;
    import org.apache.commons.codec.binary.Base64;