oidc

package
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Oct 23, 2026 License: MIT, MIT Imports: 30 Imported by: 0

README

GoModularAuth OpenID Connect (OIDC) Relying Party Plugin

gomodularauth-oidc provides OpenID Connect Relying Party (client) authentication and federated identity integration for GoModularAuth. It implements RFC 6749, RFC 7636 (PKCE), and OpenID Connect Core 1.0 specifications with rigorous zero-trust ID token verification, strict nonce binding, multi-provider discovery, and account linking.

Installation

go get github.com/BladiCreator/go-modular-auth/plugins/oidc

Quick Start

engine, err := auth.New(auth.Config{
    Adapter: adaptermem.New(),
    Cache: coreCache,
    Plugins: []auth.PluginModule{
        oidcPlugin,
    },
})
if err != nil {
    log.Fatalf("failed to initialize auth: %v", err)
}

// Access typed capability
oidcAuth, err := auth.Get[plugins.OIDC](engine)
if err != nil {
    log.Fatalf("failed to resolve OIDC capability: %v", err)
}

Detailed guide: doc/plugins/oidc.md.

Documentation

Overview

Package oidc provides OpenID Connect (OIDC) relying-party authentication and identity integration for GoModularAuth.

Index

Constants

View Source
const (
	EventOIDCAuthenticated   auth.EventName = "oidc.authenticated"
	EventOIDCAccountLinked   auth.EventName = "oidc.account_linked"
	EventOIDCAccountUnlinked auth.EventName = "oidc.account_unlinked"
	EventOIDCFailed          auth.EventName = "oidc.failed"
)

Event names emitted by the OIDC plugin.

View Source
const ModelOIDCStates = "oidc_states"

ModelOIDCStates is the schema model identifier for OIDC transient state sessions.

View Source
const PluginID = "oidc"

PluginID identifies this plugin in the GoModularAuth runtime.

Variables

View Source
var (
	// ErrProviderNotFound indicates that the requested OIDC provider is not configured.
	ErrProviderNotFound = errors.New("oidc: provider not found")

	// ErrInvalidInput indicates that mandatory input fields were missing or malformed.
	ErrInvalidInput = errors.New("oidc: invalid input")

	// ErrInvalidState indicates that the returned state parameter did not match any stored session.
	ErrInvalidState = errors.New("oidc: invalid state token")

	// ErrStateExpired indicates that the state session has passed its expiration window.
	ErrStateExpired = errors.New("oidc: state token expired")

	// ErrStateConsumed indicates that the state session was already redeemed (replay attack prevented).
	ErrStateConsumed = errors.New("oidc: state token already consumed")

	// ErrMissingIDToken indicates that the token response omitted the required id_token field.
	ErrMissingIDToken = errors.New("oidc: missing id_token in token response")

	// ErrInvalidIDToken indicates that the ID token string was malformed or could not be parsed.
	ErrInvalidIDToken = errors.New("oidc: malformed id_token")

	// ErrSignatureInvalid indicates that the cryptographic JWS signature verification failed.
	ErrSignatureInvalid = errors.New("oidc: id_token signature verification failed")

	// ErrIssuerMismatch indicates that the ID token issuer (iss) did not match the configured provider issuer.
	ErrIssuerMismatch = errors.New("oidc: issuer claim mismatch")

	// ErrAudienceMismatch indicates that the ID token audience (aud) did not include the client_id.
	ErrAudienceMismatch = errors.New("oidc: audience claim mismatch")

	// ErrNonceMismatch indicates that the ID token nonce claim did not match the expected state nonce.
	ErrNonceMismatch = errors.New("oidc: nonce claim mismatch")

	// ErrTokenExpired indicates that the ID token has expired (exp claim in the past).
	ErrTokenExpired = errors.New("oidc: id_token has expired")

	// ErrKeyNotFound indicates that the signing key (kid) could not be located in the provider JWKS set.
	ErrKeyNotFound = errors.New("oidc: signing key not found in jwks")

	// ErrAccountConflict indicates an email collision where linking is disallowed.
	ErrAccountConflict = errors.New("oidc: an account with this email already exists")

	// ErrAccountLinkingRequiresConsent indicates that linking requires explicit user approval.
	ErrAccountLinkingRequiresConsent = errors.New("oidc: account linking requires user consent")

	// ErrUnverifiedEmailLinkingProhibited indicates that linking was refused because an email is unverified.
	ErrUnverifiedEmailLinkingProhibited = errors.New("oidc: cannot link account with unverified email")

	// ErrCannotUnlinkSoleAuthMethod indicates refusal to unlink the user's only remaining login credential.
	ErrCannotUnlinkSoleAuthMethod = errors.New("oidc: cannot unlink sole authentication method")

	// ErrDiscoveryFailed indicates that OpenID Connect Discovery (.well-known/openid-configuration) failed.
	ErrDiscoveryFailed = errors.New("oidc: provider discovery failed")
)

