hasura / hasura/graphql-engine

Attempt to gracefully refresh JWKs on invalid signature error

Open
#9,759 1 comment 6 reactions 0 assignees View on GitHub
k/enhancement
Dominant language
TypeScript
Stars
32.1k
Forks
3k
PR merge metrics
PR metrics pending

Description

### Is your proposal related to a problem?

Running into a caching issue with the JWKs URL for our tech stack. We have a NextJS / Hasura app deployed by Vercel and using SuperTokens for auth. The signing keys rotate at a configurable interval, but we don't currently have any insight into exactly when it happens (for example via a `Cache-Control: max-age` header or a notification event).

We are able to either have Hasura reach out to the SuperTokens JWKs endpoint directly, or have it proxy through an exposed auth endpoint on our NextJS server. Going through Next doesn't work well as described in the "alternatives" section below.

Having Hasura reach out to SuperTokens directly for the JWKs could work, except that the JWKs endpoint doesn't currently set any of the [expected headers](https://hasura.io/docs/latest/auth/authentication/jwt/#rotating-jwks), so Hasura won't refresh JWKs.

### Describe the solution you'd like

I'd like to propose a "graceful refresh" if a new key is encountered in an access token. So, if during JWT validation Hasura encounters a new signing key (current `JWSInvalidSignature` error), it could reach out to the `jwks_url` again to see if a new key has been issued. If so, it can update its cache and validate the token against that key. If not, it would obviously return that `JWSInvalidSignature` error as it does now.

This would really help our auth flow with this Next / Vercel / Hasura / SuperTokens tech stack. It also feels like an elegant way to fail forward and handle an error as it comes up in general for any stack.

### Describe alternatives you've considered

We originally pointed the `jwk_url` to the JWKs endpoint exposed by our NextJS server. However, Vercel automatically caches responses, so we needed to override any auth endpoints with a `max-age: 0` to avoid caching those responses. Setting `max-age=0` on the JWKs endpoint caused Hasura to refresh the JWKs [once per second](https://hasura.io/docs/latest/auth/authentication/jwt/#rotating-jwks) which was noisy and expensive.

Setting a longer interval like 1 hour helped with Vercel costs, but meant we ran into a `JWSInvalidSignature` error during key rotation that Hasura would be unable to resolve. Any caching would need to happen on the Hasura side. I have opened [a GitHub issue with our auth provider](https://github.com/supertokens/supertokens-node/issues/617) to see about adding a valid `Cache-Control: max-age` header to their JWKs endpoint response to take advantage of Hasura's current refresh behavior.

Alternatively, we could use a static PEM key and manually rotate that periodically ourselves. We'll go this route if needed, but would prefer to take advantage of automatic key rotation & refresh if possible!

Contributor guide

Open the contributing guide

Research direction

Start at the JWT validation path that raises JWSInvalidSignature and trace how jwks_url is cached and refreshed. Compare the existing refresh behavior with the proposed retry path; done means a newly issued signing key can be fetched and used for validation while an unavailable key still returns the existing error.

Written by the indexing model from the issue text.

Assessment

Tech stack
nextjs
Domain
api, authentication, backend
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.