oidc

package module
v0.0.0-...-cdd79e2 Latest Latest
Warning

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

Go to latest
Published: Jul 17, 2026 License: BSD-3-Clause Imports: 13 Imported by: 0

README

go-ruby-oidc/oidc

oidc — go-ruby-oidc

Docs License Go Coverage

A pure-Go (no cgo) OpenID Connect library — Discovery, JWKS, ID-token verification, the Authorization-Code + PKCE flow, and UserInfo — mirroring the surface of Ruby's openid_connect gem where it maps cleanly onto deterministic protocol logic, without any Ruby runtime.

It is the OIDC layer for go-embedded-ruby, but a standalone, reusable module — a sibling of go-ruby-oauth2 and go-ruby-jwt, on which it builds.

What it consumes — and doesn't reinvent. The OAuth2 authorization-URL and token-request construction come from go-ruby-oauth2/oauth2; the ID token is a signed JWT parsed and signature-verified by go-ruby-jwt/jwt. This package adds the OIDC-specific pieces: discovery, JWKS→key selection from a provider's key set, and the ID-token claim validation (iss, aud, exp, iat, nbf, nonce, azp, at_hash, c_hash).

The HTTP round-trip is a host seam. Every network 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 Doeroauth2.RoundTripper adapter.

Features

  • Discovery — fetch and parse .well-known/openid-configuration into ProviderMetadata (issuer, authorization/token/userinfo/jwks endpoints, supported scopes/claims/algs), enforcing the issuer match.
  • JWKS — fetch, parse and cache a provider's JSON Web Key Set (KeySet / JWKSCache) and select the signing key by kid/alg, with a one-shot refetch on a rotated key. RSA and EC keys are materialised from the JWK n/e and crv/x/y.
  • ID-token verificationVerifier parses the ID token (via go-ruby-jwt), verifies the JWS signature (RS/ES/PS/HS families; none is always rejected) against the resolved key, and validates the OIDC claims: iss exact match, aud contains the client id, exp/iat/nbf with leeway, nonce, azp (required for multiple audiences), and at_hash/c_hash when present.
  • Authorization-Code + PKCEClient.AuthCodeURL builds the authorization request URL (scope openid, state, nonce, PKCE S256 challenge) and Client.Exchange swaps the code for tokens via go-ruby-oauth2 and verifies the returned ID token.
  • UserInfo — call the userinfo endpoint with the bearer access token and return the claims.

CGO-free, 100% test coverage (every rejection branch), gofmt + go vet clean, and green across the six 64-bit Go targets (amd64, arm64, riscv64, loong64, ppc64le, s390x — including the big-endian lane).

Install

go get github.com/go-ruby-oidc/oidc

Usage

package main

import (
	"fmt"

	"github.com/go-ruby-oidc/oidc"
)

func main() {
	// doer is your HTTP seam: oidc.DoerFunc(func(*oidc.HTTPRequest) (*oidc.HTTPResponse, error){...})
	var doer oidc.Doer

	// 1. Discover the provider and build a client.
	client, err := oidc.DiscoverClient(oidc.Config{
		Doer:         doer,
		ClientID:     "myclient",
		ClientSecret: "mysecret",
		RedirectURI:  "https://app.example.com/callback",
		Scopes:       []string{"email", "profile"},
	}, "https://accounts.example.com")
	if err != nil {
		panic(err)
	}

	// 2. Build the authorization URL (openid scope + state + nonce + PKCE S256).
	verifier := "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
	authURL := client.AuthCodeURL(oidc.AuthParams{
		State:        "xyz",
		Nonce:        "n-0S6_WzA2Mj",
		CodeVerifier: verifier,
	})
	fmt.Println(authURL) // redirect the user-agent here

	// 3. On the redirect, exchange the code — the returned id_token is verified.
	tokens, err := client.Exchange("the-code", verifier, "n-0S6_WzA2Mj")
	if err != nil {
		panic(err)
	}
	fmt.Println(tokens.Claims.Subject(), tokens.Access.Token)

	// 4. Fetch UserInfo with the access token.
	ui, err := client.UserInfo(tokens.Access.Token)
	if err != nil {
		panic(err)
	}
	fmt.Println(ui.Subject())
}
Verifying an ID token directly
v := &oidc.Verifier{
	Issuer:   "https://accounts.example.com",
	ClientID: "myclient",
	Keys:     keySet, // *oidc.KeySet or *oidc.JWKSCache
	Nonce:    "n-0S6_WzA2Mj",
}
claims, err := v.Verify(idToken)
// err is errors.Is-matchable: ErrInvalidIssuer, ErrInvalidAudience, ErrExpired,
// ErrInvalidIat, ErrNotYetValid, ErrInvalidNonce, ErrInvalidAzp, ErrInvalidHash,
// ErrInvalidToken — all under ErrOIDC.