Functions

func RegisterSchema

func RegisterSchema(s *schema.Schema) error

RegisterSchema registers the OIDC transient state session model.

Types

type Action

type Action string

Action denotes the purpose of the OIDC authorization flow.

const (
	ActionSignIn Action = "sign_in"
	ActionLink   Action = "link"
)

type AuthResult

type AuthResult struct {
	User          *user.User       `json:"user"`
	Account       *account.Account `json:"account"`
	Session       *session.Session `json:"session"`
	Token         string           `json:"token"`
	IsNewUser     bool             `json:"is_new_user"`
	IDTokenClaims *IDTokenClaims   `json:"id_token_claims"`
	TokenResponse *TokenResponse   `json:"token_response"`
}

AuthResult is returned when an OIDC authorization code flow succeeds.

type AuthenticatedPayload

type AuthenticatedPayload struct {
	UserID     string    `json:"user_id"`
	AccountID  string    `json:"account_id"`
	Provider   string    `json:"provider"`
	Subject    string    `json:"subject"`
	IsNewUser  bool      `json:"is_new_user"`
	OccurredAt time.Time `json:"occurred_at"`
}

AuthenticatedPayload describes an OIDC sign-in or registration event.

type BeginAuthInput

type BeginAuthInput struct {
	Provider    string            `json:"provider"`
	RedirectURI string            `json:"redirect_uri"`
	Scopes      []string          `json:"scopes,omitempty"`
	Prompt      string            `json:"prompt,omitempty"`
	Metadata    map[string]string `json:"metadata,omitempty"`
}

BeginAuthInput contains parameters to initiate an OIDC authentication flow.

type BeginAuthResult

type BeginAuthResult struct {
	AuthURL      string    `json:"auth_url"`
	State        string    `json:"state"`
	Nonce        string    `json:"nonce"`
	CodeVerifier string    `json:"code_verifier"`
	ExpiresAt    time.Time `json:"expires_at"`
}

BeginAuthResult contains the authorization URL and state session for redirection.

type BeginLinkInput

type BeginLinkInput struct {
	UserID      string            `json:"user_id"`
	Provider    string            `json:"provider"`
	RedirectURI string            `json:"redirect_uri"`
	Scopes      []string          `json:"scopes,omitempty"`
	Prompt      string            `json:"prompt,omitempty"`
	Metadata    map[string]string `json:"metadata,omitempty"`
}

BeginLinkInput initiates linking an external OIDC provider to an existing authenticated user.

type CompleteAuthInput

type CompleteAuthInput struct {
	Provider    string              `json:"provider"`
	Code        string              `json:"code"`
	State       string              `json:"state"`
	RedirectURI string              `json:"redirect_uri"`
	Session     auth.SessionOptions `json:"session"`
}

CompleteAuthInput provides the authorization code and state token from the redirect callback.

type CompleteLinkInput

type CompleteLinkInput struct {
	Provider    string `json:"provider"`
	Code        string `json:"code"`
	State       string `json:"state"`
	RedirectURI string `json:"redirect_uri"`
}

CompleteLinkInput completes an account linking flow for the user bound during BeginLink.

type Config

type Config struct {
	Providers     map[string]ProviderConfig
	StateTTL      time.Duration
	LinkingPolicy LinkingPolicy
	HTTPClient    HTTPClient
	Clock         func() time.Time
}

Config holds the configuration settings for the OIDC plugin.

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"`
	ResponseTypesSupported           []string `json:"response_types_supported,omitempty"`
	SubjectTypesSupported            []string `json:"subject_types_supported,omitempty"`
	IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported,omitempty"`
	ScopesSupported                  []string `json:"scopes_supported,omitempty"`
}

DiscoveryDocument holds the OpenID Connect discovery metadata fetched from .well-known/openid-configuration.

type Endpoints

type Endpoints struct {
	AuthURL     string `json:"auth_url"`
	TokenURL    string `json:"token_url"`
	JWKSURL     string `json:"jwks_url"`
	UserInfoURL string `json:"userinfo_url,omitempty"`
}

Endpoints holds explicitly configured URLs when discovery is bypassed.

type FailedPayload

type FailedPayload struct {
	Provider   string    `json:"provider"`
	Reason     string    `json:"reason"`
	OccurredAt time.Time `json:"occurred_at"`
}

FailedPayload describes an authentication failure event.

type HTTPClient

type HTTPClient interface {
	Do(req *http.Request) (*http.Response, error)
}

HTTPClient abstracts outbound HTTP request execution. It is satisfied by *http.Client.

type IDTokenClaims

