oidcprovider

package
v0.23.0 Latest Latest
Warning

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

Go to latest
Published: Aug 26, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// EventOIDCClientRegistered is published when a new OAuth client is registered.
	EventOIDCClientRegistered = "oidc:client_registered"

	// EventOIDCAuthCodeIssued is published when an authorization code is successfully generated.
	EventOIDCAuthCodeIssued = "oidc:auth_code_issued"

	// EventOIDCTokenIssued is published when tokens (Access, Refresh, ID Token) are issued.
	EventOIDCTokenIssued = "oidc:token_issued"

	// EventOIDCTokenRefreshed is published when tokens are exchanged via a refresh token.
	EventOIDCTokenRefreshed = "oidc:token_refreshed"

	// EventOIDCConsentGranted is published when a user grants scope consent to a client.
	EventOIDCConsentGranted = "oidc:consent_granted"

	// EventOIDCConsentRevoked is published when user consent for a client is revoked.
	EventOIDCConsentRevoked = "oidc:consent_revoked"
)
View Source
const DefaultJWKKeyID = "oidc-provider-key-1"
View Source
const GrantTypeAuthorizationCode = "authorization_code"

GrantTypeAuthorizationCode is the RFC 6749 authorization_code grant type string.

View Source
const GrantTypeRefreshToken = "refresh_token"

GrantTypeRefreshToken is the RFC 6749 refresh_token grant type string.

View Source
const PluginID = "oidc-provider"

PluginID is the unique string identifier for the OIDC Provider plugin ("oidc-provider").

Variables

View Source
var (
	// ErrInvalidClient is returned when client authentication fails or the client application is disabled.
	ErrInvalidClient = errors.New("invalid_client: client authentication failed or client disabled")

	// ErrInvalidGrant is returned when an authorization code or refresh token is invalid, expired, or revoked.
	ErrInvalidGrant = errors.New("invalid_grant: invalid, expired, or revoked authorization code/refresh token")

	// ErrInvalidRequest is returned when required parameters are missing or malformed.
	ErrInvalidRequest = errors.New("invalid_request: missing or invalid parameters")

	// ErrUnauthorizedClient is returned when the client is not authorized to use the requested grant type.
	ErrUnauthorizedClient = errors.New("unauthorized_client: client is not allowed to use this grant type")

	// ErrUnsupportedGrantType is returned when the requested grant_type is unsupported.
	ErrUnsupportedGrantType = errors.New("unsupported_grant_type: grant type is not supported")

	// ErrInvalidScope is returned when the requested scope is invalid or exceeds granted scope.
	ErrInvalidScope = errors.New("invalid_scope: scope is invalid or not granted")

	// ErrAccessDenied is returned when the resource owner or authorization server denies the request.
	ErrAccessDenied = errors.New("access_denied: resource owner or authorization server denied request")

	// ErrCodeAlreadyConsumed is returned when an authorization code is reused, triggering token revocation.
	ErrCodeAlreadyConsumed = errors.New("invalid_grant: authorization code has already been used")

	// ErrPKCEValidationFailed is returned when code_verifier fails SHA256/plain comparison against code_challenge.
	ErrPKCEValidationFailed = errors.New("invalid_grant: code_verifier does not match code_challenge")

	// ErrUserNotFound is returned when the user associated with a grant or token cannot be found.
	ErrUserNotFound = errors.New("oidcprovider: user not found")

	// ErrConsentRequired is returned when interactive user consent is required before authorization.
	ErrConsentRequired = errors.New("oidcprovider: user consent required")

	// ErrInvalidConsentCode is returned when a consent validation code is invalid or expired.
	ErrInvalidConsentCode = errors.New("oidcprovider: invalid consent code")
)

Sentinel errors for the OIDC Provider plugin (RFC 6749 & OpenID Connect Core compliant).

Functions

func ConstantTimeEqual

func ConstantTimeEqual(a, b string) bool

ConstantTimeEqual performs a constant-time comparison of two strings to prevent timing attacks.

func GenerateBase64URLToken

func GenerateBase64URLToken(bytesLength int) (string, error)

GenerateBase64URLToken generates a cryptographically secure base64url token string.

func GenerateRandomString

func GenerateRandomString(length int) (string, error)

GenerateRandomString generates a cryptographically secure random string of specified length.

func HasAllScopes

func HasAllScopes(grantedScopes, requestedScopes []string) bool

HasAllScopes checks if all requestedScopes are present in grantedScopes.

func HasScope

func HasScope(scopes []string, target string) bool