The HTTP seam

type Doer interface {
	Do(req *HTTPRequest) (*HTTPResponse, error)
}

A Doer performs one round-trip. A host binds it to go-ruby-net-http / faraday; tests supply an in-memory mock. Nothing in this package opens a socket.

What it consumes

capability source
authorization-URL + token-request construction, PKCE S256, token-response parsing go-ruby-oauth2/oauth2
ID-token JWS parse + signature verify (RS/ES/PS/HS) go-ruby-jwt/jwt
discovery, JWKS→key selection, OIDC claim validation, the flow orchestration this package

API

// Discovery
func Discover(doer Doer, issuer string) (*ProviderMetadata, error)
func ParseProviderMetadata(data []byte) (*ProviderMetadata, error)

// JWKS
func ParseJWKS(data []byte) (*KeySet, error)
func FetchJWKS(doer Doer, uri string) (*KeySet, error)
func NewJWKSCache(doer Doer, uri string, ttl time.Duration) *JWKSCache
func (s *KeySet) Select(kid, alg string) (*Key, error)     // also *JWKSCache
type KeySource interface{ Select(kid, alg string) (*Key, error) }

// ID token
type Verifier struct { Issuer, ClientID string; Keys KeySource; HMACSecret []byte; Nonce string; Leeway time.Duration; AccessToken, Code string; Algorithms []string }
func (v *Verifier) Verify(idToken string) (*IDTokenClaims, error)

// Flow
func NewClient(cfg Config) (*Client, error)
func DiscoverClient(cfg Config, issuer string) (*Client, error)
func (c *Client) AuthCodeURL(p AuthParams) string
func (c *Client) Exchange(code, codeVerifier, nonce string) (*Tokens, error)
func (c *Client) UserInfo(accessToken string) (*UserInfo, error)
func (c *Client) Verifier() *Verifier

Tests & coverage

COVERPKG=$(go list ./... | paste -sd, -)
go test -race -coverpkg="$COVERPKG" -coverprofile=cover.out ./...
go tool cover -func=cover.out | tail -1   # 100.0%

The suite verifies a known-good ID token validates and that tampered, expired, wrong-aud, wrong-iss, wrong-nonce, bad-azp and bad-at_hash/c_hash tokens are rejected — every rejection branch — plus the discovery/JWKS/token/ userinfo error paths (transport error, non-200, malformed JSON, missing kid, unsupported alg, clock skew) over the mocked HTTP seam.

License

BSD-3-Clause — see LICENSE. Copyright the go-ruby-oidc/oidc authors.

WebAssembly

Being pure Go (CGO=0), this library also compiles to WebAssembly — both GOOS=js GOARCH=wasm (browser / Node.js) and GOOS=wasip1 GOARCH=wasm (WASI). CI builds both targets on every push, alongside the six 64-bit native/qemu arches.

