+ Start a Discussion
Helen He 7Helen He 7 

InvalidJwtException while validating JWT token generated in salesforce

Hi All,
 
We are going to use Salesforce as IdP and Weblogic as SP which hosts our existing java web apps, thinking about OAuth 2.0 JWT Bearer Token Flow for the SSO.
 
Basically user logs into our customized community, clicks a link which redirects user to external web apps via http request, a bearer token with extra claims is set in the authorization header. Our java web doesn’t need to talk back to salesforce, it just needs to validate the JWT token for the authentication.

I creates a connected app with digital signature, create JWT token with extra claims using apex Auth.JWT, then I verify the JWT using jose4j in java, but get the following error while validating the JWT in java, I use https://login.salesforce.com/id/keys as the key resolver since I use my developer account to create the JWT token

org.jose4j.jwt.consumer.InvalidJwtException: Unable to process JOSE object (cause: org.jose4j.lang.UnresolvableKeyException: Unable to find a suitable verification key for JWS 

Any input is appreciated.

Here are links I followed for implementation
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_class_Auth_JWTBearerTokenExchange.htm
https://help.salesforce.com/articleView?id=remoteaccess_asset_token_using_validating.htm&type=5

And here is my codes
Apex code for generating JWT
Auth.JWT jwt = new Auth.JWT();
        jwt.setSub('XX@XXX.XXX'); 
        jwt.setAud('https://login.salesforce.com'); 
        jwt.setIss('3MVG9KsVczVNcM8y.FPNyZ.BU9I1hnzFYR1VBtqxIyA2mJoJ8zsHwzEE8GkytJwXWhSTwulBu14ecCMp3XV2q');
        
        //Additional claims to set scope
        Map<String, Object> claims = new Map<String, Object>();
        claims.put('scope', 'scope name');
            
        jwt.setAdditionalClaims(claims);

        //Create the object that signs the JWT bearer token
        Auth.JWS jws = new Auth.JWS(jwt, 'SelfSignedCert_02Feb2019_210623');
        
        //Get the resulting JWS in case debugging is required
        String token = jws.getCompactSerialization();


Generated JWT looks like this:
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlNlbGZTaWduZWRDZXJ0XzAyRmViMjAxOV8yMTA2MjMifQ.eyJpc3MiOiIzTVZHOUtzVmN6Vk5jTTh5LkZQTnlaLkJVOUkxaG56RllSMVZCdHF4SXlBMm1Kb0o4enNId3pFRThHa3l0SndYV2hTVHd1bEJ1MTRlY0NNcDNYVjJxIiwic3ViIjoiemhlQG5ibWUub3JnIiwiYXVkIjoiaHR0cHM6Ly9sb2dpbi5zYWxlc2ZvcmNlLmNvbSIsImlhdCI6MTU1NTYwMzc4NywibmJmIjoxNTU1NjAzNzU3LCJleHAiOjE1NTU2MDQwODcsImp0aSI6IjJjZjg1ZTE5LTE1MjMtNDgyNS04MjEyLTcwNzQ2NTcwNDk1NSIsInNjb3BlIjoic2NvcGUgbmFtZSJ9.WU3QTDA5ixc1dG5fTYtdHKbkNoTglFYfu9XznzeqEAq6w44db

Java code for validation
final String ISSUER = "https://login.salesforce.com";
        final String KEY_ENDPOINT = ISSUER + "/id/keys";
        final String AUDIENCE = "https://login.salesforce.com";

        boolean isValidAssetToken = false;
        String assetToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlNlbGZTaWduZWRDZXJ0XzAyRmViMjAxOV8yMTA2MjMifQ.eyJpc3MiOiIzTVZHOUtzVmN6Vk5jTTh5LkZQTnlaLkJVOUkxaG56RllSMVZCdHF4SXlBMm1Kb0o4enNId3pFRThHa3l0SndYV2hTVHd1bEJ1MTRlY0NNcDNYVjJxIiwic3ViIjoiemhlQG5ibWUub3JnIiwiYXVkIjoiaHR0cHM6Ly9sb2dpbi5zYWxlc2ZvcmNlLmNvbSIsImlhdCI6MTU1NTYwMzc4NywibmJmIjoxNTU1NjAzNzU3LCJleHAiOjE1NTU2MDQwODcsImp0aSI6IjJjZjg1ZTE5LTE1MjMtNDgyNS04MjEyLTcwNzQ2NTcwNDk1NSIsInNjb3BlIjoic2NvcGUgbmFtZSJ9.WU3QTDA5ixc1dG5fTYtdHKbkNoTglFYfu9XznzeqEAq6w44db";

        // The HttpsJwksVerificationKeyResolver uses JWKs obtained from the HttpsJwks and
        // selects the most appropriate one to use for verification based on the Key ID and other factors
        // provided in the header of the JWS/JWT.
        HttpsJwks httpsJkws = new HttpsJwks(KEY_ENDPOINT);
        HttpsJwksVerificationKeyResolver httpsJwksKeyResolver = new
                HttpsJwksVerificationKeyResolver(httpsJkws);

        // The JwtConsumer establishes the rules for Validation of our asset token.
        JwtConsumer jwtConsumer = new JwtConsumerBuilder()
                .setVerificationKeyResolver(httpsJwksKeyResolver)
                .setRequireExpirationTime() // The JWT must have an expiration time.
                .setAllowedClockSkewInSeconds(30) // Allow some leeway in validating time-based claims to account for clock skew.
                .setExpectedIssuer(ISSUER) // Entity that the asset token must be issued by.
                .setExpectedAudience(AUDIENCE) // Entity that the asset token is intended for.
                .build(); // Create the JwtConsumer instance.
        try {
            // Validate the JWT and process it to the Claims.
            JwtClaims jwtClaims = jwtConsumer.processToClaims(assetToken);
            isValidAssetToken = true;
        } catch (InvalidJwtException e) {
            // InvalidJwtException thrown if the asset token failed processing or validation.
            System.out.println("Invalid Asset Token: " + e);
        }

 
Jefferson JonesJefferson Jones
In the JWT bearer token exchange sequence, the client generates if the asset token failed processing or validation mybkexperience (https://mybkexperience.me).