JWKS key sources now fetched and cached automatically

n8n gains the ability to fetch signing keys directly from external identity provider JWKS endpoints, with automatic caching based on server hints.
n8n instances configured to trust keys from external identity providers previously hit a wall: JWKS-type key sources were recognized but silently skipped during refresh cycles, leaving the trusted_key table empty despite the configuration. This PR removes that limitation by adding a dedicated resolver that fetches remote JWKS endpoints, validates the signing keys within, converts them to PEM-encoded material for database storage, and respects the cache expiry hints sent by the identity provider.
When a JWKS source is encountered during refresh, the new resolver retrieves the JWK Set from the configured URL, filters out encryption-only keys, symmetric keys, and anything without a proper key ID. For keys that pass validation, the algorithm is inferred from the key type and curve when not explicitly declared — RSA becomes RS256, P-256 curves map to ES256, and so on. Each valid key is converted to standard PEM format and stored alongside its issuer, audience, and role metadata.
The observed cache TTL is computed from three sources in order of priority: the Cache-Control max-age header from the JWKS endpoint, the configured cacheTtlSeconds on the source itself, or a default of 3600 seconds. This value is clamped between 60 seconds and 24 hours, then persisted back to the source configuration so the next refresh cycle respects the identity provider's guidance on how long those keys remain valid.
This work completes Phase 2a of the OAuth 2.0 Token Exchange initiative, which began with database infrastructure for trusted keys and continued with a DB-backed service featuring leader refresh. The JWKS resolver bridges external key sources and the internal TrustedKeyData format used by the trusted_key table.
View Original GitHub Description
Summary
Adds a JwksResolverService that fetches JWKS endpoints, validates and filters signing keys, converts them to PEM-encoded key material, and determines cache TTL from Cache-Control headers. This is the bridge between external identity provider JWKS URLs and n8n's internal TrustedKeyData format used by the trusted_key table.
This is part of the OAuth 2.0 Token Exchange initiative (Phase 2a — Embed flow). The previous PRs added the DB infrastructure for trusted keys (#28097) and the DB-backed TrustedKeyService with leader refresh (#28136). This PR implements the JWKS resolution layer so that jwks-type key sources are no longer skipped during refresh — they now fetch, parse, and persist keys from remote JWKS endpoints.
Key implementation decisions
JwksResolverServiceis a standalone@n8n/diservice with aresolveKeys()method that accepts aJwksKeySourceconfig and returns resolved keys + TTL + skipped key diagnostics.- Algorithm inference: When a JWK lacks an explicit
algfield, the service infers it fromkty/crv(RSA → RS256, EC P-256 → ES256, EC P-384 → ES384, EC P-521 → ES512, OKP Ed25519 → EdDSA). - Key filtering: Skips keys without
kid, encryption-only keys (use: "enc"), symmetric keys (kty: "oct"), and keys with unsupported algorithms. Returns detailed skip diagnostics for observability. - TTL computation:
Cache-Control max-age>source.cacheTtlSeconds> default (3600s), clamped to [60s, 24h]. The observed TTL is persisted back to the source config so refresh scheduling respects it. TrustedKeyServiceintegration:resolveKeysForSource()is now async, dispatches toresolveKeysForJwksSource()for JWKS sources, and persists the observedcacheTtlSecondson the source entity for future refresh interval computation.JwtAlgorithmSchemaexported fromtoken-exchange.schemas.tsso the resolver can validate algorithms against the canonical set.- Removed the old integration test that asserted JWKS sources are skipped (they are now fully supported).
How to test manually
To test end-to-end with a real JWKS endpoint (requires a running instance):
- Configure a
jwks-type trusted key source pointing to an IdP's/.well-known/jwks.json
For example:
export N8N_ENV_FEAT_TOKEN_EXCHANGE=true
export N8N_TOKEN_EXCHANGE_TRUSTED_KEYS='[{"type": "jwks","url": "https://dev-3nz2ealgrt07hg24.eu.auth0.com/.well-known/jwks.json","issuer": "https://dev-3nz2ealgrt07hg24.eu.auth0.com"}]'
- Start n8n with the give envvars
- Verify keys appear in the
trusted_keytable with correct PEM material and algorithm and the jwks source in thetrusted_key_sourcetable is correct.
Related tickets
closes https://linear.app/n8n/issue/IAM-518 closes
Review checklist
- PR title and summary are descriptive. (conventions)
- Docs updated or follow-up ticket created.
- Tests included.
- I have seen this code, I have run this code, and I take responsibility for this code.