GOOS=js     GOARCH=wasm go build ./...   # browser / Node
GOOS=wasip1 GOARCH=wasm go build ./...   # WASI (wasmtime, wasmer, wasmedge, …)

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

Constants

This section is empty.

Variables

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

func DiscoverClient(cfg Config, issuer string) (*Client, error)

DiscoverClient runs discovery for issuer and returns a configured Client. It is a convenience over Discover + NewClient.

func NewClient

func NewClient(cfg Config) (*Client, error)

NewClient builds a Client from a Config. It requires Metadata and a Doer.

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

func (c *Client) Exchange(code, codeVerifier, nonce string) (*Tokens, error)

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.

func (*Client) UserInfo

func (c *Client) UserInfo(accessToken string) (*UserInfo, error)

UserInfo calls the provider's userinfo endpoint with the bearer access token and returns the parsed claims.

func (*Client) Verifier

func (c *Client) Verifier() *Verifier

Verifier returns an ID-token Verifier bound to this client's provider issuer, client id, JWKS cache and HMAC secret (the client secret). Callers may set Nonce/AccessToken/Code before Verify for the optional checks.

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.

func (DoerFunc) Do

func (f DoerFunc) Do(req *HTTPRequest) (*HTTPResponse, error)

Do calls f(req).

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

func (*Error) Error

func (e *Error) Error() string

Error implements the error interface.

func (*Error) Unwrap

func (e *Error) Unwrap() []error

Unwrap exposes the family sentinel and, when present, the wrapped cause, so errors.Is matches both the oidc family and the original error.

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

func (c *IDTokenClaims) Nonce() string

Nonce returns the nonce 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

func NewJWKSCache(doer Doer, uri string, ttl time.Duration) *JWKSCache

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.

func (*JWKSCache) Get

func (c *JWKSCache) Get() (*KeySet, error)

Get returns the cached key set, fetching (or refetching a stale set) as needed.

func (*JWKSCache) Select

func (c *JWKSCache) Select(kid, alg string) (*Key, error)

Select resolves a key by kid/alg from the cache. On a miss against a cached (not freshly fetched) set, it refetches once — covering a rotated signing key — and retries.

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.

func (*Key) PublicKey

func (k *Key) PublicKey() crypto.PublicKey

PublicKey returns the parsed crypto public key (*rsa.PublicKey or *ecdsa.PublicKey), the value the jwt verifier consumes.

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

func FetchJWKS(doer Doer, uri string) (*KeySet, error)

FetchJWKS retrieves and parses a key set from a JWKS URI over the HTTP seam, requiring a 200 response.

func ParseJWKS

func ParseJWKS(data []byte) (*KeySet, error)

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.

func (*KeySet) Find

func (s *KeySet) Find(kid string) *Key

Find returns the key with the given kid, or nil.

func (*KeySet) Keys

func (s *KeySet) Keys() []*Key

Keys returns the set's keys in document order. The slice must not be mutated.

func (*KeySet) Select

func (s *KeySet) Select(kid, alg string) (*Key, error)

Select resolves the verification key for a token, mirroring how a client picks the JWKS key: a non-empty kid selects that exact key; an empty kid falls back to the sole signing key whose type matches alg (rejecting an ambiguous set).

type KeySource

type KeySource interface {
	Select(kid, alg string) (*Key, error)
}

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

func FetchUserInfo(doer Doer, endpoint, accessToken string) (*UserInfo, error)

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

func ParseUserInfo(data []byte) (*UserInfo, error)

ParseUserInfo decodes a UserInfo JSON response, requiring a non-empty sub.

func (*UserInfo) Get

func (u *UserInfo) Get(name string) (any, bool)

Get returns a claim value and whether it is present.

func (*UserInfo) Raw

func (u *UserInfo) Raw() map[string]any

Raw returns the full decoded claim set.

func (*UserInfo) Subject

func (u *UserInfo) Subject() string

Subject returns the sub claim.

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.

Jump to

Keyboard shortcuts

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