HasScope checks whether a specific target scope is present in a scope slice.

func NormalizeScopes

func NormalizeScopes(scopes []string) string

NormalizeScopes joins a slice of scope strings into a single space-delimited string.

func ParseScopes

func ParseScopes(scopeStr string) []string

ParseScopes splits a space-delimited scope string into a clean slice of scope strings.

func ValidatePKCE

func ValidatePKCE(codeVerifier, codeChallenge, codeChallengeMethod string, allowPlain bool) bool

ValidatePKCE verifies a PKCE code_verifier against a code_challenge using the specified method (RFC 7636).

func ValidateRedirectURI

func ValidateRedirectURI(requestedURI string, allowedURIs []string) bool

ValidateRedirectURI checks if requestedURI strictly matches one of the client's registered redirect_uris.

Types

type AdditionalClaimsFunc

type AdditionalClaimsFunc func(ctx context.Context, user *entity.User, scopes []string, client *OAuthClient) map[string]any

AdditionalClaimsFunc defines a custom callback to append custom claims to ID Tokens or UserInfo responses.

type AuthorizeParams

type AuthorizeParams struct {
	ClientID            string  `json:"client_id"`
	RedirectURI         string  `json:"redirect_uri"`
	ResponseType        string  `json:"response_type"` // Must be "code"
	Scope               string  `json:"scope"`
	State               *string `json:"state,omitempty"`
	Nonce               *string `json:"nonce,omitempty"`
	CodeChallenge       *string `json:"code_challenge,omitempty"`
	CodeChallengeMethod *string `json:"code_challenge_method,omitempty"`
	Prompt              *string `json:"prompt,omitempty"` // "consent", "login", "none"
	UserID              string  `json:"user_id"`
}

AuthorizeParams holds input parameters for an authorization request.

type AuthorizeResponse

type AuthorizeResponse struct {
	RedirectURI     string       `json:"redirect_uri"`
	Code            *string      `json:"code,omitempty"`
	State           *string      `json:"state,omitempty"`
	RequiresConsent bool         `json:"requires_consent"`
	ConsentCode     *string      `json:"consent_code,omitempty"`
	ScopesRequested []string     `json:"scopes_requested,omitempty"`
	ClientInfo      *OAuthClient `json:"client_info,omitempty"`
}

AuthorizeResponse holds the outcome of an authorization request.

type ClientType

type ClientType string

ClientType defines the OAuth 2.0 / OIDC client application type.

const (
	// ClientTypeWeb represents confidential server-side web applications capable of keeping secrets.
	ClientTypeWeb ClientType = "web"

	// ClientTypeNative represents native mobile or desktop applications.
	ClientTypeNative ClientType = "native"

	// ClientTypeUserAgentBased represents Single Page Applications (SPAs) executing in a browser.
	ClientTypeUserAgentBased ClientType = "user-agent-based"

	// ClientTypePublic represents public clients incapable of storing client secrets securely.
	ClientTypePublic ClientType = "public"
)

type Config

type Config struct {
	Issuer                         string
	BaseURL                        string
	AccessTokenExpiresIn           time.Duration
	RefreshTokenExpiresIn          time.Duration
	CodeExpiresIn                  time.Duration
	SupportedScopes                []string
	DefaultScope                   string
	RequirePKCE                    bool
	AllowPlainCodeChallenge        bool
	AllowDynamicClientRegistration bool
	StoreClientSecretMode          SecretStoreMode
	SigningAlgorithm               string // "RS256" or "HS256"
	PrivateKey                     *rsa.PrivateKey
	SecretKey                      []byte
	ConsentPageURL                 *string
	LoginPageURL                   string
	GetAdditionalClaims            AdditionalClaimsFunc
}

Config holds configuration settings for the OIDC Provider plugin.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns a Config struct pre-populated with standard OIDC defaults.

type DiscoveryMetadata

type DiscoveryMetadata 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,omitempty"`
	EndSessionEndpoint                string   `json:"end_session_endpoint,omitempty"`
	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"`
	TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported"`
}

DiscoveryMetadata represents the OpenID Connect Discovery 1.0 JSON payload (/.well-known/openid-configuration).

type ExchangeTokenParams

type ExchangeTokenParams struct {
	GrantType    string  `json:"grant_type"` // "authorization_code" or "refresh_token"
	Code         *string `json:"code,omitempty"`
	RefreshToken *string `json:"refresh_token,omitempty"`
	RedirectURI  *string `json:"redirect_uri,omitempty"`
	ClientID     string  `json:"client_id"`
	ClientSecret *string `json:"client_secret,omitempty"`
	CodeVerifier *string `json:"code_verifier,omitempty"`
}

