Jwks unstable
JSON Web Key Sets and the Jwks service that resolves verification keys
from them.
A key is selected by algorithm and optional kid. It must fit the
algorithm's key family and curve, and its alg, use, and key_ops
members must allow verification with that algorithm. No match fails with
KeyNotFound and several matches with AmbiguousKey.
layerLocal serves a fixed key set. makeRemote and layerRemote fetch one from an HTTPS endpoint and cache it.
Constructors
Builds a Jwks service for a fixed key set.
Signature
declare function makeLocal(keySet: { readonly keys: readonly Array<{ readonly alg?: string; readonly d?: undefined; readonly dp?: undefined; readonly dq?: undefined; readonly e: string; readonly k?: undefined; readonly key_ops?: readonly Array<string>; readonly kid?: string; readonly kty: "RSA"; readonly n: string; readonly oth?: undefined; readonly p?: undefined; readonly q?: undefined; readonly qi?: undefined; readonly use?: string; } | { readonly alg?: string; readonly crv: "P-256" | "P-384" | "P-521"; readonly d?: undefined; readonly dp?: undefined; readonly dq?: undefined; readonly k?: undefined; readonly key_ops?: readonly Array<string>; readonly kid?: string; readonly kty: "EC"; readonly oth?: undefined; readonly p?: undefined; readonly q?: undefined; readonly qi?: undefined; readonly use?: string; readonly x: string; readonly y: string; } | { readonly alg?: string; readonly crv: "Ed25519"; readonly d?: undefined; readonly dp?: undefined; readonly dq?: undefined; readonly k?: undefined; readonly key_ops?: readonly Array<string>; readonly kid?: string; readonly kty: "OKP"; readonly oth?: undefined; readonly p?: undefined; readonly q?: undefined; readonly qi?: undefined; readonly use?: string; readonly x: string; } | { [key: string]: unknown; readonly crv?: string; readonly d?: undefined; readonly dp?: undefined; readonly dq?: undefined; readonly k?: undefined; readonly kty: string; readonly oth?: undefined; readonly p?: undefined; readonly q?: undefined; readonly qi?: undefined; }>;}): Effect<{ readonly resolve: (selection: Selection) => Effect<PublicKey, JoseError>;}>Example
import { Effect, Layer } from "effect"import { SecureRandom, Signature } from "effect/crypto"import { Jwk, Jwks } from "effect/jose"
const layer = Signature.layerWebCrypto.pipe(Layer.provide(SecureRandom.layerWebCrypto))
const kid = await Effect.runPromise(Effect.gen(function*() { const pair = yield* Jwk.generateKeyPair({ algorithm: "ES256", kid: "signing-1" }) const jwks = yield* Jwks.makeLocal({ keys: [pair.publicKey] }) const key = yield* jwks.resolve({ algorithm: "ES256", kid: "signing-1" }) return key.kid}).pipe(Effect.provide(layer)))kid // => "signing-1"makeRemote
unstableBuilds a Jwks service for a remote key set, using the HttpClient
service. Use it when the URL is only known at runtime, for example from
OpenID Connect discovery.
Details
The key set is fetched on first use and reused until timeToLive passes.
Concurrent callers share one request. A selection that finds no key fetches
the set again, at most once per cooldown, so rotated keys are picked up
without letting unknown kids drive a request per token. A failed fetch
fails every caller until cooldown passes; there is no fallback to an
expired set.
Only a 200 response from the configured URL is accepted, so a redirected key set is rejected. The check runs on the response, so it cannot stop a client from sending the redirected request.
Gotchas
Supply a client that does not follow redirects. FetchHttpClient follows
them by default: provide FetchHttpClient.RequestInit with
{ redirect: "manual" } on its layer so a redirect fails before any further
request. Clients wrapped with HttpClient.followRedirects also follow them.
Closing the scope interrupts any request in flight, and later resolutions
fail with KeySetUnavailable.
Signature
declare const makeRemote: (...args: [options: RemoteOptions]) => Effect<{ readonly resolve: (selection: Selection) => Effect<PublicKey, JoseError>;}, JoseError, Scope | HttpClient>Layers
layerLocal
unstableLayer that provides Jwks for a fixed key set.
Signature
declare function layerLocal(keySet: { readonly keys: readonly Array<{ readonly alg?: string; readonly d?: undefined; readonly dp?: undefined; readonly dq?: undefined; readonly e: string; readonly k?: undefined; readonly key_ops?: readonly Array<string>; readonly kid?: string; readonly kty: "RSA"; readonly n: string; readonly oth?: undefined; readonly p?: undefined; readonly q?: undefined; readonly qi?: undefined; readonly use?: string; } | { readonly alg?: string; readonly crv: "P-256" | "P-384" | "P-521"; readonly d?: undefined; readonly dp?: undefined; readonly dq?: undefined; readonly k?: undefined; readonly key_ops?: readonly Array<string>; readonly kid?: string; readonly kty: "EC"; readonly oth?: undefined; readonly p?: undefined; readonly q?: undefined; readonly qi?: undefined; readonly use?: string; readonly x: string; readonly y: string; } | { readonly alg?: string; readonly crv: "Ed25519"; readonly d?: undefined; readonly dp?: undefined; readonly dq?: undefined; readonly k?: undefined; readonly key_ops?: readonly Array<string>; readonly kid?: string; readonly kty: "OKP"; readonly oth?: undefined; readonly p?: undefined; readonly q?: undefined; readonly qi?: undefined; readonly use?: string; readonly x: string; } | { [key: string]: unknown; readonly crv?: string; readonly d?: undefined; readonly dp?: undefined; readonly dq?: undefined; readonly k?: undefined; readonly kty: string; readonly oth?: undefined; readonly p?: undefined; readonly q?: undefined; readonly qi?: undefined; }>;}): Layer<Jwks>layerRemote
unstableLayer that provides Jwks from a remote key set built by makeRemote.
Closing the layer interrupts any request in flight.
Signature
declare function layerRemote(options: RemoteOptions): Layer<Jwks, JoseError, HttpClient>Models
A set of public JWKs.
Signature
type KeySet = typeof KeySet.TypeOptions for makeRemote and layerRemote.
Signature
interface RemoteOptions { readonly cooldown?: Input; readonly maxKeys?: number; readonly maxResponseBytes?: number; readonly timeout?: Input; readonly timeToLive?: Input; readonly url: string;}The algorithm, and optionally the key id, of the key to resolve.
Signature
interface Selection { readonly algorithm: "RS256" | "RS384" | "RS512" | "PS256" | "PS384" | "PS512" | "ES256" | "ES384" | "ES512" | "EdDSA"; readonly kid?: string; readonly protectedHeader?: { [key: string]: Json; readonly alg: "RS256" | "RS384" | "RS512" | "PS256" | "PS384" | "PS512" | "ES256" | "ES384" | "ES512" | "EdDSA" | "HS256" | "HS384" | "HS512"; readonly cty?: string; readonly kid?: string; readonly typ?: string; };}Schemas
Schema for a set of public JWKs.
Details
Keys of other families or curves are kept but never selected. Private or symmetric members anywhere in the set fail decoding.
Signature
declare const KeySet: Struct<{ readonly keys: $Array<Union<readonly [Union<readonly [Struct<{ readonly alg: optionalKey<String>; readonly d: optionalKey<Never>; readonly dp: optionalKey<Never>; readonly dq: optionalKey<Never>; readonly e: String; readonly k: optionalKey<Never>; readonly key_ops: optionalKey<$Array<...>>; readonly kid: optionalKey<String>; readonly kty: Literal<"RSA">; readonly n: String; readonly oth: optionalKey<Never>; readonly p: optionalKey<Never>; readonly q: optionalKey<Never>; readonly qi: optionalKey<Never>; readonly use: optionalKey<String>; }>, Struct<{ readonly alg: optionalKey<String>; readonly crv: Literals<readonly [..., ..., ...]>; readonly d: optionalKey<Never>; readonly dp: optionalKey<Never>; readonly dq: optionalKey<Never>; readonly k: optionalKey<Never>; readonly key_ops: optionalKey<$Array<...>>; readonly kid: optionalKey<String>; readonly kty: Literal<"EC">; readonly oth: optionalKey<Never>; readonly p: optionalKey<Never>; readonly q: optionalKey<Never>; readonly qi: optionalKey<Never>; readonly use: optionalKey<String>; readonly x: String; readonly y: String; }>, Struct<{ readonly alg: optionalKey<String>; readonly crv: Literal<"Ed25519">; readonly d: optionalKey<Never>; readonly dp: optionalKey<Never>; readonly dq: optionalKey<Never>; readonly k: optionalKey<Never>; readonly key_ops: optionalKey<$Array<...>>; readonly kid: optionalKey<String>; readonly kty: Literal<"OKP">; readonly oth: optionalKey<Never>; readonly p: optionalKey<Never>; readonly q: optionalKey<Never>; readonly qi: optionalKey<Never>; readonly use: optionalKey<String>; readonly x: String; }>]>, StructWithRest<Struct<{ readonly crv: optionalKey<String>; readonly d: optionalKey<Never>; readonly dp: optionalKey<Never>; readonly dq: optionalKey<Never>; readonly k: optionalKey<Never>; readonly kty: String; readonly oth: optionalKey<Never>; readonly p: optionalKey<Never>; readonly q: optionalKey<Never>; readonly qi: optionalKey<Never>; }>, readonly [$Record<String, Unknown>]>]>>;}>