type IDTokenClaims struct {
	Issuer            string         `json:"iss"`
	Subject           string         `json:"sub"`
	Audience          []string       `json:"aud"`
	ExpiresAt         time.Time      `json:"exp"`
	IssuedAt          time.Time      `json:"iat"`
	Nonce             string         `json:"nonce,omitempty"`
	AuthTime          *time.Time     `json:"auth_time,omitempty"`
	AuthorizedParty   string         `json:"azp,omitempty"`
	Email             string         `json:"email,omitempty"`
	EmailVerified     bool           `json:"email_verified,omitempty"`
	Name              string         `json:"name,omitempty"`
	PreferredUsername string         `json:"preferred_username,omitempty"`
	Picture           string         `json:"picture,omitempty"`
	RawClaims         map[string]any `json:"raw_claims"`
}

IDTokenClaims contains cryptographically verified claims parsed from an OIDC ID Token.

func ParseAndVerifyIDToken

func ParseAndVerifyIDToken(rawToken string, cfg ProviderConfig, jwks JWKS, expectedNonce string, now time.Time) (*IDTokenClaims, error)

ParseAndVerifyIDToken verifies the cryptographic signature and claims of an OIDC ID token. CRITICAL: Never trusts unverified claims; verifies signature against JWKS before reading claims.

type JWK

type JWK struct {
	Kty string `json:"kty"`
	Kid string `json:"kid"`
	Use string `json:"use"`
	Alg string `json:"alg"`
	N   string `json:"n"`
	E   string `json:"e"`
	Crv string `json:"crv"`
	X   string `json:"x"`
	Y   string `json:"y"`
}

JWK represents a cryptographic key in JSON Web Key format.

type JWKS

type JWKS struct {
	Keys []JWK `json:"keys"`
}

JWKS represents an RFC 7517 JSON Web Key Set.

type LinkResult

type LinkResult struct {
	Account       *account.Account `json:"account"`
	IDTokenClaims *IDTokenClaims   `json:"id_token_claims"`
}

LinkResult is returned when an account linking operation succeeds.

type LinkedPayload

type LinkedPayload struct {
	UserID     string    `json:"user_id"`
	AccountID  string    `json:"account_id"`
	Provider   string    `json:"provider"`
	Subject    string    `json:"subject"`
	OccurredAt time.Time `json:"occurred_at"`
}

LinkedPayload describes an external OIDC provider linkage event.

type LinkingPolicy

type LinkingPolicy string

LinkingPolicy defines rules for associating external OIDC identities with Core Users.

const (
	// LinkingPolicyVerifiedEmail automatically links when both the external provider and Core record have verified email.
	LinkingPolicyVerifiedEmail LinkingPolicy = "verified_email"

	// LinkingPolicyExplicitOnly requires the user to already be logged in and call BeginLink.
	LinkingPolicyExplicitOnly LinkingPolicy = "explicit_only"

	// LinkingPolicyDisabled prohibits account linking entirely, returning ErrAccountConflict on collision.
	LinkingPolicyDisabled LinkingPolicy = "disabled"
)

type Module

type Module struct {
	// contains filtered or unexported fields
}

Module implements auth.PluginModule and auth.SchemaProvider for OpenID Connect relying party authentication.

func New

func New(opts ...Option) *Module

New creates an installable OIDC PluginModule with options.

func (*Module) ID

func (m *Module) ID() string

ID returns the module's unique identifier.

func (*Module) Init

func (m *Module) Init(h *auth.Host) error

Init configures infrastructure and registers the typed Capability in GoModularAuth.

func (*Module) RegisterSchema

func (m *Module) RegisterSchema(s *schema.Schema) error

RegisterSchema contributes schema models for OIDC state sessions.

type OIDC

type OIDC interface {
	// BeginAuth initiates an OpenID Connect authorization code flow with PKCE and Nonce.
	BeginAuth(ctx context.Context, in BeginAuthInput) (*BeginAuthResult, error)

	// BeginLink initiates an OIDC flow specifically for linking a provider to an existing authenticated user.
	BeginLink(ctx context.Context, in BeginLinkInput) (*BeginAuthResult, error)

	// CompleteAuth completes the OIDC flow: atomically consumes state, exchanges code for tokens,
	// cryptographically verifies the ID token, provisions or locates Core User & Account, and issues a Core Session.
	CompleteAuth(ctx context.Context, in CompleteAuthInput) (*AuthResult, error)

	// CompleteLink completes an account linking flow for the user bound during BeginLink.
	CompleteLink(ctx context.Context, in CompleteLinkInput) (*LinkResult, error)

	// UnlinkAccount removes the link between a user and an OIDC provider.
	UnlinkAccount(ctx context.Context, userID, provider string) error

	// ListAccounts returns all external OIDC accounts linked to the user.
	ListAccounts(ctx context.Context, userID string) ([]*account.Account, error)

	// ValidateIDToken cryptographically verifies a raw ID token against the provider's JWKS and issuer rules.
	ValidateIDToken(ctx context.Context, providerID, rawIDToken, expectedNonce string) (*IDTokenClaims, error)

	// GetProvider returns a registered provider configuration.
	GetProvider(providerID string) (*ProviderConfig, error)

	// Discover queries an OpenID Provider's discovery document (.well-known/openid-configuration).
	Discover(ctx context.Context, issuerURL string) (*DiscoveryDocument, error)
}

