Documentation
¶
Overview ¶
Package oidc is a pure-Go (CGO-free) OpenID Connect library, the OIDC layer that sits on top of the go-ruby ecosystem's OAuth2 and JWT building blocks. It mirrors the surface of Ruby's `openid_connect` gem where that surface maps cleanly onto deterministic protocol logic, without any Ruby runtime.
What it does ¶
- Discovery — fetch and parse a provider's `.well-known/openid-configuration` document into ProviderMetadata.
- JWKS — fetch, parse and cache a provider's JSON Web Key Set and select the signing key by `kid`/`alg` (KeySet, JWKSCache).
- ID-token verification — parse the ID token (a signed JWT, via github.com/go-ruby-jwt/jwt), verify its signature against the JWKS key, and validate the OIDC claims (iss, aud, exp, iat, nbf, nonce, azp, at_hash, c_hash) with Verifier.
- Authorization-Code + PKCE — build the authorization request URL and exchange the code for tokens (built on github.com/go-ruby-oauth2/oauth2), then verify the returned ID token (Client).
- UserInfo — call the userinfo endpoint with an access token and return the claims.
The HTTP seam ¶
Like the other go-ruby libraries, the network round-trip is a host seam: every fetch (discovery, JWKS, token, userinfo) goes through the injectable Doer interface, so tests mock it and a host (go-embedded-ruby / rbgo) binds it without the core opening a socket. The token exchange reuses go-ruby-oauth2's request/response model via a small [Doer]→oauth2.RoundTripper adapter.
What it consumes ¶
It builds on two siblings rather than re-implementing them:
- github.com/go-ruby-oauth2/oauth2 — the authorization-URL and token-request construction and token-response parsing for the Authorization-Code+PKCE grant.
- github.com/go-ruby-jwt/jwt — parsing and signature verification of the ID token JWT (RS/ES/PS/HS families). JWKS→key selection from a provider's JSON (which the jwt gem's JWK, keyed by its own digest, does not cover) lives here, in KeySet.
Index ¶
- Variables
- type AuthParams
- type Client
- type Config
- type Doer
- type DoerFunc
- type Error
- type HTTPRequest
- type HTTPResponse
- type IDTokenClaims
- func (c *IDTokenClaims) Audience() []string
- func (c *IDTokenClaims) ExpiresAt() int64
- func (c *IDTokenClaims) Get(name string) (any, bool)
- func (c *IDTokenClaims) IssuedAt() int64
- func (c *IDTokenClaims) Issuer() string
- func (c *IDTokenClaims) Nonce() string
- func (c *IDTokenClaims) Raw() *jwt.OrderedMap
- func (c *IDTokenClaims) String(name string) (string, bool)
- func (c *IDTokenClaims) Subject() string
- type JWKSCache
- type Key
- type KeySet
- type KeySource
- type ProviderMetadata
- type Tokens
- type UserInfo
- type Verifier
Constants ¶
This section is empty.
Variables ¶
var ( // ErrOIDC is the root every oidc error Is. ErrOIDC = errors.New("oidc") // ErrDiscovery is a failure fetching or parsing the discovery document. ErrDiscovery = newParent("Discovery", ErrOIDC) // ErrJWKS is a failure fetching or parsing the JSON Web Key Set. ErrJWKS = newParent("JWKS", ErrOIDC) // ErrHTTP is a transport failure from the HTTP seam. ErrHTTP = newParent("HTTP", ErrOIDC) // ErrInvalidToken is a malformed ID token or one whose signature/algorithm // could not be resolved or verified. ErrInvalidToken = newParent("InvalidToken", ErrOIDC) // ErrInvalidIssuer is an iss claim that does not match the expected issuer. ErrInvalidIssuer = newParent("InvalidIssuer", ErrOIDC) // ErrInvalidAudience is an aud claim that does not contain the client id. ErrInvalidAudience = newParent("InvalidAudience", ErrOIDC) // ErrInvalidAzp is an azp claim that is required-but-absent or does not match. ErrInvalidAzp = newParent("InvalidAzp", ErrOIDC) // ErrExpired is an ID token whose exp is in the past (or missing). ErrExpired = newParent("Expired", ErrOIDC) // ErrInvalidIat is an iat claim that is missing, malformed or in the future. ErrInvalidIat = newParent("InvalidIat", ErrOIDC) // ErrNotYetValid is an nbf claim still in the future. ErrNotYetValid = newParent("NotYetValid", ErrOIDC) // ErrInvalidNonce is a nonce claim that does not match the expected nonce. ErrInvalidNonce = newParent("InvalidNonce", ErrOIDC) // ErrInvalidHash is an at_hash/c_hash claim that does not match its value. ErrInvalidHash = newParent("InvalidHash", ErrOIDC) // ErrConfig is an invalid client/verifier configuration. ErrConfig = newParent("Config", ErrOIDC) // ErrNoIDToken is a token response without an id_token. ErrNoIDToken = newParent("NoIDToken", ErrOIDC) // ErrToken is a failure at the token endpoint (transport or OAuth2 error). ErrToken = newParent("Token", ErrOIDC) // ErrUserInfo is a failure fetching or parsing the userinfo response. ErrUserInfo = newParent("UserInfo", ErrOIDC) )
The sentinels below name the error categories. Match the whole family with errors.Is(err, oidc.ErrOIDC).
Functions ¶
This section is empty.
Types ¶
type AuthParams ¶
type AuthParams struct {
// State is the CSRF state value echoed back on the redirect.
State string
// Nonce binds the ID token to this request (recommended); echoed in the token.
Nonce string
// CodeVerifier is the PKCE verifier; when non-empty an S256 challenge is added
// and the same verifier must be supplied to Exchange.
CodeVerifier string
// Scopes are additional scopes for this request (merged with the config's).
Scopes []string
// Extra carries any additional authorization params (prompt, login_hint, …).
Extra oauth2.Params
}
AuthParams carries the per-request inputs for Client.AuthCodeURL.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client drives the OIDC Authorization-Code + PKCE flow: it builds the authorization URL, exchanges the code for tokens (reusing go-ruby-oauth2), verifies the returned ID token, and calls the userinfo endpoint.
func DiscoverClient ¶
DiscoverClient runs discovery for issuer and returns a configured Client. It is a convenience over Discover + NewClient.
func (*Client) AuthCodeURL ¶
func (c *Client) AuthCodeURL(p AuthParams) string
AuthCodeURL builds the authorization request URL to redirect the user-agent to: scope "openid" (plus configured/extra scopes), response_type=code, the client id, redirect_uri, state, nonce, and — when a CodeVerifier is given — the PKCE S256 code_challenge. The query is byte-faithful to go-ruby-oauth2's encoder.
func (*Client) Exchange ¶
Exchange swaps an authorization code for tokens, then verifies the returned ID token. codeVerifier is the PKCE verifier from the matching Client.AuthCodeURL call (empty if PKCE was not used); nonce is the expected nonce (empty to skip the nonce check). The access token and code are used for at_hash/c_hash validation when those claims are present.
type Config ¶
type Config struct {
// Metadata is the provider configuration (from Discover / ParseProviderMetadata).
Metadata *ProviderMetadata
// ClientID and ClientSecret are the registered client credentials. The secret
// doubles as the HMAC key for HS-signed ID tokens.
ClientID string
ClientSecret string
// RedirectURI is the client's redirect endpoint.
RedirectURI string
// Scopes are extra scopes requested alongside the mandatory "openid".
Scopes []string
// Doer is the HTTP seam used for the token and userinfo requests.
Doer Doer
// Leeway is the clock-skew allowance for ID-token time checks.
Leeway time.Duration
// JWKSTTL is how long a fetched key set is cached (non-positive caches until a
// kid miss).
JWKSTTL time.Duration
}
Config configures an OIDC Client for the Authorization-Code + PKCE flow. The provider endpoints come from a discovery ProviderMetadata; the HTTP seam is the injectable Doer.
type Doer ¶
type Doer interface {
Do(req *HTTPRequest) (*HTTPResponse, error)
}
Doer is the injectable HTTP seam: it performs one round-trip for a built HTTPRequest and returns the raw HTTPResponse. The core never opens a socket — a host (go-ruby-net-http / faraday) or a test mock implements this.
type DoerFunc ¶
type DoerFunc func(req *HTTPRequest) (*HTTPResponse, error)
DoerFunc adapts a function to the Doer interface.
type Error ¶
type Error struct {
// Kind names the error category (e.g. "InvalidToken").
Kind string
// Message is the human-readable failure text.
Message string
// contains filtered or unexported fields
}
Error is the error type this package raises. Kind names the failing category (mirroring the OpenID::Connect error families) and Message is the human-readable text. Every error Is ErrOIDC, so a caller can match the whole family with errors.Is(err, oidc.ErrOIDC), a specific category with the matching sentinel (e.g. errors.Is(err, oidc.ErrInvalidNonce)), and — for a wrapped failure — the underlying cause too (e.g. a jwt sentinel).
type HTTPRequest ¶
type HTTPRequest struct {
// Method is "GET" or "POST".
Method string
// URL is the fully-qualified request URL (query already appended for GET).
URL string
// Header carries request headers (e.g. Authorization for userinfo).
Header map[string]string
// Body is the request body (form-encoded for a POST token request), or "".
Body string
}
HTTPRequest is the request the HTTP seam performs. It is intentionally minimal — method, URL, headers and a (form-encoded) body — so any host transport (go-ruby-net-http / faraday / a test mock) can satisfy it without depending on net/http types.
type HTTPResponse ¶
type HTTPResponse struct {
// Status is the HTTP status code.
Status int
// Header carries response headers; Content-Type drives body parsing.
Header map[string]string
// Body is the raw response body.
Body string
}
HTTPResponse is the raw response the HTTP seam returns.
type IDTokenClaims ¶
type IDTokenClaims struct {
// contains filtered or unexported fields
}
IDTokenClaims is the validated payload of an ID token. The typed accessors cover the standard OIDC claims; Get/String and Raw expose the rest.
func (*IDTokenClaims) Audience ¶
func (c *IDTokenClaims) Audience() []string
Audience returns the aud claim as a slice (a single string audience becomes a one-element slice).
func (*IDTokenClaims) ExpiresAt ¶
func (c *IDTokenClaims) ExpiresAt() int64
ExpiresAt returns the exp claim as a Unix timestamp (0 if absent).
func (*IDTokenClaims) Get ¶
func (c *IDTokenClaims) Get(name string) (any, bool)
Get returns a raw claim value and whether it is present.
func (*IDTokenClaims) IssuedAt ¶
func (c *IDTokenClaims) IssuedAt() int64
IssuedAt returns the iat claim as a Unix timestamp (0 if absent).
func (*IDTokenClaims) Issuer ¶
func (c *IDTokenClaims) Issuer() string
Issuer returns the iss claim.
func (*IDTokenClaims) Raw ¶
func (c *IDTokenClaims) Raw() *jwt.OrderedMap
Raw returns the underlying ordered claim map.
func (*IDTokenClaims) String ¶
func (c *IDTokenClaims) String(name string) (string, bool)
String returns a string-valued claim and whether it is present as a string.
func (*IDTokenClaims) Subject ¶
func (c *IDTokenClaims) Subject() string
Subject returns the sub claim.
type JWKSCache ¶
type JWKSCache struct {
// contains filtered or unexported fields
}
JWKSCache fetches a provider's key set on demand and caches it for a TTL, refetching once when a requested kid is absent from the cached set (a key rotation). It is safe for concurrent use.
func NewJWKSCache ¶
NewJWKSCache returns a cache that fetches from uri through doer and retains the set for ttl. A non-positive ttl caches until a kid miss forces a refetch.
type Key ¶
type Key struct {
// Kid is the provider-assigned key id used to select the key.
Kid string
// Kty is the key type ("RSA" or "EC").
Kty string
// Alg is the intended algorithm ("RS256", "ES256", …), or "" if unspecified.
Alg string
// Use is the intended use ("sig"/"enc"), or "" if unspecified.
Use string
// contains filtered or unexported fields
}
Key is one parsed JSON Web Key: a provider-assigned kid/alg/use and the RSA or EC public key it carries. Unlike the jwt gem's JWK (whose kid is a digest of the key), a provider's JWKS assigns the kid, so this type retains the provider's value for `kid`-based selection.
type KeySet ¶
type KeySet struct {
// contains filtered or unexported fields
}
KeySet is a parsed JSON Web Key Set (RFC 7517) — the signing keys of a provider, with selection by kid/alg.
func FetchJWKS ¶
FetchJWKS retrieves and parses a key set from a JWKS URI over the HTTP seam, requiring a 200 response.
func ParseJWKS ¶
ParseJWKS decodes a JSON Web Key Set by delegating the RFC 7517 import to go-ruby-jwt's transport-agnostic ParseJWKS, then adapting each imported *jwt.JWK into this package's Key. The delegate materialises RSA and EC keys into crypto public keys and skips a key of any other type (e.g. an "oct" symmetric key), matching a client that ignores keys it cannot use for JWS verification; a key of a supported type with malformed material fails the whole set. Its jwt errors are re-wrapped under ErrJWKS so callers keep matching the OIDC error family.
type KeySource ¶
KeySource resolves a verification key by kid/alg. Both a static KeySet and a JWKSCache satisfy it, so a Verifier accepts either.
type ProviderMetadata ¶
type ProviderMetadata struct {
Issuer string `json:"issuer"`
AuthorizationEndpoint string `json:"authorization_endpoint"`
TokenEndpoint string `json:"token_endpoint"`
UserinfoEndpoint string `json:"userinfo_endpoint"`
JWKSURI string `json:"jwks_uri"`
RegistrationEndpoint string `json:"registration_endpoint"`
EndSessionEndpoint string `json:"end_session_endpoint"`
ScopesSupported []string `json:"scopes_supported"`
ResponseTypesSupported []string `json:"response_types_supported"`
ResponseModesSupported []string `json:"response_modes_supported"`
GrantTypesSupported []string `json:"grant_types_supported"`
SubjectTypesSupported []string `json:"subject_types_supported"`
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"`
ClaimsSupported []string `json:"claims_supported"`
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"`
// Raw is the whole decoded document, for members not promoted above.
Raw map[string]any `json:"-"`
}
ProviderMetadata is a parsed OpenID Provider configuration (`.well-known/openid-configuration`), per OpenID Connect Discovery 1.0 §3. The named fields cover the members this library uses; Raw retains the full decoded document so a caller (or rbgo) can read provider extras.
func Discover ¶
func Discover(doer Doer, issuer string) (*ProviderMetadata, error)
Discover fetches and parses a provider's configuration document over the HTTP seam. It requires a 200 response and enforces that the document's issuer is identical to issuer (Discovery §4.3), rejecting a mismatch.
func ParseProviderMetadata ¶
func ParseProviderMetadata(data []byte) (*ProviderMetadata, error)
ParseProviderMetadata decodes a discovery document, requiring the four members the code flow relies on — issuer, authorization_endpoint, token_endpoint and jwks_uri — to be present (a missing one is an ErrDiscovery).
type Tokens ¶
type Tokens struct {
// Access is the parsed OAuth2 access token (with any residual params).
Access *oauth2.AccessToken
// IDToken is the raw compact ID token JWT.
IDToken string
// Claims is the validated ID-token claim set.
Claims *IDTokenClaims
}
Tokens is the result of a successful code exchange.
type UserInfo ¶
type UserInfo struct {
// contains filtered or unexported fields
}
UserInfo is the parsed response of the UserInfo endpoint (OIDC Core §5.3): the claims about the authenticated end-user. The response MUST carry a sub claim.
func FetchUserInfo ¶
FetchUserInfo calls the userinfo endpoint with a bearer access token over the HTTP seam and parses the JSON claims. A missing endpoint or a non-200 response is an error.
func ParseUserInfo ¶
ParseUserInfo decodes a UserInfo JSON response, requiring a non-empty sub.
type Verifier ¶
type Verifier struct {
// Issuer is the expected iss value (exact match).
Issuer string
// ClientID is the expected audience member (and azp value).
ClientID string
// Keys resolves the RSA/EC signing key by kid/alg (a KeySet or JWKSCache).
Keys KeySource
// HMACSecret is the shared secret for HS* tokens (typically the client
// secret); required only when an HS-family token is verified.
HMACSecret []byte
// Nonce, when non-empty, is the expected nonce claim.
Nonce string
// Leeway is the allowed clock skew for the exp/iat/nbf checks.
Leeway time.Duration
// AccessToken, when non-empty, enables at_hash validation against it.
AccessToken string
// Code, when non-empty, enables c_hash validation against it.
Code string
// Algorithms optionally restricts the accepted alg values; empty means
// defaultAlgs.
Algorithms []string
}
Verifier validates an OpenID Connect ID token: it resolves the signing key, verifies the JWS signature (via github.com/go-ruby-jwt/jwt), and checks the OIDC claims. The signature check is delegated to the jwt sibling; every OIDC claim rule (iss, aud, exp, iat, nbf, nonce, azp, at_hash, c_hash) is applied here so the accepted/rejected semantics are OIDC-specific.
func (*Verifier) Verify ¶
func (v *Verifier) Verify(idToken string) (*IDTokenClaims, error)
Verify parses idToken, verifies its signature against the resolved key, and applies the OIDC claim checks. On success it returns the validated claims; any failure is an error whose family (errors.Is) identifies the failing check.
