JWKS key provider that fetches keys from an HTTP endpoint with ETS caching.
Fetches JSON Web Key Sets from the authorization server's JWKS URI, caches them in ETS, and auto-refreshes on cache miss or expiration.
Configuration
auth: [
strategy: :oauth,
key_provider: {ConduitMcp.OAuth.KeyProvider.JWKS,
jwks_uri: "https://auth.example.com/.well-known/jwks.json",
cache_ttl: :timer.hours(1)} # default: 1 hour
]The jwks_uri must use https. For local development against a
plain-HTTP authorization server, set allow_insecure_jwks: true in the
provider config.
Fetches use conservative HTTP settings: redirects are not followed,
requests time out (5s connect / 10s receive), and responses are capped
at 1MB. If a refresh fails and previously fetched keys are still cached,
those stale keys are served (with a logged warning) so a transient
authorization-server outage does not hard-fail all authentication —
bounded by :stale_max_age (default 24 hours), after which the provider
fails closed so revoked keys cannot validate tokens indefinitely.
Security considerations
The jwks_uri must be trusted operator config — never client-derived.
Fetches are hardened against SSRF abuse (https-only unless
allow_insecure_jwks, redirects disabled, 1MB body cap), but the URI
itself is not range-checked: a jwks_uri pointing at a private or
link-local address — e.g. the cloud metadata endpoint
http(s)://169.254.169.254/... — is fetched, not rejected. Set
jwks_uri from a configuration source you control. If it must come from a
less-trusted source, restrict outbound egress at the network layer (the
library deliberately does not block private ranges, since for most
deployments the JWKS endpoint is an internal/private host).
Revocation lag during an outage. While the JWKS endpoint is
unreachable, cached keys keep validating tokens until :stale_max_age
(default 24h), then the provider fails closed. The trade-off: a key
revoked during an outage can still validate tokens for up to
:stale_max_age. Lower it if your threat model needs faster revocation;
raise it to tolerate longer authorization-server outages.
Requirements
Requires the req package. Use 0.6.1 or newer: earlier versions carry
advisories this provider can reach, including unbounded decompression driven
by the response's content-type (GHSA — Req < 0.6.1). The 1MB cap below is
applied to the decoded body, so it does not protect against a compression
bomb on its own.
{:req, "~> 0.6"}