ExchangeTokenParams holds parameters submitted to the token endpoint.

type GrantConsentParams

type GrantConsentParams struct {
	ClientID    string   `json:"client_id"`
	UserID      string   `json:"user_id"`
	ConsentCode string   `json:"consent_code"`
	Accept      bool     `json:"accept"`
	Scopes      []string `json:"scopes"`
}

GrantConsentParams holds input parameters when an authenticated user approves client scopes.

type JWKKey

type JWKKey struct {
	Kty string `json:"kty"`
	Use string `json:"use"`
	Alg string `json:"alg"`
	Kid string `json:"kid"`
	N   string `json:"n,omitempty"`
	E   string `json:"e,omitempty"`
	K   string `json:"k,omitempty"`
}

JWKKey represents a single JSON Web Key in a JWKS set.

type JWKS

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

JWKS represents a JSON Web Key Set payload.

type OAuthClient

type OAuthClient struct {
	ID           string     `json:"id"`
	ClientID     string     `json:"client_id"`
	ClientSecret *string    `json:"client_secret,omitempty"`
	Type         ClientType `json:"type"`
	Name         string     `json:"name"`
	Icon         *string    `json:"icon,omitempty"`
	Metadata     *string    `json:"metadata,omitempty"`
	RedirectURIs []string   `json:"redirect_uris"`
	Disabled     bool       `json:"disabled"`
	UserID       *string    `json:"user_id,omitempty"`
	SkipConsent  bool       `json:"skip_consent,omitempty"`
	CreatedAt    time.Time  `json:"created_at"`
	UpdatedAt    time.Time  `json:"updated_at"`
}

OAuthClient represents a registered OAuth 2.0 / OIDC client application entity.

type OAuthCode

type OAuthCode struct {
	ID                  string    `json:"id"`
	Code                string    `json:"code"`
	ClientID            string    `json:"client_id"`
	UserID              string    `json:"user_id"`
	RedirectURI         string    `json:"redirect_uri"`
	Scope               string    `json:"scope"`
	State               *string   `json:"state,omitempty"`
	Nonce               *string   `json:"nonce,omitempty"`
	CodeChallenge       *string   `json:"code_challenge,omitempty"`
	CodeChallengeMethod *string   `json:"code_challenge_method,omitempty"`
	ExpiresAt           time.Time `json:"expires_at"`
	Consumed            bool      `json:"consumed"`
	CreatedAt           time.Time `json:"created_at"`
}

OAuthCode represents a single-use authorization code grant (RFC 6749 4.1.2).

type OAuthConsent

type OAuthConsent struct {
	ID           string    `json:"id"`
	ClientID     string    `json:"client_id"`
	UserID       string    `json:"user_id"`
	Scopes       string    `json:"scopes"`
	ConsentGiven bool      `json:"consent_given"`
	CreatedAt    time.Time `json:"created_at"`
	UpdatedAt    time.Time `json:"updated_at"`
}

OAuthConsent represents explicit user consent granted to a client application.

type OAuthToken

type OAuthToken struct {
	ID                    string    `json:"id"`
	AccessToken           string    `json:"access_token"`
	RefreshToken          string    `json:"refresh_token"`
	ClientID              string    `json:"client_id"`
	UserID                string    `json:"user_id"`
	Scope                 string    `json:"scope"`
	AccessTokenExpiresAt  time.Time `json:"access_token_expires_at"`
	RefreshTokenExpiresAt time.Time `json:"refresh_token_expires_at"`
	CreatedAt             time.Time `json:"created_at"`
	UpdatedAt             time.Time `json:"updated_at"`
}

OAuthToken represents an Access Token and Refresh Token pair entity.

type OIDCAuthCodeIssuedPayload

type OIDCAuthCodeIssuedPayload struct {
	Code     string `json:"code"`
	ClientID string `json:"client_id"`
	UserID   string `json:"user_id"`
}

OIDCAuthCodeIssuedPayload defines the event bus payload when an authorization code is issued.

type OIDCClientRegisteredPayload

type OIDCClientRegisteredPayload struct {
	Client *OAuthClient `json:"client"`
}

OIDCClientRegisteredPayload defines the event bus payload when a client application is created.

type OIDCConsentGrantedPayload

