vcima
vcima

Reputation: 463

Error validating Azure AD JWT access token

I have an Azure AD JWT token that is obtained using Msal library but when I try to validate this token something is wrong:

Client: A Sharepoint Web Part

const config = {
 auth: {
     clientId: "xxxxx",
     authority: "https://login.microsoftonline.com/yyyyyy"
 }
};

const myMSALObj = new UserAgentApplication(config);

let accessTokenRequest = {
 scopes: ["user.read"],
 loginHint: this.context.pageContext.user.loginName,
 extraQueryParameters: {domain_hint: 'organizations'}
}

myMSALObj.acquireTokenSilent(accessTokenRequest).then(
function(accessTokenResponse) { 
// Acquire token silent success 
let accessToken = accessTokenResponse.accessToken;

On the other hand I have a server app (Java) where the access token is validated

Validator:

<dependency>
  <groupId>com.microsoft.azure</groupId>
  <artifactId>azure-storage</artifactId>
  <version>8.6.2</version>
</dependency>

<dependency>
  <groupId>com.auth0</groupId>
  <artifactId>jwks-rsa</artifactId>
  <version>0.11.0</version>
</dependency>

Code

 String token="<your AD token>";
    DecodedJWT jwt = JWT.decode(token);
    System.out.println(jwt.getKeyId());

    JwkProvider provider = null;
    Jwk jwk =null;
    Algorithm algorithm=null;

    try {
       provider = new UrlJwkProvider(new URL("https://login.microsoftonline.com/common/discovery/keys"));
      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
     } catch (MalformedURLException e) {
         e.printStackTrace();
     } catch (JwkException e) {
        e.printStackTrace();
     }catch(SignatureVerificationException e){
       System.out.println(e.getMessage());
     }

My problem is that when I try to validate this token I obtained this error: The Token's Signature resulted invalid when verified using the Algorithm: SHA256withRSA

I'm stuck with this, If the token is right, Why I have this error?

Regards

Upvotes: 2

Views: 3031

Answers (2)

vcima
vcima

Reputation: 463

Finally, It works with something like this.

  1. To obtain the token (using adal in the Web Part):

        // Obtaining token provider
        let tp = await this.context.aadTokenProviderFactory.getTokenProvider();
        let config = tp["_defaultConfiguration"];
        let aadInstanceUrl = config.aadInstanceUrl[length - 1] === "/" ? config.aadInstanceUrl : config.aadInstanceUrl + "/";
    
        // Config context
        let ctx = new AuthenticationContext({
            tenant: tenantId,
            clientId: clientId,
            instance: aadInstanceUrl,
            redirectUri: config.redirectUri,
            extraQueryParameter: "login_hint=" + encodeURIComponent(loginName),
            loadFrameTimeout: 60000
        });
    
        // Check user
        let cu = ctx.getCachedUser();
    
        console.log("USER", cu, loginName, ctx);
        if (cu && cu.userName.toLowerCase() !== loginName.toLowerCase()) {
            console.log("Clean user cache");
            ctx.clearCache();
        }
    
        // Login process
        console.log("Login process");
    
        // Obtaining Azure AD Token
        let azureADToken = this.acquireToken(ctx, clientId);
    
  2. To validate the token:

    String token = "XXXXXX";
    
    DecodedJWT jwt = JWT.decode(token);
    System.out.println(jwt.getKeyId());
    
    JwkProvider provider = null;
    Jwk jwk = null;
    Algorithm algorithm = null;
    
    try {
        provider = new UrlJwkProvider(new URL("https://login.microsoftonline.com/common/discovery/keys"));
        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
    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (JwkException e) {
        e.printStackTrace();
    } catch (SignatureVerificationException e) {
    
        System.out.println(e.getMessage());
    
    }
    
    System.out.println("works!");
    

With this dependencies:

  <dependencies>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.1</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.1</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
            <version>0.11.1</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.10</version>
            <type>bundle</type>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.8</version>
            <type>bundle</type>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.9.10</version>
            <type>bundle</type>
        </dependency>
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.9.3</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-json-jackson</artifactId>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet</artifactId>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
        </dependency>
        <!-- JUNIT -->
        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>com.microsoft.azure</groupId>
            <artifactId>azure-storage</artifactId>
            <version>8.6.2</version>
        </dependency>

        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>jwks-rsa</artifactId>
            <version>0.11.0</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.10.2</version>
        </dependency>

    </dependencies>

Upvotes: 2

Tony Ju
Tony Ju

Reputation: 15639

I noticed that the scope is user.read which means the token is for Microsoft Graph API.

Please note:

If you're a client getting a token for Graph, assume that it's an encrypted string that you should never look at - sometimes it will be. We use a special token format for Graph that they know how to validate - you shouldn't be looking at access tokens if they're not for you.

You can use this access token to call Microsoft Graph API directly, if the token is wrong, you will get the response from Microsoft API server.

Reference:

https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/609#issuecomment-529537264

Upvotes: 2

Related Questions