Documentation
¶
Overview ¶
Package oidcclient provides a provider-neutral OIDC client used across all kombify Go services (Simulate, StackKits-Server, TechStack).
The package is split into four pieces that compose:
- Verifier — verifies RS256 ID tokens against issuer + audience using a cached JWKS endpoint.
- Provider / Registry — wraps a Verifier together with the OAuth2 authorization-code endpoints of a single issuer (Pocket ID, Auth0, PocketBase-OIDC bridge, generic OIDC).
- CodeExchanger — exchanges an authorization code (with optional PKCE verifier) for an ID token.
- Discover — best-effort fetch of `.well-known/openid-configuration`.
Identity bridge: IdentityFromClaims converts an OIDC Claims into a github.com/kombifyio/go-common/identity.Identity so that downstream middleware can keep using the existing identity context plumbing.
Self-hosted vs SaaS: callers configure the same client with a different Issuer URL — Pocket ID for the self-hosted default, Auth0 for SaaS, the PocketBase OIDC bridge for legacy operators. There are no SaaS-specific branches in this package.
Index ¶
- Constants
- Variables
- func ApplyDiscovery(cfg *ProviderConfig, doc *DiscoveryDocument)
- func IdentityFromClaims(c *Claims) *identity.Identity
- func PKCEVerifier() (verifier, challenge string, err error)
- type Claims
- type CodeExchangeRequest
- type CodeExchangeResult
- type CodeExchanger
- type DiscoveryDocument
- type HTTPCodeExchanger
- type Kind
- type Provider
- func (p *Provider) AuthCodeURL(redirectURI, state, codeChallenge string) string
- func (p *Provider) AuthorizationURL() string
- func (p *Provider) ClientID() string
- func (p *Provider) ClientSecret() string
- func (p *Provider) ID() string
- func (p *Provider) Issuer() string
- func (p *Provider) Kind() Kind
- func (p *Provider) Scopes() []string
- func (p *Provider) TokenURL() string
- func (p *Provider) Verifier() *Verifier
- func (p *Provider) Verify(ctx context.Context, rawToken string) (*Claims, error)
- type ProviderConfig
- type Registry
- type Verifier
- type VerifierConfig
Constants ¶
const ( DefaultJWKSRefreshMin = 30 * time.Second DefaultJWKSRefreshMax = 15 * time.Minute DefaultHTTPTimeout = 5 * time.Second )
Defaults for JWKS cache behaviour. Min interval throttles upstream JWKS hits when a token references an unknown KID (could be attacker-driven). Max interval forces periodic refresh even if no cache miss occurs.
Variables ¶
var ( ErrUnknownProvider = errors.New("oidcclient: unknown provider") ErrInvalidProvider = errors.New("oidcclient: invalid provider configuration") )
Errors returned by Registry / Provider / PKCE helpers.
var ( ErrInvalidToken = errors.New("oidcclient: invalid token") ErrUnknownKID = errors.New("oidcclient: unknown key id") ErrUnsupportedAlg = errors.New("oidcclient: unsupported signing algorithm") ErrIssuerMismatch = errors.New("oidcclient: issuer mismatch") ErrAudienceMismatch = errors.New("oidcclient: audience mismatch") ErrTokenExpired = errors.New("oidcclient: token expired") ErrTokenNotYetValid = errors.New("oidcclient: token not yet valid") ErrJWKSFetchFailed = errors.New("oidcclient: jwks fetch failed") ErrConfigInvalid = errors.New("oidcclient: configuration invalid") )
Errors returned by Verify.
var ErrDiscoveryFailed = errors.New("oidcclient: discovery failed")
ErrDiscoveryFailed is returned when `.well-known/openid-configuration` is unreachable or malformed.
var (
ErrTokenExchange = errors.New("oidcclient: token exchange failed")
)
Errors returned by code exchange.
Functions ¶
func ApplyDiscovery ¶
func ApplyDiscovery(cfg *ProviderConfig, doc *DiscoveryDocument)
ApplyDiscovery fills the URL fields of cfg from doc when they are unset. It is a convenience used after Discover:
doc, _ := oidcclient.Discover(ctx, cfg.Issuer, nil) oidcclient.ApplyDiscovery(&cfg, doc) provider, _ := oidcclient.NewProvider(cfg)
func IdentityFromClaims ¶
IdentityFromClaims projects an OIDC Claims onto a identity.Identity. The mapping is intentionally simple — services that need richer mapping (per-tenant role tables, group→role rewrites) should post-process the returned value.
Claim sources, in priority order:
- UserID ← `sub`
- Email ← `email`
- OrgID ← `org_id` || `org` || `project_id` (raw claim)
- Tier ← `tier` || `plan` (raw claim)
- Roles ← `roles` (raw claim, []string or comma-separated string) || single `role` claim
Returns nil if claims is nil.
func PKCEVerifier ¶
PKCEVerifier returns a fresh RFC-7636 PKCE verifier (43–128 chars, base64url unpadded random bytes) plus its S256 challenge. Callers persist the verifier server-side keyed by login-state and pass the challenge to Provider.AuthCodeURL.
Types ¶
type Claims ¶
type Claims struct {
Subject string `json:"sub"`
Issuer string `json:"iss"`
Audience []string `json:"aud"`
Email string `json:"email,omitempty"`
Name string `json:"name,omitempty"`
IssuedAt int64 `json:"iat,omitempty"`
Expires int64 `json:"exp,omitempty"`
Raw map[string]interface{} `json:"-"`
}
Claims is the verified subset of an ID token. Provider-specific claims can be re-decoded by callers from Claims.Raw.
type CodeExchangeRequest ¶
CodeExchangeRequest carries the parameters needed for an authorization-code exchange. PKCEVerifier is optional but strongly recommended for public clients (no ClientSecret).
type CodeExchangeResult ¶
type CodeExchangeResult struct {
IDToken string `json:"id_token"`
AccessToken string `json:"access_token,omitempty"`
RefreshToken string `json:"refresh_token,omitempty"`
TokenType string `json:"token_type,omitempty"`
ExpiresIn int64 `json:"expires_in,omitempty"`
Scope string `json:"scope,omitempty"`
}
CodeExchangeResult is the parsed token endpoint response. Refresh- and access-token are exposed for callers that need them; the typical kombify flow only consumes IDToken.
type CodeExchanger ¶
type CodeExchanger interface {
ExchangeCode(ctx context.Context, p *Provider, req CodeExchangeRequest) (*CodeExchangeResult, error)
}
CodeExchanger exchanges an OAuth2 authorization code for an ID token.
Implementations must be safe for concurrent use. The default HTTPCodeExchanger talks RFC-6749 directly to Provider.TokenURL.
type DiscoveryDocument ¶
type DiscoveryDocument struct {
Issuer string `json:"issuer"`
AuthorizationEndpoint string `json:"authorization_endpoint"`
TokenEndpoint string `json:"token_endpoint"`
JWKSURI string `json:"jwks_uri"`
UserinfoEndpoint string `json:"userinfo_endpoint,omitempty"`
EndSessionEndpoint string `json:"end_session_endpoint,omitempty"`
ScopesSupported []string `json:"scopes_supported,omitempty"`
}
DiscoveryDocument is the subset of the OIDC discovery metadata kombify services consume. The full RFC-defined document has many more fields; we only surface what we use.
func Discover ¶
Discover fetches `<issuer>/.well-known/openid-configuration` and returns the parsed metadata. Use it on startup to populate ProviderConfig.AuthorizationURL / ProviderConfig.TokenURL / ProviderConfig.JWKSURL without hard-coding endpoint paths.
The HTTP client defaults to a 5 s timeout when nil.
type HTTPCodeExchanger ¶
HTTPCodeExchanger is the default RFC-6749 code-exchanger.
func NewHTTPCodeExchanger ¶
func NewHTTPCodeExchanger() *HTTPCodeExchanger
NewHTTPCodeExchanger returns an exchanger with a sane default timeout.
func (*HTTPCodeExchanger) ExchangeCode ¶
func (h *HTTPCodeExchanger) ExchangeCode(ctx context.Context, p *Provider, req CodeExchangeRequest) (*CodeExchangeResult, error)
ExchangeCode posts an `application/x-www-form-urlencoded` token request to the provider's token endpoint. ClientSecret (if present) is sent as HTTP Basic auth. PKCE verifier is sent as a form field.
type Kind ¶
type Kind string
Kind enumerates the supported provider flavors. The kind drives default discovery URL composition; once a provider is constructed all kinds share the same OIDC verification path.
const ( // KindPocketID is the kombify self-hosted default identity provider // (passkey-first OIDC). KindPocketID Kind = "pocketid" // KindAuth0 is the kombify SaaS identity provider via Cloudflare Edge // Router. KindAuth0 Kind = "auth0" // KindPocketBase is the optional self-hosted alternative — a dedicated // PocketBase instance behind the kombify OIDC bridge. KindPocketBase Kind = "pocketbase" // KindGeneric is any other RFC-compliant OIDC issuer. KindGeneric Kind = "generic" )
type Provider ¶
type Provider struct {
// contains filtered or unexported fields
}
Provider is a verified-OIDC bridge for a single identity provider config.
func NewProvider ¶
func NewProvider(cfg ProviderConfig) (*Provider, error)
NewProvider builds a Provider from a ProviderConfig. JWKSURL defaults to `<issuer>/.well-known/jwks.json` when not explicitly set; that is the convention for both Pocket ID and Auth0. Use Discover to populate the URL fields from `.well-known/openid-configuration`.
func (*Provider) AuthCodeURL ¶
AuthCodeURL constructs the user-agent redirect URL for starting an auth-code flow. When codeChallenge is non-empty PKCE (S256) is added.
func (*Provider) AuthorizationURL ¶
AuthorizationURL returns the provider authorization endpoint.
func (*Provider) ClientSecret ¶
ClientSecret returns the OAuth2 client secret, if configured.
type ProviderConfig ¶
type ProviderConfig struct {
ID string // logical id (per org/tenant), e.g. "primary", "auth0-saas"
Kind Kind // provider flavor (drives default URL composition)
Issuer string // required
Audience string // optional; defaults to ClientID
ClientID string // required for auth code login
ClientSecret string // optional for public clients (use PKCE instead)
AuthorizationURL string // optional; derived from issuer when empty
TokenURL string // optional; derived from issuer when empty
JWKSURL string // optional; derived from issuer when empty
Scopes []string // optional; defaults to openid/profile/email
}
ProviderConfig describes a single identity provider entry.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry holds providers indexed by ID. Safe for concurrent reads after construction; write methods take an internal mutex.
type Verifier ¶
type Verifier struct {
// contains filtered or unexported fields
}
Verifier verifies OIDC ID tokens issued by a single issuer.
func NewVerifier ¶
func NewVerifier(cfg VerifierConfig) (*Verifier, error)
NewVerifier constructs a Verifier and validates required config.
func (*Verifier) Verify ¶
Verify parses and validates the given raw ID token. On success the returned Claims are guaranteed to match issuer + audience and to be within exp/nbf bounds (with VerifierConfig.ClockSkew tolerance).