Skip to main content

Module jwks

Module jwks 

Source
Expand description

Keys fetched from a JWKS URL (PGRST_JWT_JWKS_URL), and kept current.

An identity provider – Supabase Auth (GoTrue), Auth0, Clerk, Cognito, Keycloak – publishes the public keys its tokens are signed with as a JSON Web Key Set at a well-known URL, and rotates them: a new key is published, tokens start being signed with it, and the old one is withdrawn once no token signed by it can still be valid. A server that read the set once, at start-up, would refuse every token signed after the first rotation. So the set is fetched again, on a schedule, and whenever a token names a kid this server has never seen.

§Where the waiting happens

Verifying a token is synchronous (crate::validate_token), and stays so: the keys a request verifies against are a snapshot taken from JwtKeys, swapped whole when a fetch brings a new set. The only asynchronous step is Jwks::ensure_for_token (reached through crate::ensure_keys), which a host awaits before the synchronous check. It reads the token’s header, and if the snapshot already holds a key the header selects it returns at once without awaiting anything – the common case, every request but the few after a rotation. Only an unknown kid, or keys never loaded, or (on a host with no background task) keys past their refresh time, make it fetch.

§Fetching

The HTTP request itself is the host’s: Fetch is implemented with reqwest by the server and with the Workers Fetch API by the Worker, so this crate stays free of an HTTP client and builds for wasm32. What is fetched, and whether the answer is trusted, is decided here, once for every host:

  • HTTPS only, unless the host is loopback (localhost, 127.0.0.0/8, ::1) – a development IdP, or a sidecar. A key set fetched over plain HTTP is a key set anyone on the path can replace with their own, and then sign any token they like. Checked when the URL is configured, and again for every redirect.
  • No redirect to another host. The URL the operator configured names the party trusted to say which keys are valid; a redirect elsewhere is that party delegating trust in a way nobody reviewed. A redirect on the same host (/jwks to /jwks/, http to https on loopback) is followed, at most MAX_REDIRECTS times. The Fetch implementation must not follow redirects itself, so that this rule is the only one.
  • FETCH_TIMEOUT and MAX_JWKS_BYTES, enforced by the Fetch implementation, which is the only party that can stop reading: a JWKS endpoint that hangs or streams forever must not hold a request, or the refresh task, hostage. Real key sets are a few kilobytes.
  • The body is parsed with KeySet::from_jwk_json, exactly as a JWKS in PGRST_JWT_SECRET is. A response that is not a 200, or not a set this server can use, is a failed refresh.

§Failing

A failed refresh keeps the last good set and logs a warning: an identity provider having a bad minute must not log every user out. If no set has ever loaded, every token the JWKS would have verified is refused – fail closed – and Jwks::status says so, which the server’s readiness endpoints report as not ready. Start-up does not fail: an IdP outage that crash-looped every replica would turn a partial outage into a total one.

§Refreshing

A successful fetch is trusted for its Cache-Control: max-age, clamped to MIN_MAX_AGE..=MAX_MAX_AGE, or for the configured refresh interval (PGRST_JWT_JWKS_REFRESH, default DEFAULT_REFRESH) when the response says nothing. The provider knows how often it rotates; the clamp keeps a max-age=0 from turning into a fetch per request and a max-age of a year from making a withdrawn key valid for a year. A failed fetch is retried after 1 s, 2 s, 4 s… up to MAX_RETRY (and never later than the refresh interval).

A token whose kid is unknown triggers one fetch, at most once per UNKNOWN_KID_INTERVAL – anyone can mint a token with a random kid, and without the limit every such request would be a request to the identity provider. Requests that arrive while that fetch is in flight wait for it rather than each starting their own (natively; a Worker’s requests never wait on one another, see Jwks::exclusive); if the kid is still unknown afterwards the token is refused with PGRST301, as any token is whose key this server does not hold.

§With PGRST_JWT_SECRET

Both may be set, and a token is accepted if either verifies it: the keys in force are the union of the secret’s and the fetched set’s. That is the deployment where a backend mints HS256 service tokens with a shared secret while users arrive with tokens from the identity provider – Supabase’s own legacy service_role key is exactly that. Neither source can widen the other: a key still admits only its own algorithms, and a token naming a kid still selects only keys carrying that kid (the plain-string secret, which has no kid, has always accepted any, and still does).

Structs§

FetchError
A fetch or a refresh that failed, and why, for the log.
FetchResponse
What one GET answered.
Jwks
The keys at a JWKS URL, fetched and kept current. See the module documentation.
JwksBuilder
Builds a Jwks; see Jwks::builder.
JwksStatus
What a Jwks has done so far: the numbers a metric or a readiness check reads. A snapshot, taken by Jwks::status.
JwksUrlError
A JWKS URL that cannot be fetched from.
StoredJwks
A fetched set, as a JwksStore keeps it.

Constants§

DEFAULT_REFRESH
How long a fetched set is used without being fetched again, when neither the response nor PGRST_JWT_JWKS_REFRESH says otherwise: five minutes.
FETCH_TIMEOUT
How long one fetch may take, connecting and reading included.
MAX_JWKS_BYTES
The largest JWKS response read, in bytes: 1 MiB.
MAX_MAX_AGE
The longest Cache-Control: max-age honoured: a day.
MAX_REDIRECTS
How many same-host redirects are followed before the fetch is a failure.
MAX_RETRY
The longest wait before retrying a failed fetch.
MIN_MAX_AGE
The shortest Cache-Control: max-age honoured. Shorter, no-cache and no-store all mean this.
UNKNOWN_KID_INTERVAL
How often a token with an unknown kid may cause a fetch.

Traits§

Fetch
An HTTP client, as much of one as fetching a JWKS needs.
JwksStore
A copy of the fetched set somewhere other processes can read it.
MaybeSendSync
Send + Sync natively, nothing at all on wasm32, where a Worker’s JavaScript objects are neither and nothing runs on another thread anyway.

Functions§

check_url
Check a JWKS URL against the rules in the module documentation: absolute, https, or http to a loopback host.