type OIDCConsentGrantedPayload struct {
	ClientID string   `json:"client_id"`
	UserID   string   `json:"user_id"`
	Scopes   []string `json:"scopes"`
}

OIDCConsentGrantedPayload defines the event bus payload when consent is given.

type OIDCConsentRevokedPayload

type OIDCConsentRevokedPayload struct {
	ClientID string `json:"client_id"`
	UserID   string `json:"user_id"`
}

OIDCConsentRevokedPayload defines the event bus payload when consent is revoked.

type OIDCTokenIssuedPayload

type OIDCTokenIssuedPayload struct {
	AccessToken  string `json:"access_token"`
	RefreshToken string `json:"refresh_token,omitempty"`
	IDToken      string `json:"id_token,omitempty"`
	ClientID     string `json:"client_id"`
	UserID       string `json:"user_id"`
}

OIDCTokenIssuedPayload defines the event bus payload when an access/id token is issued.

type OIDCTokenRefreshedPayload

type OIDCTokenRefreshedPayload struct {
	NewAccessToken  string `json:"new_access_token"`
	NewRefreshToken string `json:"new_refresh_token,omitempty"`
	ClientID        string `json:"client_id"`
	UserID          string `json:"user_id"`
}

OIDCTokenRefreshedPayload defines the event bus payload when tokens are refreshed.

type Option

type Option func(*Config)

Option represents a functional option for configuring the OIDC Provider plugin.

func WithAdditionalClaims

func WithAdditionalClaims(fn AdditionalClaimsFunc) Option

WithAdditionalClaims registers a callback to inject custom claims into UserInfo and ID Tokens.

func WithAllowPlainCodeChallenge

func WithAllowPlainCodeChallenge(allow bool) Option

WithAllowPlainCodeChallenge enables plain code_challenge_method in PKCE (not recommended).

func WithBaseURL

func WithBaseURL(url string) Option

WithBaseURL sets the base URL used to construct endpoint paths.

func WithConsentPageURL

func WithConsentPageURL(url string) Option

WithConsentPageURL sets the URL of the consent UI page.

func WithIssuer

func WithIssuer(issuer string) Option

WithIssuer sets the OIDC issuer identifier URL (e.g. "https://auth.example.com").

func WithLoginPageURL

func WithLoginPageURL(url string) Option

WithLoginPageURL sets the login redirect URL.

func WithRSAKeys

func WithRSAKeys(privateKey *rsa.PrivateKey) Option

WithRSAKeys configures an RSA private key for RS256 ID Token signing and JWKS export.

func WithRequirePKCE

func WithRequirePKCE(require bool) Option

WithRequirePKCE enforces PKCE (RFC 7636) for authorization code grant requests.

func WithSecretKey

func WithSecretKey(secret []byte) Option

WithSecretKey configures a shared secret key for HS256 ID Token signing.

func WithStoreClientSecretMode

func WithStoreClientSecretMode(mode SecretStoreMode) Option

WithStoreClientSecretMode sets how client_secret values are stored and compared.

func WithSupportedScopes

func WithSupportedScopes(scopes []string) Option

WithSupportedScopes sets the list of supported OIDC scopes.

func WithTokenExpirations

func WithTokenExpirations(access, refresh, code time.Duration) Option

WithTokenExpirations sets access token, refresh token, and authorization code expiration durations.

type Plugin

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

Plugin implements the OpenID Connect 1.0 / OAuth 2.0 Provider plugin for go-modular-auth.

func New

func New(repo Repository, opts ...Option) *Plugin

New instantiates a new OIDC Provider plugin configured with the given repository and options.

func (*Plugin) Authorize

func (p *Plugin) Authorize(ctx context.Context, params AuthorizeParams) (*AuthorizeResponse, error)

Authorize processes an OAuth 2.0 / OIDC authorization request.

func (*Plugin) Config

func (p *Plugin) Config() Config

Config returns a copy of the active plugin configuration.

func (*Plugin) EndSession

func (p *Plugin) EndSession(ctx context.Context, idTokenHint string, postLogoutRedirectURI *string) (string, error)

EndSession processes RP-Initiated Logout.

func (*Plugin) ExchangeToken

func (p *Plugin) ExchangeToken(ctx context.Context, params ExchangeTokenParams) (*TokenResponse, error)

ExchangeToken exchanges an authorization code or refresh token for Access, Refresh, and ID Tokens.

func (*Plugin) GenerateIDToken

func (p *Plugin) GenerateIDToken(ctx context.Context, user *entity.User, client *OAuthClient, scope string, nonce *string, accessToken *string, expiresIn time.Duration) (string, error)

