API Gateway - JWT Authorizer - unable to decode "n" from RSA public key

0

I have trouble getting the JWT Authorizer on my API Gateway working. The API Gateway works fine, when I remove the authorizer, so the problem should be with the authorizer.

I am using OpenID token which I get from Cognito Identity GetOpenIdTokenForDeveloperIdentity request.

All the request just respond with the following (excerpt from curl -i output):

www-authenticate: Bearer scope="authenticated" error="invalid_token" error_description="unable to decode "n" from RSA public key"


{"message":"Unauthorized"}

As far as I understand the error_description complains about not being able to read the "n" parameter from here. But since I have no control over this, I'm unsure what I need to do to get this working.

1 Respuesta
1

Hi.

I'm not sure if that is the proper way for implementing a custom authorizer. We have implemented several custom authorizers for API Gateway and Cognito pool. All of them use the JWT library for this purpose. Please take a look at the following code in order to validate, decode and get claims for a Cognito token:

import java.util.Map;

import com.auth0.jwt.interfaces.Claim;

/**
 * Parses an AWS Cognito token, verifies it and returns claims found.
 *
 *
 */
public interface AwsCognitoTokenParser {
    Map<String, Claim> parse(String token, String tokenUse);
}



import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * {@link AwsCognitoTokenParser} implementation.
 *
 * @see https://stackoverflow.com/questions/43010211/verify-aws-id-token-on-java
 *
 *
 */
public class AwsCognitoTokenParserImpl implements AwsCognitoTokenParser {

    private static final Logger logger = LoggerFactory.getLogger(AwsCognitoTokenParserImpl.class);

    private final Algorithm algorithm;
    private final String iss;

    public AwsCognitoTokenParserImpl(final String awsRegion, final String awsCognitoPoolId) {
        this(new AwsCognitoRSAKeyProviderImpl(awsRegion, awsCognitoPoolId));
    }

    public AwsCognitoTokenParserImpl(final UrlRSAKeyProvider urlRsaKeyProvider) {
        algorithm = Algorithm.RSA256(urlRsaKeyProvider);
        iss = urlRsaKeyProvider.getIssuerUrl();
    }

    @Override
    public Map<String, Claim> parse(final String token, final String tokenUse) {
        logger.trace("Parsing token {} for token use {}. Expected issuer is {}...", token, tokenUse, iss);
        final JWTVerifier verifier = JWT.require(algorithm).withIssuer(iss).withClaim("token_use", tokenUse).build();
        final DecodedJWT jwt = verifier.verify(token);
        logger.trace("Parsed token. Claims are {}", jwt.getClaims());
        return jwt.getClaims();
    }
}



import com.auth0.jwt.interfaces.RSAKeyProvider;

/**
 * {@link RSAKeyProvider} specialization obtaining the keys from an url. The issuer url is exposed too.
 *
 *
 */
public interface UrlRSAKeyProvider extends RSAKeyProvider {
    String getIssuerUrl();

    String getKeysUrl();
}


import java.net.MalformedURLException;
import java.net.URL;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Optional;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.auth0.jwk.Jwk;
import com.auth0.jwk.JwkProvider;
import com.auth0.jwk.JwkProviderBuilder;

import cloud.plexo.neuron.customer.realtime.api.authorizer.exception.NeuronCustomerRealtimeApiAuthorizerException;

/**
 * RSAKeyProvider implementation for AWS Cognito keys.
 *
 * @see https://stackoverflow.com/questions/48356287/is-there-any-java-example-of-verification-of-jwt-for-aws-cognito-api
 *
 *
 */
public class AwsCognitoRSAKeyProviderImpl implements UrlRSAKeyProvider {

    private static final String COGNITO_POOL_KEYS_URL_TEMPLATE = "https://cognito-idp.%s.amazonaws.com/%s/.well-known/jwks.json";
    private static final String COGNITO_POOL_ISSUER_URL_TEMPLATE = "https://cognito-idp.%s.amazonaws.com/%s";

    private static final Logger logger = LoggerFactory.getLogger(AwsCognitoRSAKeyProviderImpl.class);

    private final String keysUrl;
    private final String issuerUrl;
    private final URL awsKidStoreUrl;

    private Optional<JwkProvider> jwkProvider = Optional.empty();

    public AwsCognitoRSAKeyProviderImpl(final String awsRegion, final String awsCognitoPoolId) {
        keysUrl = String.format(COGNITO_POOL_KEYS_URL_TEMPLATE, awsRegion, awsCognitoPoolId);
        issuerUrl = String.format(COGNITO_POOL_ISSUER_URL_TEMPLATE, awsRegion, awsCognitoPoolId);
        try {
            this.awsKidStoreUrl = new URL(keysUrl);

        } catch (final MalformedURLException e) {
            logger.error("Exception thrown constructing URL", e);
            throw new RuntimeException(String.format("Invalid URL provided, URL=%s", keysUrl));
        }
    }

    @Override
    public String getIssuerUrl() {
        return issuerUrl;
    }

    @Override
    public String getKeysUrl() {
        return keysUrl;
    }

    @Override
    public RSAPrivateKey getPrivateKey() {
        return null;
    }

    @Override
    public String getPrivateKeyId() {
        return null;
    }

    @Override
    public RSAPublicKey getPublicKeyById(final String kid) {
        try {
            if (!jwkProvider.isPresent()) {
                jwkProvider = Optional.of(new JwkProviderBuilder(awsKidStoreUrl).build());
            }
            final Jwk jwk = jwkProvider.get().get(kid);
            return (RSAPublicKey) jwk.getPublicKey();

        } catch (final Exception e) {
            logger.error("Exception thrown recovering JWT kid {} from store {}", kid, awsKidStoreUrl);
            throw new RuntimeException(
                    String.format("Failed to get JWT kid=%s from AWS kid store =%s", kid, awsKidStoreUrl));
        }
    }
}

Please, take a look at the referenced Stackoverflow post.

Hope this may help.

Best regards,

jgg
respondido hace 2 años

No has iniciado sesión. Iniciar sesión para publicar una respuesta.

Una buena respuesta responde claramente a la pregunta, proporciona comentarios constructivos y fomenta el crecimiento profesional en la persona que hace la pregunta.

Pautas para responder preguntas