OIDC is the strongly typed capability provided by the OIDC plugin. Applications and transport adapters retrieve this capability via:

oidcAuth, err := auth.Get[plugins.OIDC](engine)

type Option

type Option func(*Config)

Option modifies Config settings.

func WithClock

func WithClock(clk func() time.Time) Option

WithClock overrides the system clock for testing.

func WithHTTPClient

func WithHTTPClient(client HTTPClient) Option

WithHTTPClient overrides the default HTTP client used for discovery, token exchange, and JWKS fetching.

func WithLinkingPolicy

func WithLinkingPolicy(policy LinkingPolicy) Option

WithLinkingPolicy configures account linking rules on email collision.

func WithProvider

func WithProvider(p ProviderConfig) Option

WithProvider registers an OpenID Connect Relying Party identity provider configuration.

func WithStateTTL

func WithStateTTL(ttl time.Duration) Option

WithStateTTL sets the validity window of authentication state tokens.

type PKCE

type PKCE struct {
	Verifier  string
	Challenge string
	Method    string
}

PKCE holds a generated RFC 7636 Proof Key for Code Exchange pair.

func GeneratePKCE

func GeneratePKCE() (*PKCE, error)

GeneratePKCE creates a cryptographically secure S256 PKCE code verifier and challenge.

type ProviderConfig

type ProviderConfig struct {
	ID                         string        `json:"id"`
	Issuer                     string        `json:"issuer"`
	ClientID                   string        `json:"client_id"`
	ClientSecret               string        `json:"client_secret"`
	RedirectURI                string        `json:"redirect_uri"`
	Scopes                     []string      `json:"scopes"`
	DiscoveryURL               string        `json:"discovery_url,omitempty"`
	CustomEndpoints            *Endpoints    `json:"custom_endpoints,omitempty"`
	SkipIssuerCheck            bool          `json:"skip_issuer_check,omitempty"`
	InsecureSkipSignatureCheck bool          `json:"insecure_skip_signature_check,omitempty"`
	ClockSkew                  time.Duration `json:"clock_skew,omitempty"`
}

ProviderConfig configures an external OpenID Connect Relying Party identity provider.

type StateSession

type StateSession struct {
	State        string            `json:"state"`
	Provider     string            `json:"provider"`
	RedirectURI  string            `json:"redirect_uri"`
	CodeVerifier string            `json:"code_verifier,omitempty"`
	Nonce        string            `json:"nonce,omitempty"`
	Action       Action            `json:"action"`
	UserID       string            `json:"user_id,omitempty"`
	Metadata     map[string]string `json:"metadata,omitempty"`
	ConsumedAt   *time.Time        `json:"consumed_at,omitempty"`
	ExpiresAt    time.Time         `json:"expires_at"`
	CreatedAt    time.Time         `json:"created_at"`
}

StateSession represents an ephemeral, single-use state binding for an in-flight OIDC authentication flow.

func (*StateSession) Expired

func (s *StateSession) Expired(now time.Time) bool

Expired reports whether the state session has expired relative to the given time.

func (*StateSession) IsConsumed

func (s *StateSession) IsConsumed() bool

IsConsumed reports whether the state session has already been redeemed.

type TokenResponse

type TokenResponse struct {
	AccessToken  string `json:"access_token"`
	IDToken      string `json:"id_token"`
	RefreshToken string `json:"refresh_token,omitempty"`
	TokenType    string `json:"token_type"`
	ExpiresIn    int    `json:"expires_in"`
}

TokenResponse contains the raw tokens returned by the OIDC Token Endpoint.

type UnlinkedPayload

type UnlinkedPayload struct {
	UserID     string    `json:"user_id"`
	AccountID  string    `json:"account_id"`
	Provider   string    `json:"provider"`
	OccurredAt time.Time `json:"occurred_at"`
}

UnlinkedPayload describes an unlinking event.

Directories

Path Synopsis
Package migrations contains SQL schema migrations for the oidc plugin.
Package migrations contains SQL schema migrations for the oidc plugin.
Package plugins exposes capability aliases for GoModularAuth plugins.
Package plugins exposes capability aliases for GoModularAuth plugins.

Jump to

Keyboard shortcuts

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