0
votes

A phone client sends me an access_token acquired from Active Directory B2C in Azure. I have a server-side Spring Boot Application and want to check if the token's source is the Active Directory from Azure and was not manipulated by hand and it is valid.

I am expecting something like

        String validationUrl = "https://azure.microsoft/validate-token";
        // put token in header
        RestTemplate restTemplate = new RestTemplate();
        HttpEntity<String> entity = new HttpEntity<>(null, headers);
        String result = restTemplate.exchange(validationUrl, HttpMethod.GET,
                entity, String.class).getBody();

I tried implementation similar to :

        try {
            provider = new UrlJwkProvider(new URL("https://login.microsoftonline.com/common/.well-known/openid-configuration"));
            jwk = provider.get(jwt.getKeyId());
            algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null);
            algorithm.verify(jwt);// if the token signature is invalid, the method will throw SignatureVerificationException
            return true;
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (JwkException e) {
            e.printStackTrace();
        } catch (SignatureVerificationException e) {
            System.out.println(e.getMessage());
        }

The result was always com.auth0.jwk.SigningKeyNotFoundException: No keys found in

Then I tried another implementation :

  HttpsJwks httpsJkws = new HttpsJwks(url);
        HttpsJwksVerificationKeyResolver httpsJwksKeyResolver = new HttpsJwksVerificationKeyResolver(httpsJkws);
        JwtConsumer jwtConsumer = new JwtConsumerBuilder()
                .setRequireExpirationTime() // the JWT must have an expiration time
                .setAllowedClockSkewInSeconds(3600) // allow some leeway in validating time based claims to account for clock skew
                .setVerificationKeyResolver(httpsJwksKeyResolver)
                .setRelaxVerificationKeyValidation()
                .build();

        try
        {
            //  Validate the JWT and process it to the Claims
            JwtClaims jwtClaims = jwtConsumer.processToClaims(accessToken);
            System.out.println("JWT validation succeeded! " + jwtClaims);
            return true;
        }
        catch (InvalidJwtException e)
        {
            // InvalidJwtException will be thrown, if the JWT failed processing or validation in anyway.
            // Hopefully with meaningful explanations(s) about what went wrong.
            System.out.println("Invalid JWT! " + e);
        }

The result was always Invalid JWT! org.jose4j.jwt.consumer.InvalidJwtException: JWT processing failed. Additional details: [[17] Unable to process JOSE object (cause: org.jose4j.lang.UnresolvableKeyException: Unable to find a suitable verification key for JWS w/ header

I have tried also following URLs: https://login.microsoftonline.com/MY_TENANT/.well-known/openid-configuration

https://login.microsoftonline.com/MY_TENANT/discovery/v2.0/keys?appid=APP_ID

https://login.microsoftonline.com/MY_TENANT/discovery/v2.0/keys

https://login.microsoftonline.com/common/.well-known/openid-configuration

https://login.microsoftonline.com/discovery/v2.0/keys

1
Did you try to follow the steps done in the other language? Here is the same problem, widely explained for c# with code samples: stackoverflow.com/a/39870281/1595293Luke Duda

1 Answers

1
votes

The access_token of Azure AD is a JSON Web Token(JWT). So you could validate JWT tokens using JWKS in Java. jwks_uri can be acquired by the link that formed as /.well-known/openid-configuration, see here.

enter image description here

DecodedJWT jwt = JWT.decode(token);
JwkProvider provider = provider = new UrlJwkProvider("https://login.microsoftonline.com/common/discovery/keys");
Jwk jwk = provider.get(jwt.getKeyId());
Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null);
algorithm.verify(jwt);

You could test your token online first, https://jwt.io/.