Help us shape the future of Go! Take the 2026 Go Developer Survey

oidcclient

package
v0.6.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 12, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

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

View Source
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

View Source
var (
	ErrUnknownProvider = errors.New("oidcclient: unknown provider")
	ErrInvalidProvider = errors.New("oidcclient: invalid provider configuration")
)

Errors returned by Registry / Provider / PKCE helpers.

View Source
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.

View Source
var ErrDiscoveryFailed = errors.New("oidcclient: discovery failed")

ErrDiscoveryFailed is returned when `.well-known/openid-configuration` is unreachable or malformed.

View Source
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

func IdentityFromClaims(c *Claims) *identity.Identity

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

func PKCEVerifier() (verifier, challenge string, err error)

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

type CodeExchangeRequest struct {
	Code         string
	RedirectURI  string
	PKCEVerifier string
}

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

func Discover(ctx context.Context, issuer string, client *http.Client) (*DiscoveryDocument, error)

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

type HTTPCodeExchanger struct {
	Client *http.Client
}

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

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

func (p *Provider) AuthCodeURL(redirectURI, state, codeChallenge string) string

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

func (p *Provider) AuthorizationURL() string

AuthorizationURL returns the provider authorization endpoint.

func (*Provider) ClientID

func (p *Provider) ClientID() string

ClientID returns the OAuth2 client id used for auth-code login.

func (*Provider) ClientSecret

func (p *Provider) ClientSecret() string

ClientSecret returns the OAuth2 client secret, if configured.

func (*Provider) ID

func (p *Provider) ID() string

ID returns the logical provider id.

func (*Provider) Issuer

func (p *Provider) Issuer() string

Issuer returns the provider issuer URL.

func (*Provider) Kind

func (p *Provider) Kind() Kind

Kind returns the provider flavor.

func (*Provider) Scopes

func (p *Provider) Scopes() []string

Scopes returns the default scopes used in auth-code requests.

func (*Provider) TokenURL

func (p *Provider) TokenURL() string

TokenURL returns the provider token endpoint.

func (*Provider) Verifier

func (p *Provider) Verifier() *Verifier

Verifier returns the underlying ID-token verifier (rarely needed by callers, exposed for tests and advanced flows).

func (*Provider) Verify

func (p *Provider) Verify(ctx context.Context, rawToken string) (*Claims, error)

Verify delegates to the underlying OIDC verifier.

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.

func NewRegistry

func NewRegistry() *Registry

NewRegistry returns an empty registry.

func (*Registry) Add

func (r *Registry) Add(p *Provider)

Add registers (or replaces) a provider.

func (*Registry) Get

func (r *Registry) Get(id string) (*Provider, error)

Get returns the provider with the given id.

func (*Registry) IDs

func (r *Registry) IDs() []string

IDs returns the registered provider ids in arbitrary order.

func (*Registry) Len

func (r *Registry) Len() int

Len returns the number of registered providers.

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

func (v *Verifier) Verify(ctx context.Context, rawToken string) (*Claims, error)

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).

type VerifierConfig

type VerifierConfig struct {
	Issuer         string
	Audience       string
	JWKSURL        string
	HTTPClient     *http.Client
	JWKSRefreshMin time.Duration
	JWKSRefreshMax time.Duration
	ClockSkew      time.Duration
}

VerifierConfig configures a Verifier.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL