JS SDK Authentication / Server verifyAccessToken() Issue

Greetings,

I made a simple one-page web client using python -m SimpleHTTPServer 8000 to get familiar with the okta calls using the 1.6.0/okta-auth-js.min.js SDK. (I am not using the widget.) Implemented are: signIn(), signOut(), getUserInfo(), verifyIdToken(), an authenticated REST API call to my back end server, and an unauthenticated call. All okta calls seem to work as expected on the client.

I have an Express app running on the back end on port 3000 with @okta/jwt-verifier configured with the authenticationRequired() middleware.

My problem is that verifyAcccessToken() fails at the server with an error:

err:
name: JwtParseError
message: Error while resolving signing key for kid "–bunch-of-alpha-and-numeric-characters–"
innerError:
name: SigningKeyNotFoundError
message: Unable to find a signing key that matches “–bunch-of-alpha-and-numeric-characters–”

I would appreciate help resolving the error.

Here’s the relevant code.

The client (index.html):

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://ok1static.oktacdn.com/assets/js/sdk/okta-auth-js/1.6.0/okta-auth-js.min.js" type="text/javascript"></script>

<script type="text/javascript">
    var authClient = new OktaAuth({
        url: 'https://{mysubdomain}.oktapreview.com',
        clientId: '{myclientid}',
        redirectUri: 'http://localhost:8000/'
    });

    function signIn() {
        console.log('signIn()');
        authClient.signIn({
            username: '{testusername}',
            password: '{testpassword}'
        })
        .then(function(transaction) {
            // If successful, save the access and ID tokens in the tokenManager for easy retrieval later.
            if (transaction.status === 'SUCCESS') {
                console.log('transaction.sessionToken => ' + transaction.sessionToken);
                authClient.token.getWithoutPrompt({
                    responseType: ['token','id_token'], 
                    sessionToken: transaction.sessionToken
                })
                .then(function(tokens) {
                    authClient.tokenManager.add('accessToken',tokens[0]);
                    authClient.tokenManager.add('idToken',tokens[1]);
                })
                .catch(function(err) {
                    // handle OAuthError
                });
            } else {
                throw 'We cannot handle the ' + transaction.status + ' status';
            }
        })
        .fail(function(err) {
            console.error(err);
        });
    }

    function listCatalog() {
        console.log('listCatalog()');
        var accessToken = authClient.tokenManager.get('accessToken');
        if (!accessToken) {
            console.log('Could not find an access token.');
            return;
        }
        $.ajax({
            url: 'http://localhost:3000/api/list',
            headers: {
                Authorization : 'Bearer ' + accessToken.accessToken
            },
            success: function(response) {
                // Received messages!
                console.log('Messages', response);
            },
            error: function(response) {
                console.error(response);
            }
        });
    }
</script>

<body>
<a href="javascript:signIn()"        id="signin">Sign In</a><br>
<a href="javascript:listCatalog()" id="loadlists">Load Lists</a><br>
</body>

The server (Express):

const oktaJwtVerifier = new OktaJwtVerifier({
    issuer: 'https://{mysubdomain}.oktapreview.com/oauth2/default',
    assertClaims: {
        aud: 'api://default',
    },
});

function authenticationRequired(req, res, next) {
    const authHeader = req.headers.authorization || '';
    const match = authHeader.match(/Bearer (.+)/);

    if (!match) {
        return res.status(401).end();
    }

    const accessToken = match[1];

    // Here is where we fail:
    return oktaJwtVerifier.verifyAccessToken(accessToken)
        .then((jwt) => {
            req.jwt = jwt;
            next();
        })
        .catch((err) => {
            // This is where the error I reported is thrown.
            res.status(401).send(err.message);
        });
}

The middleware gets called as expected and the token is successfully parsed from the authorization header.

–Ray

You’ve correctly set the issuer to default on the server side, but not on the client side. This fails for a subtle reason: the token the client gets is not from the same authorization server, so the signature check fails.

Try adding this to your client code:

var authClient = new OktaAuth({
    url: 'https://{mysubdomain}.oktapreview.com',
    issuer: 'default', // <-- add this!
    clientId: ...
});
2 Likes

Thanks Nate. That solved it!

I specified the fully qualified issuer to match the server:

https://{mysubdomain}.oktapreview.com/oauth2/default

I also found this other thread that covers this in Invalid signature in Access and Id Token .

–Ray

2 Likes

Glad you got it working Ray!

{“name”:“JwtParseError”,“userMessage”:"Error while resolving signing key for kid \

If I use developer default authz server than I am able to successfully validate access token once I use my company okta authz server than getting signing key for kid error.

I searched a lot but couldn’t find right solution yet…

I am using postman as a client

Hi @lnrajput,

If you are using Okta org authorization server to mint the access token (issuer like https://oktaorg.com), then you may not be able to validate the access token locally in your application. This is a limitation as the tokens minted by okta org authorization server are supposed to be used against okta apis like /userinfo.
You could check this by taking a look at the ‘iss’ claim in the token.

For tokens minted by okta org authorization server, you could use /introspect
endpoint for remote token validation.