Verifying JWT with JWKS uri throws a 500

jbryant
Participant I

When verifying an auth token using the JWKS uri as the public key, I receive a 500 internal error:

{
    "fault": {
        "faultstring": "NullPointerException",
        "detail": {
            "errorcode": "Internal Server Error"
        }
    }
}

The auth token is coming in via the authorization header.

My VerifyJWS policy looks like:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<VerifyJWS name="JWS-VerifyIdaToken" enabled="true">
    <DisplayName>JWS-VerifyIdaToken</DisplayName>
    <Algorithm>RS256</Algorithm>
    <PublicKey>
        <JWKS uri="https:/jwks_uri/jwks"/>
    </PublicKey>
    <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
</VerifyJWS>

My trace output for the VerifyJWS policy step clearly show it's able to pull the required properties from the looks like:

Variables Read and Assigned

jws.JWS-VerifyIdaToken.valid false
jws.JWS-VerifyIdaToken.header-json
jws.JWS-VerifyIdaToken.header-claim-names [x5t, kid, typ, alg]
jws.JWS-VerifyIdaToken.decoded.header.x5t BrfGYpn6HU1Uc6PwDue94PM3rgA
jws.JWS-VerifyIdaToken.decoded.header.kid gou8yAJLiQVa0eoEjcl4Y2aXlsgQrKsOnyiNILUgZiQ
jws.JWS-VerifyIdaToken.decoded.header.typ
jws.JWS-VerifyIdaToken.decoded.header.alg RS256
JWS.failed true

Content-Type

application/json

Error Content -> Body

{"fault":{"faultstring":"NullPointerException","detail":{"errorcode":"Internal Server Error"}}}

action ABORT
stepDefinition-async false
internal false
stepDefinition-type JWS
type VerifyJWSStepExecution
enforcement request
stepDefinition-continueOnError false
stepDefinition-displayName JWS-VerifyIdaToken
stepDefinition-name JWS-VerifyIdaToken
stepDefinition-enabled true
result false
error null
type ErrorPoint
state PROXY_REQ_FLOW
error.class java.lang.NullPointerException
Identifier fault

I've been able to verify the token using other online services such as https://jwt.davetonge.co.uk/

Solved Solved
2 6 1,164
1 ACCEPTED SOLUTION

This is possibly a bug with <JWKS uri="https:/jwks_uri/jwks"/> where it is unable to retrieve the JWKS if passed as a URI.

Please confirm that if you pass in the JWKS as a ref <JWKS ref="public.jwks"/> (use a ServiceCallout policy to grab the JWKS before the ValidateJWS) works?

View solution in original post

6 REPLIES 6

This is possibly a bug with <JWKS uri="https:/jwks_uri/jwks"/> where it is unable to retrieve the JWKS if passed as a URI.

Please confirm that if you pass in the JWKS as a ref <JWKS ref="public.jwks"/> (use a ServiceCallout policy to grab the JWKS before the ValidateJWS) works?

Hi Joey,

Yes, using a service callout to fetch the jwks payload works fine.

For posterity..

My ServiceCallout policy:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ServiceCallout async="false" continueOnError="false" enabled="true" name="SC-FetchJwks">
    <DisplayName>SC-FetchJwks</DisplayName>
    <Properties/>
    <Request clearPayload="true" variable="jwksRequest">
        <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
    </Request>
    <Response>jwksResponse</Response>
    <HTTPTargetConnection>
        <Properties/>
        <URL>http://jwks_uri/jwks</URL>
    </HTTPTargetConnection>
</ServiceCallout>

My VerifyJWS policy:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<VerifyJWS name="JWS-VerifyToken">
    <DisplayName>JWS-VerifyToken</DisplayName>
    <Algorithm>RS256</Algorithm>
    <PublicKey>
        <JWKS ref="jwksResponse.content"/>
    </PublicKey>
    <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
    <KnownHeaders/>
    <IgnoreCriticalHeaders/>
</VerifyJWS>

Thanks for the example code.
The relevant bug is b/139360107.

Just a further suggestion: it's probably a good idea to cache the result of the SC-FetchJwks for maybe 30 minutes or so. Rather than invoking the ServiceCallout for every time you verify a token, you'd use the same JWKS data, from cache. That means the sequence would not be:

  • ServiceCallout - to fetch JWKS
  • VerifyJWT (specifying the jwksResponse.content for the JWKS data)

But instead it would be:

  • LookupCache - to see if JWKS response is present
  • ServiceCallout - conditional, to retrieve jwksResponse if cache is cold
  • PopulateCache - conditional, to set the cache
  • LookupCache - conditional, to retrieve the just cached data into a context variable
  • VerifyJWT

Thanks Dino. I'll add to that ticket.

The bug seems to have been resolved. However, if I try to use a variable in the address,

<JWKS uri="https://{oam_env}/connect/jwk_uri"/>

to dynamically change the destination based on the Apigee deployment environment, still it returns a 500 error

{
    "fault": {
        "faultstring": "NullPointerException",
        "detail": {
            "errorcode": "Internal Server Error"
        }
    }
}

Am I doing something wrong or is it not allowed to use the variables in this case?

Thanks for your help.

The bug seems to have been resolved.

Glad to hear it.

Am I doing something wrong

Yes

or is it not allowed to use the variables in this case?

yes, it is not allowed.

There is a SEPARATE feature enhancement which will allow you to specify a variable that holds the URI. It won't be a message template, but it will be a variable, like (uriref='variablename'). ref: b/139642475. I don't have an estimate on availability of that feature. For now you need to use hard-coded URIs.

And also, getting a fault that says "NullPointerException" seems to also be a bug in Apigee. It shouldn't do that. It should instead just fail to resolve the host. I think maybe you will still get a 500 error, but, it shouldn't give you a lame message like "NullPointerException" . Thanks for reporting that. r/159341213