Documentation
¶
Overview ¶
Package oidcx provides an auto-refreshing JWK Set cache that survives IdP key rotation without restarting the host process. It is a thin, import-light wrapper around jwx v2's jwk.Cache that:
- registers a single JWKS URL with sane defaults (15-minute minimum refresh interval, refresh-on-failure semantics);
- exposes a Set(ctx) accessor that callers use on every JWT verification path so that key rotation is picked up without polling or process restart;
- emits structured failure callbacks via an optional Logger hook so that obsx (or any other observability package) can audit refresh errors without oidcx pulling obsx in as a dependency.
The cache is safe for concurrent use; jwk.Cache serializes refresh requests internally. Code generated by apic; DO NOT EDIT.
Index ¶
Constants ¶
const DefaultFetchTimeout = 30 * time.Second
DefaultFetchTimeout is the default per-request timeout applied to the HTTP client used to fetch the JWKS document. APPSEC-7: prior versions inherited http.DefaultClient (no timeout), so a hung IdP could stall cold-start, refresh goroutines, and (once wired) every sync JWT verification. 30 seconds is conservative enough to cover slow-but-healthy IdPs while still bounding the worst case.
const DefaultMaxStaleness = 0
DefaultMaxStaleness is 0, which disables the staleness cap. Callers that want a hard upper bound on how stale the cache may become on extended IdP outages must opt in via WithMaxStaleness. APPSEC-3.
const DefaultMinRefreshInterval = 15 * time.Minute
DefaultMinRefreshInterval is the floor for re-fetching the JWKS, regardless of any Cache-Control / Expires hint supplied by the IdP. 15 minutes balances rotation responsiveness against IdP load.
Variables ¶
var ErrEmptyJWKSURL = errors.New("oidcx: empty JWKS URL")
ErrEmptyJWKSURL is returned by NewJWKCache when the supplied JWKS URL is empty. Distinct from ErrEmptyJWKSet which signals that a fetch succeeded but returned no keys.
var ErrEmptyJWKSet = errors.New("oidcx: jwks endpoint returned no keys")
ErrEmptyJWKSet is returned by NewJWKCache when the IdP responds with a parseable but empty JWK set on cold start. Callers should treat this as a misconfiguration of the JWKS endpoint.
var ErrJWKSStaleExceeded = errors.New("oidcx: jwks cache exceeded max staleness")
ErrJWKSStaleExceeded is returned by JWKCache.Set when the most recent successful fetch is older than the configured WithMaxStaleness window. Callers should treat this as a hard authentication failure rather than fall back to the cached set: jwk.AutoRefresh would otherwise serve the stale set indefinitely during an extended IdP outage. APPSEC-3.
var ErrNilJWKCache = errors.New("oidcx: nil JWKCache")
ErrNilJWKCache is returned by methods on a nil receiver. This is strictly a defense against caller error -- production code should always inject a non-nil cache.
Functions ¶
This section is empty.
Types ¶
type HTTPClient ¶
HTTPClient is the minimal interface oidcx needs to fetch JWKS over HTTP. Tests and callers using a non-default transport (mTLS, proxy, custom timeout) inject their own client via WithHTTPClient.
The signature deliberately mirrors *http.Client.Do so callers can pass *http.Client or any wrapper (e.g. an mTLS or instrumented client) that already satisfies that contract. Internally the cache adapts this to jwx v2's Get(url string)-shaped HTTPClient interface; callers don't need to think about that.
type JWKCache ¶
type JWKCache struct {
// contains filtered or unexported fields
}
JWKCache is an auto-refreshing JWK Set cache. The zero value is not usable; construct one with NewJWKCache.
func NewJWKCache ¶
NewJWKCache creates a cache for the given JWKS URL and primes it by performing an initial Refresh against the IdP. It returns an error if the cold-start fetch fails or the response is empty. The supplied context governs only the cold-start fetch; once the cache is constructed, refreshes use a context derived from the host process.
func (*JWKCache) Healthy ¶
Healthy reports whether the cache is within the configured max-staleness window. Always true when WithMaxStaleness was not supplied. APPSEC-3.
func (*JWKCache) Refresh ¶
Refresh forces an immediate refresh of the cached JWK set. Intended for tests, key-rotation event handlers, and admin-triggered refresh endpoints. Production paths should rely on Set's auto-refresh.
Errors from the underlying refresh are also reported to any logger supplied via WithLogger so the caller's observability pipeline sees the same failure surface that the background refresher emits via the ErrSink. jwx v2's synchronous Refresh returns the error directly rather than forwarding to the ErrSink, so without this hop the logger would miss manual refresh failures and admin-triggered retries.
func (*JWKCache) Set ¶
Set returns the current JWK set, triggering a refresh if the configured interval has elapsed. Per AutoRefresh semantics: if a refresh is in progress, callers receive the cached set, not a stale error. If the refresh fails, the cache continues to serve the last-good set so authentication does not flap during transient IdP outages -- subject to the WithMaxStaleness cap, which when set causes Set to return ErrJWKSStaleExceeded rather than indefinitely extend the trust window. APPSEC-3.
type Logger ¶
Logger is the structured logging contract the cache uses to surface refresh failures and stale-fallback events. Callers wire their own logger (slog, obsx audit, log.Logger) by passing WithLogger. Keeping this an interface rather than a concrete type lets oidcx stay free of any logging dependency.
type Option ¶
type Option func(*config)
Option configures a JWKCache at construction time.
func WithFetchTimeout ¶
WithFetchTimeout overrides the per-request HTTP timeout applied when fetching the JWKS document. Defaults to DefaultFetchTimeout. Values <= 0 fall back to the default rather than disabling the timeout: a permanently-disabled timeout was the APPSEC-7 root cause and is not user-recoverable from the cache contract. Callers that need an unusual timeout policy should inject their own http.Client via WithHTTPClient; that client's Timeout is preserved verbatim. APPSEC-7.
func WithHTTPClient ¶
func WithHTTPClient(c HTTPClient) Option
WithHTTPClient configures the underlying HTTP client used to fetch the JWKS document. Defaults to http.DefaultClient via jwx.
func WithLogger ¶
WithLogger registers a structured logger that receives a Warn entry on every refresh failure followed by an Error entry if the cache cannot serve any prior key set (cold-start failure).
func WithMaxStaleness ¶
WithMaxStaleness configures the upper bound on how stale the cached JWK set may become before JWKCache.Set starts returning ErrJWKSStaleExceeded. Counted from the last successful fetch. Zero (the default) disables the cap, preserving the legacy "serve last-good indefinitely" behaviour. Operators running security-sensitive workloads should set this to a value larger than the JWKS rotation cadence but smaller than the access-token lifetime so an extended IdP outage cannot indefinitely extend the trust window. APPSEC-3.
func WithMinRefreshInterval ¶
WithMinRefreshInterval overrides DefaultMinRefreshInterval. Values below 1 second are clamped to 1 second to avoid pathological refresh storms; values above 24 hours are accepted as-is so operators can extend the floor for tightly cost-controlled environments.