GenerateIDToken constructs and signs an OpenID Connect ID Token JWT.

func (*Plugin) GetClient

func (p *Plugin) GetClient(ctx context.Context, clientID string) (*OAuthClient, error)

GetClient retrieves a registered OAuth client application by client_id.

func (*Plugin) GetDiscoveryMetadata

func (p *Plugin) GetDiscoveryMetadata(ctx context.Context) (*DiscoveryMetadata, error)

GetDiscoveryMetadata generates the OpenID Connect Discovery 1.0 JSON configuration metadata.

func (*Plugin) GetJWKS

func (p *Plugin) GetJWKS(ctx context.Context) (map[string]any, error)

GetJWKS exports the public key set in standard JWKS JSON format.

func (*Plugin) GetUserInfo

func (p *Plugin) GetUserInfo(ctx context.Context, accessToken string) (UserInfoClaims, error)

GetUserInfo returns standard OIDC UserInfo claims for a valid access_token.

func (*Plugin) GrantConsent

func (p *Plugin) GrantConsent(ctx context.Context, params GrantConsentParams) (*AuthorizeResponse, error)

GrantConsent saves user consent and returns authorization parameters.

func (*Plugin) ID

func (p *Plugin) ID() string

ID returns the unique string identifier for the plugin ("oidc-provider").

func (*Plugin) Init

func (p *Plugin) Init(ctx *plugin.Context) error

Init initializes the plugin within the shared plugin context environment.

func (*Plugin) RegisterClient

func (p *Plugin) RegisterClient(ctx context.Context, params RegisterClientParams) (*OAuthClient, error)

RegisterClient registers a new OAuth 2.0 / OIDC client application.

func (*Plugin) ServeDiscoveryMetadata added in v0.20.0

func (p *Plugin) ServeDiscoveryMetadata(w http.ResponseWriter, r *http.Request)

ServeDiscoveryMetadata is a net/http handler for serving /.well-known/openid-configuration.

func (*Plugin) ServeJWKS added in v0.20.0

func (p *Plugin) ServeJWKS(w http.ResponseWriter, r *http.Request)

ServeJWKS is a net/http handler for serving /.well-known/jwks.json.

func (*Plugin) VerifyJWT

func (p *Plugin) VerifyJWT(tokenStr string) (map[string]any, error)

VerifyJWT verifies a JWT token signature and decodes its claims map.

type RegisterClientParams

type RegisterClientParams struct {
	Name         string     `json:"name"`
	Type         ClientType `json:"type"`
	RedirectURIs []string   `json:"redirect_uris"`
	Icon         *string    `json:"icon,omitempty"`
	Metadata     *string    `json:"metadata,omitempty"`
	UserID       *string    `json:"user_id,omitempty"`
	SkipConsent  bool       `json:"skip_consent,omitempty"`
}

RegisterClientParams holds input parameters when registering a new OAuth/OIDC client application.

type Repository

