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 Antwort
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
beantwortet vor 2 Jahren

Du bist nicht angemeldet. Anmelden um eine Antwort zu veröffentlichen.

Eine gute Antwort beantwortet die Frage klar, gibt konstruktives Feedback und fördert die berufliche Weiterentwicklung des Fragenstellers.

Richtlinien für die Beantwortung von Fragen