oidc

import "github.com/infrapi/kayra/internal/oidc"

Fetches and caches OIDC discovery documents and JWKS key sets for every remote authentifications.<name> provider, and serves Kayra’s own aggregated discovery/JWKS endpoints — the identity every other provider’s clients can, in principle, point back at Kayra itself for.

Manager#

manager, err := oidc.NewManager(s, keys, cfg)

Loads every provider’s local: signing keys up front (crypto.LoadSigningKeys) and owns the JWKS cache for every remote: provider.

manager.LocalKeys(provider)       // []crypto.SigningKey for a local: provider
manager.KeysFor(provider)         // []JWK — cached if remote, decoded if local
manager.RefreshRemote(provider)   // re-fetch discovery + JWKS, replace the cache
manager.AllKeys()                 // every enabled provider's keys, aggregated
manager.FindKey(kid)              // locate (provider, JWK) by kid, across every provider

FindKey checks local providers first (no I/O), then every remote provider’s cache, and only refreshes a remote provider’s JWKS (once, each) if no cache held the kid — the spec’s “absent from cache -> refresh, otherwise don’t” rule. authn.Verify is FindKey’s one caller.

Cached JWKS live at keys.AuthCache(provider) in the store — see kayra cache clean --scope auth.

JWK#

type JWK struct {
	Kty, Kid, Use, Alg string
	N, E               string // base64url, RSA only
	InfraPIAuthProvider string // which authentifications.<name> this key belongs to
}

pub, err := jwk.PublicKey()              // decode n/e into *rsa.PublicKey
jwk := oidc.NewJWK(provider, kid, alg, pub) // encode the other way

InfraPIAuthProvider is the spec’s extension field tagging every key (both fetched and self-issued) with the provider it belongs to, so /oauth2/discovery/keys’s aggregated set stays attributable.

Discovery/JWKS fetch#

doc, err := oidc.FetchDiscovery(url, proxy)           // GET .../.well-known/openid-configuration
keys, err := oidc.FetchJWKS(doc.JWKSURI, proxy)       // GET the jwks_uri
info, err := oidc.FetchUserinfo(url, token, proxy)    // POST with Authorization: Bearer

DiscoveryDoc also carries AuthorizationEndpoint/TokenEndpoint — unused by anything in this package itself, but what oauth2login builds its own Authorization Code + PKCE flow against.

proxy is a provider’s own authentifications.<name>.remote.proxy (empty string for none) — every call here goes through ClientFor(proxy), so each provider’s outbound traffic is routed independently. A 10-second timeout either way — these calls are small and infrequent (results are cached), so no retry/backoff machinery.

ClientFor#

client, err := oidc.ClientFor(proxy)

Returns the *http.Client every outbound call for one provider should use: a single shared, env-proxy-aware client (http.DefaultTransport, which already honors HTTP_PROXY/HTTPS_PROXY/NO_PROXY) when proxy is empty, or a client pinned to that exact proxy URL otherwise. Also used by oauth2login.Login for its own token-exchange call, so a provider’s remote.proxy applies there too.

Kayra’s own endpoints#

oidc.RegisterRoutes(engine, manager, baseURL)

Mounts GET /.well-known/openid-configuration (issuer, jwks_uri, userinfo_endpoint all built from baseURL) and GET /oauth2/discovery/keys (manager.AllKeys()), unauthenticated — this is the identity document, so it can’t itself require already being authenticated.