type Repository interface {
	// CreateClient persists a new OAuth client application in storage.
	//
	// Function:
	//   Used during dynamic client registration or administrative client creation.
	//
	// Storage:
	//   Database (GORM / SQL) - Relational client entity persistence.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - client: The OAuthClient entity to persist.
	//
	// Returns:
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   INSERT INTO oidc_clients (id, client_id, client_secret, name, redirect_uris, grant_types, created_at, updated_at)
	//   VALUES ($1, $2, $3, $4, $5, $6, $7, $8);
	CreateClient(ctx context.Context, client *OAuthClient) error

	// FindByClientID retrieves an OAuth client application by its public client_id.
	//
	// Function:
	//   Used during authorize, token exchange, userinfo, and client authentication requests.
	//
	// Storage:
	//   Both (Cache-Aside Strategy) - Cached by client_id in Redis/memory.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - clientID: The unique public OAuth client ID.
	//
	// Returns:
	//   - *OAuthClient: Matching OAuth client entity if found.
	//   - error: ErrInvalidClient if not found, or database error.
	//
	// Example SQL:
	//   SELECT id, client_id, client_secret, name, redirect_uris, grant_types, created_at, updated_at FROM oidc_clients WHERE client_id = $1 LIMIT 1;
	//
	// Example Cache (Redis):
	//   val, err := rdb.Get(ctx, "oidc:client:" + clientID).Bytes()
	FindByClientID(ctx context.Context, clientID string) (*OAuthClient, error)

	// UpdateClient updates mutable fields of an existing OAuth client.
	//
	// Function:
	//   Used during administrative client updates or secret rotation.
	//
	// Storage:
	//   Database (GORM / SQL) - Client update.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - client: Modified OAuthClient entity.
	//
	// Returns:
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   UPDATE oidc_clients SET name = $1, redirect_uris = $2, grant_types = $3, updated_at = $4 WHERE client_id = $5;
	UpdateClient(ctx context.Context, client *OAuthClient) error

	// DeleteClient removes an OAuth client record from persistent storage by client_id.
	//
	// Function:
	//   Used when unregistering or purging an OAuth client application.
	//
	// Storage:
	//   Database (GORM / SQL) - Client record removal.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - clientID: The unique public OAuth client ID.
	//
	// Returns:
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   DELETE FROM oidc_clients WHERE client_id = $1;
	DeleteClient(ctx context.Context, clientID string) error

	// ListClientsByUserID retrieves all OAuth clients owned/registered by a specific user.
	//
	// Function:
	//   Used in developer portals or user settings to list user-created OAuth applications.
	//
	// Storage:
	//   Database (GORM / SQL) - User client apps query.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - userID: The unique user identifier.
	//
	// Returns:
	//   - []*OAuthClient: Slice of matching OAuth clients.
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   SELECT id, client_id, client_secret, name, redirect_uris, grant_types, created_at, updated_at FROM oidc_clients WHERE user_id = $1;
	ListClientsByUserID(ctx context.Context, userID string) ([]*OAuthClient, error)

	// CreateAuthorizationCode persists a new single-use authorization code grant.
	//
	// Function:
	//   Called at the end of the authorization flow after user consent.
	//
	// Storage:
	//   Cache (Redis / In-Memory TTL) - Short-lived authorization code state.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - code: OAuthCode entity containing code secret, PKCE challenge, and granted scopes.
	//
	// Returns:
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   INSERT INTO oidc_codes (id, code, client_id, user_id, redirect_uri, scope, code_challenge, code_challenge_method, expires_at, created_at)
	//   VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
	//
	// Example Cache (Redis):
	//   err := rdb.Set(ctx, "oidc:code:" + code.Code, bytes, ttl).Err()
	CreateAuthorizationCode(ctx context.Context, code *OAuthCode) error

	// ConsumeAuthorizationCode atomically retrieves and invalidates/deletes an authorization code record.
	//
	// Function:
	//   Called during authorization_code token exchange to prevent code reuse and replay attacks.
	//
	// Storage:
	//   Cache (Redis GETDEL / Memory) - Atomic read-and-delete single-use code consumption.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - code: The authorization code string secret.
	//
	// Returns:
	//   - *OAuthCode: The consumed code grant entity if valid.
	//   - error: ErrInvalidGrant if missing or expired, ErrCodeAlreadyConsumed if reused.
	//
	// Example SQL:
	//   DELETE FROM oidc_codes WHERE code = $1 AND expires_at > $2 RETURNING id, code, client_id, user_id, redirect_uri, scope, code_challenge, code_challenge_method, expires_at, created_at;
	//
	// Example Cache (Redis):
	//   val, err := rdb.GetDel(ctx, "oidc:code:" + code).Bytes()
	ConsumeAuthorizationCode(ctx context.Context, code string) (*OAuthCode, error)

	// DeleteExpiredCodes purges all expired authorization codes from storage.
	//
	// Function:
	//   Called by background cleanup tasks or maintenance crons.
	//
	// Storage:
	//   Database (GORM / SQL) - Bulk code deletion.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//
	// Returns:
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   DELETE FROM oidc_codes WHERE expires_at <= $1;
	DeleteExpiredCodes(ctx context.Context) error

	// CreateTokenPair persists a new access token (and optional refresh token) grant.
	//
	// Function:
	//   Called during token issuance for authorization_code, refresh_token, or client_credentials grants.
	//
	// Storage:
	//   Database (GORM / SQL) - Token pair insertion.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - token: OAuthToken entity containing access_token, refresh_token, and expiration details.
	//
	// Returns:
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   INSERT INTO oidc_tokens (id, access_token, refresh_token, client_id, user_id, scope, access_token_expires_at, refresh_token_expires_at, revoked, created_at)
	//   VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
	CreateTokenPair(ctx context.Context, token *OAuthToken) error

	// FindByAccessToken retrieves token details by matching access_token string.
	//
	// Function:
	//   Used during Introspection, UserInfo, or API authorization token validation.
	//
	// Storage:
	//   Both (Cache-Aside Strategy) - Cached in Redis (`oidc:token:<accessToken>`) for fast token validation.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - accessToken: The raw access token string.
	//
	// Returns:
	//   - *OAuthToken: Matching token grant if found and active.
	//   - error: ErrInvalidGrant if missing, revoked, or expired.
	//
	// Example SQL:
	//   SELECT id, access_token, refresh_token, client_id, user_id, scope, access_token_expires_at, refresh_token_expires_at, revoked, created_at FROM oidc_tokens WHERE access_token = $1 LIMIT 1;
	//
	// Example Cache (Redis):
	//   val, err := rdb.Get(ctx, "oidc:token:" + accessToken).Bytes()
	FindByAccessToken(ctx context.Context, accessToken string) (*OAuthToken, error)

	// FindByRefreshToken retrieves token grant details matching a refresh_token string.
	//
	// Function:
	//   Called during refresh_token grant type token renewal.
	//
	// Storage:
	//   Database (GORM / SQL) - Refresh token query.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - refreshToken: The raw refresh token string.
	//
	// Returns:
	//   - *OAuthToken: Matching token grant if found and valid.
	//   - error: ErrInvalidGrant if missing or expired, ErrRefreshTokenRevoked if already revoked.
	//
	// Example SQL:
	//   SELECT id, access_token, refresh_token, client_id, user_id, scope, access_token_expires_at, refresh_token_expires_at, revoked, created_at FROM oidc_tokens WHERE refresh_token = $1 LIMIT 1;
	FindByRefreshToken(ctx context.Context, refreshToken string) (*OAuthToken, error)

	// RevokeTokenPair marks a specific token grant (by token ID or token string) as revoked.
	//
	// Function:
	//   Called during RFC 7009 Token Revocation or session sign-out.
	//
	// Storage:
	//   Database (GORM / SQL) - Revocation status update.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - tokenID: Primary record ID or token identifier.
	//
	// Returns:
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   UPDATE oidc_tokens SET revoked = true WHERE id = $1 OR access_token = $1 OR refresh_token = $1;
	RevokeTokenPair(ctx context.Context, tokenID string) error

	// RevokeTokensByClientIDAndUserID revokes all active token grants issued to a user for a specific client.
	//
	// Function:
	//   Called when a user revokes access to a third-party client application.
	//
	// Storage:
	//   Database (GORM / SQL) - Bulk token revocation update.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - clientID: OAuth client identifier.
	//   - userID: Target user identifier.
	//
	// Returns:
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   UPDATE oidc_tokens SET revoked = true WHERE client_id = $1 AND user_id = $2;
	RevokeTokensByClientIDAndUserID(ctx context.Context, clientID, userID string) error

	// DeleteExpiredTokens purges all expired and revoked token records from storage.
	//
	// Function:
	//   Called by maintenance tasks to keep storage size optimal.
	//
	// Storage:
	//   Database (GORM / SQL) - Bulk token deletion.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//
	// Returns:
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   DELETE FROM oidc_tokens WHERE refresh_token_expires_at <= $1 OR (refresh_token IS NULL AND access_token_expires_at <= $1);
	DeleteExpiredTokens(ctx context.Context) error

	// GetConsent retrieves persistent user consent granted to a client application.
	//
	// Function:
	//   Called during interactive authorization to check if prompt=none or remembered consent applies.
	//
	// Storage:
	//   Database (GORM / SQL) - Consent record query.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - clientID: Target OAuth client ID.
	//   - userID: User identifier.
	//
	// Returns:
	//   - *OAuthConsent: User consent record if found.
	//   - error: ErrConsentRequired if not consented, or database error.
	//
	// Example SQL:
	//   SELECT id, client_id, user_id, scopes, granted_at FROM oidc_consents WHERE client_id = $1 AND user_id = $2 LIMIT 1;
	GetConsent(ctx context.Context, clientID, userID string) (*OAuthConsent, error)

	// SaveConsent creates or updates remembered user consent for a client application.
	//
	// Function:
	//   Called when a user approves scopes during authorization prompt.
	//
	// Storage:
	//   Database (GORM / SQL) - Consent record insertion/upsert.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - consent: OAuthConsent entity to persist.
	//
	// Returns:
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   INSERT INTO oidc_consents (id, client_id, user_id, scopes, granted_at) VALUES ($1, $2, $3, $4, $5)
	//   ON CONFLICT (client_id, user_id) DO UPDATE SET scopes = $4, granted_at = $5;
	SaveConsent(ctx context.Context, consent *OAuthConsent) error

	// RevokeConsent removes remembered consent granted by a user to a client application.
	//
	// Function:
	//   Called when a user disconnects an authorized application in account settings.
	//
	// Storage:
	//   Database (GORM / SQL) - Consent record deletion.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - clientID: Target OAuth client ID.
	//   - userID: User identifier.
	//
	// Returns:
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   DELETE FROM oidc_consents WHERE client_id = $1 AND user_id = $2;
	RevokeConsent(ctx context.Context, clientID, userID string) error

	// GetUserByID fetches user profile details to populate UserInfo response claims.
	//
	// Function:
	//   Called during OpenID Connect /userinfo endpoint processing or ID token claim assembly.
	//
	// Storage:
	//   Database (GORM / SQL) - User primary key lookup.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - userID: Target user identifier.
	//
	// Returns:
	//   - *entity.User: Matching user entity if found.
	//   - error: ErrUserNotFound if missing, or database error.
	//
	// Example SQL:
	//   SELECT id, email, name, email_verified, created_at, updated_at FROM users WHERE id = $1 LIMIT 1;
	GetUserByID(ctx context.Context, userID string) (*entity.User, error)
}

Repository defines the persistent storage contract required by the OIDC Provider plugin. Implement this interface on your custom database adapter (e.g. PostgreSQL, MySQL, SQLite, MongoDB, GORM).

Implementation Example (GORM / database/sql):

type GormOIDCProviderRepository struct {
	db *gorm.DB
}

func (r *GormOIDCProviderRepository) FindByClientID(ctx context.Context, clientID string) (*oidcprovider.OAuthClient, error) {
	var c oidcprovider.OAuthClient
	if err := r.db.WithContext(ctx).Where("client_id = ?", clientID).First(&c).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, oidcprovider.ErrInvalidClient
		}
		return nil, err
	}
	return &c, nil
}

Storage and Caching Recommendation (Token Introspection & Authorization Code Cache):

High-traffic OIDC Provider deployments benefit greatly from caching access tokens and client credentials:

  1. Access Token Introspection (`FindByAccessToken`): Cache access token grants in Redis (`oidc:token:<accessToken>`) with TTL matching `access_token_expires_at`.

  2. Authorization Codes (`ConsumeAuthorizationCode`): Store single-use codes in Redis (`oidc:code:<code>`) and consume via `GETDEL` for single-use replay protection.

Recommended Caching Decorator Example:

type CachedOIDCProviderRepository struct {
	dbRepo oidcprovider.Repository
	redis  *redis.Client
}

func (r *CachedOIDCProviderRepository) FindByAccessToken(ctx context.Context, tokenStr string) (*oidcprovider.OAuthToken, error) {
	val, err := r.redis.Get(ctx, "oidc:token:"+tokenStr).Bytes()
	if err == nil {
		var token oidcprovider.OAuthToken
		if json.Unmarshal(val, &token) == nil {
			return &token, nil // Fast Introspection Cache Hit
		}
	}
	token, err := r.dbRepo.FindByAccessToken(ctx, tokenStr)
	if err == nil {
		bytes, _ := json.Marshal(token)
		ttl := time.Until(token.AccessTokenExpiresAt)
		r.redis.Set(ctx, "oidc:token:"+tokenStr, bytes, ttl)
	}
	return token, err
}

type SecretStoreMode

type SecretStoreMode string

SecretStoreMode specifies how client_secret values are stored and verified.

const (
	// SecretStorePlain stores client_secret in plain text.
	SecretStorePlain SecretStoreMode = "plain"

	// SecretStoreHashed stores client_secret as a password hash (Argon2id/Bcrypt).
	SecretStoreHashed SecretStoreMode = "hashed"

	// SecretStoreEncrypted stores client_secret in encrypted form.
	SecretStoreEncrypted SecretStoreMode = "encrypted"
)

type TokenResponse

type TokenResponse struct {
	AccessToken  string `json:"access_token"`
	TokenType    string `json:"token_type"` // "Bearer"
	ExpiresIn    int64  `json:"expires_in"` // seconds
	RefreshToken string `json:"refresh_token,omitempty"`
	IDToken      string `json:"id_token,omitempty"`
	Scope        string `json:"scope,omitempty"`
}

TokenResponse represents a successful token issuance response.

type UserInfoClaims

type UserInfoClaims map[string]any

UserInfoClaims represents standard OpenID Connect UserInfo claims.

Jump to

Keyboard shortcuts

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