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

oidc

package
v0.0.12 Latest Latest
Warning

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

Go to latest
Published: Sep 20, 2026 License: Apache-2.0, MIT Imports: 41 Imported by: 0

Documentation

Overview

Package oidc is the OpenID Connect / OAuth 2.0 provider surface: /authorize + PAR (root router), /api/oidc/token, userinfo, introspect, revoke, end_session. One flat package, file-per-area; split into subpackages only if a coherent boundary emerges.

Files: schema.go (contracts), client.go (relying-party clients), authorize.go (authorize + interaction), token.go (codes, tokens, refresh rotation), userinfo.go, store.go (persistence).

Authentication happens in identity features (passkeys, sessions); this package consumes identity contracts via consumer-side adapters.

Index

Constants

View Source
const (
	SubjectUser   = "user"
	SubjectClient = "client"
)

Subject types mirror the apiaccess grant sides; plain strings keep this package free of the identity tree.

View Source
const (
	AuthorizePath = "/authorize"
	TokenPath     = "/api/oidc/token"
	UserInfoPath  = "/api/oidc/userinfo"
	// InteractionPath is the SPA page resolving an interaction
	// session (sign-in / consent); the API lives under /api/oidc/interaction.
	InteractionPath = "/interaction"

	// ClientsAPIPath mounts the admin client CRUD under /api.
	ClientsAPIPath = "/api/oidc/clients"

	// DefaultAuthorizationCodeTTL bounds the one-time code lifetime.
	DefaultAuthorizationCodeTTL = 2 * time.Minute
	// DefaultAccessTokenTTL and DefaultRefreshTokenTTL are the
	// provider defaults; a client's own durations override both.
	DefaultAccessTokenTTL  = 60 * time.Minute
	DefaultRefreshTokenTTL = 30 * 24 * time.Hour
	// DefaultInteractionSessionTTL bounds the sign-in/consent bridge.
	DefaultInteractionSessionTTL = 15 * time.Minute

	// DefaultDeviceCodeTTL bounds the device authorization (RFC 8628
	// 6.1: the verification_uri_complete window).
	DefaultDeviceCodeTTL = 15 * time.Minute
	// DevicePollInterval is the minimum seconds between token polls
	// (RFC 8628 3.5: slow_down backoff baseline).
	DevicePollInterval = 5
	// DeviceUserCodeLength is the random part of the user code,
	// 7 unambiguous characters after the "E" prefix.
	DeviceUserCodeLength = 7
	// DeviceUserCodePrefix labels a tango device user code.
	DeviceUserCodePrefix = "E"
	// DeviceVerificationPath is the SPA page where the user enters
	// the user code, joined onto the public base URL.
	DeviceVerificationPath = "/device"
	// DefaultPARTTL bounds a pushed authorization request_uri
	// lifetime; RFC 9126 3.2.2 recommends keeping it short.
	DefaultPARTTL = 1 * time.Minute

	// GrantDeviceCode is the RFC 8628 token grant type.
	GrantDeviceCode = "urn:ietf:params:oauth:grant-type:device_code"
)

Endpoint paths, token lifetimes, and protocol constants.

View Source
const (
	ScopeOpenID  = "openid"
	ScopeEmail   = "email"
	ScopeProfile = "profile"
	ScopeGroups  = "groups"
)

Scopes understood by this provider.

View Source
const (
	DeviceStatusPending  = "pending"
	DeviceStatusApproved = "approved"
	DeviceStatusDenied   = "denied"
	DeviceStatusConsumed = "consumed"
)

Device authorization statuses; status drives the token poll.

View Source
const (
	KindAuthorizeCode = "authorize_code"
	KindAccessToken   = "access_token"
	KindRefresh       = "refresh_token"
	// KindPAR is a pushed authorization request (RFC 9126): the key
	// is the request_uri payload, unique per push.
	KindPAR = "par"
)

Session kinds stored in oauth2_sessions.

View Source
const MaxMetadataDocument = 1 << 20 // 1 MiB, matches fosite's default

MaxMetadataDocument bounds one metadata document fetch.

View Source
const MetadataDocumentTTL = 24 * time.Hour

MetadataDocumentTTL controls the metadata refresh window.

Variables

View Source
var (
	ErrInvalidInteraction = errors.New("oidc: invalid interaction id")
	ErrInteractionExpired = errors.New("oidc: interaction expired")
)

Interaction lookup outcomes (the HTTP layer maps them onto status codes).

View Source
var (
	ErrInvalidHint           = errors.New("oidc: id_token_hint is invalid")
	ErrMissingAuthorization  = errors.New("oidc: the user has not authorized this client")
	ErrInvalidLogoutCallback = errors.New("oidc: post_logout_redirect_uri is not registered")
)

End-session failures; the handler folds every one of them into the same redirect so callers cannot probe token or authorization state.

View Source
var (
	ErrNotFound = errors.New("oidc: not found")
	// ErrInvalidClient maps to the invalid_client token error.
	ErrInvalidClient = errors.New("oidc: client authentication failed")
	// ErrInvalidGrant maps to the invalid_grant token error: bad or
	// expired code, wrong verifier, replayed refresh token.
	ErrInvalidGrant = errors.New("oidc: grant is invalid")
	// ErrInvalidRequest covers malformed authorize/token parameters.
	ErrInvalidRequest = errors.New("oidc: request is invalid")
	// ErrAccessDenied covers group-restricted clients whose user is
	// not a member of any allowed group.
	ErrAccessDenied = errors.New("oidc: access denied")
	// ErrUnauthorizedClient rejects clients using grants they may
	// not use.
	ErrUnauthorizedClient = errors.New("oidc: client is not authorized for this grant")
)

Errors surfaced to relying parties (RFC 6749 §5.2) and handlers.

Functions

This section is empty.

Types

type APIAccessProvider

type APIAccessProvider interface {
	// AllowedScopesForAudience returns the permission keys the
	// client may request for the API behind the resource, whether
	// such an API exists, and whether the client may reach it at
	// all. A client with access but no permission gets an empty
	// scope list with hasAccess true.
	AllowedScopesForAudience(ctx context.Context, clientID, resource string, subjectType string) (scopes []string, apiExists bool, hasAccess bool, err error)
}

APIAccessProvider is implemented by the identity apiaccess feature; it lets the provider resolve RFC 8707 resources to audiences and per-client permission keys without importing the identity tree.

type AuditLogger

type AuditLogger func(ctx context.Context, event string, params map[string]any)

AuditLogger receives audit events; the registry adapts auditlog. Func type keeps the provider module-free.

type AuthorizationCode

type AuthorizationCode struct {
	CodeHash      string
	Scope         string
	Nonce         string
	CodeChallenge string
	MethodSHA256  bool
	AuthMethod    string
	UserID        string
	ClientID      string
	ExpiresAt     time.Time
}

AuthorizationCode is a one-time code row; Code holds the SHA-256 of the value sent to the relying party.

type AuthorizedClient

type AuthorizedClient struct {
	UserID     string    `json:"user_id"`
	ClientID   string    `json:"client_id"`
	Scopes     []string  `json:"scopes"`
	LastUsedAt time.Time `json:"last_used_at"`
}

AuthorizedClient is a consent record: user × client + granted scopes + last use.

type Client

type Client struct {
	ID                          OIDCClientID
	Name                        string
	Description                 string
	Secrets                     []ClientSecret
	CallbackURLs                []string
	LogoutCallbackURLs          []string
	LaunchURL                   string
	LogoPath                    *string
	MetadataURL                 *string
	ClientType                  string
	IsPublic                    bool
	PKCEEnabled                 bool
	PKCESupported               bool
	RequiresReauthentication    bool
	SkipConsent                 bool
	IsGroupRestricted           bool
	AccessTokenDurationMinutes  int64
	RefreshTokenDurationMinutes int64
	CreatedByID                 *string
	CreatedAt                   time.Time
	AllowedGroupIDs             []string
	MetadataGrantTypes          []string
	MetadataExpiresAt           *time.Time
}

Client is a relying party. Secrets never round-trip: the store keeps SHA-256 hashes in the credentials JSONB and only the create-secret call sees raw.

func (Client) MatchesCallback

func (c Client) MatchesCallback(uri string) bool

MatchesCallback reports whether uri matches a registered callback pattern. Patterns match exactly, or by prefix when they carry a trailing `*` (the callback-url-wildcards contract).

type ClientCreateParams

type ClientCreateParams struct {
	Name                        string
	Description                 string
	CallbackURLs                []string
	LogoutCallbackURLs          []string
	LaunchURL                   string
	IsPublic                    bool
	PKCEEnabled                 bool
	PKCESupported               bool
	SkipConsent                 bool
	IsGroupRestricted           bool
	AccessTokenDurationMinutes  int64
	RefreshTokenDurationMinutes int64
	CreatedByID                 string
	SecretHash                  string
	MetadataURL                 string
	ClientType                  string
	MetadataGrantTypes          []string
	MetadataExpiresAt           *time.Time
}

ClientCreateParams carries admin-supplied client fields.

type ClientImageStore

type ClientImageStore interface {
	Save(ctx context.Context, path string, data io.Reader) error
	Open(ctx context.Context, path string) (io.ReadCloser, int64, error)
	Delete(ctx context.Context, path string) error
}

ClientImageStore is the consumer-side blob adapter; registry wires the shared storage backend here.

type ClientSecret

type ClientSecret struct {
	ID         string
	SecretHash string
	CreatedAt  time.Time
	ExpiresAt  *time.Time
	IsActive   bool
}

ClientSecret is one credentials row (metadata; the hash lives in the JSONB, never serialized to clients).

type ClientUpdateParams

type ClientUpdateParams struct {
	Name                        *string
	Description                 *string
	CallbackURLs                []string // nil keeps; non-nil replaces
	LogoutCallbackURLs          []string
	LaunchURL                   *string
	IsPublic                    *bool
	PKCEEnabled                 *bool
	PKCESupported               *bool
	SkipConsent                 *bool
	IsGroupRestricted           *bool
	RequiresReauthentication    *bool
	AccessTokenDurationMinutes  *int64
	RefreshTokenDurationMinutes *int64
	SecretHash                  *string
	MetadataGrantTypes          []string
}

ClientUpdateParams patches a client; nil fields keep values.

type DeviceCode

type DeviceCode struct {
	DeviceCodeHash string
	UserCodeHash   string
	Scope          string
	Resource       string
	Nonce          string
	ClientID       string
	UserID         *string
	Status         string
	CreatedAt      time.Time
	ExpiresAt      time.Time
	ApprovedAt     *time.Time
	LastPolledAt   *time.Time
}

DeviceCode is one oidc_device_codes row. Both codes live as SHA-256 hashes; Scope/Resource/Nonce carry the original request so the token grant can mint from the committed authorization.

type DocumentFetcher

type DocumentFetcher interface {
	SendRaw(ctx context.Context, method, url string, headers map[string]string, body []byte) (status int, respBody []byte, err error)
}

DocumentFetcher downloads raw documents (consumer-side adapter — *fetcher.Fetcher satisfies it); kept one-way so this module never imports the fetcher package.

type Feature

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

Feature is the wireable oidc unit backed by the provider service. Guards are not stored: the mount call receives the route groups.

func New

func New(service *Service) Feature

New wires the feature to its service.

func (Feature) APIRoutes

func (f Feature) APIRoutes(r chi.Router, _ federation.RouteGroups)

APIRoutes mounts /api/oidc endpoints — implements federation.APIFeature. Paths are RELATIVE to the /api group (the discovery document advertises the absolute /api/oidc/* URLs). The client administration and consent surfaces serve ConnectRPC exclusively (see handler_rpc.go); what remains here is the OIDC protocol surface, the public logo read, and the interaction flow.

func (Feature) ConsentRPCService added in v0.0.12

func (f Feature) ConsentRPCService() (string, http.Handler)

ConsentRPCService returns the consent registration; the surface mixes self-service procedures with the admin-wide views, so the per-procedure guard rides the handler (the composition root mounts it bare).

func (Feature) Name

func (Feature) Name() string

Name implements federation.Feature.

func (Feature) RPCService added in v0.0.12

func (f Feature) RPCService() (string, http.Handler)

RPCService returns the client administration registration; the composition root wraps it with the admin guard.

func (Feature) Routes

func (f Feature) Routes(r chi.Router)

Routes mounts the root-router endpoints (/authorize) — implements federation.RootRoutableFeature.

type InteractionSession

type InteractionSession struct {
	ID                     InteractionSessionID
	ClientID               string
	UserID                 *string
	Scopes                 []string
	Parameters             map[string]any
	ConsentRequired        bool
	AuthenticationRequired bool
	RequestedAt            time.Time
}

InteractionSession bridges /authorize to the SPA sign-in/consent flow. Parameters preserves the original authorize query for the resume.

type InteractionSessionID

type InteractionSessionID = typeid.TypeID[interactionSessionPrefix]

Typed IDs for the OIDC/OAuth 2.0 tables: UUIDv7 suffix, snake_case prefix matching the singular table name (lowercase letters and underscores only, per the TypeID spec). Only IDs that appear in URLs or cross-module references carry a TypeID — token/code rows are addressed by their SHA-256 key and keep their DB uuidv7 IDs.

func InteractionIDFromString

func InteractionIDFromString(raw string) (InteractionSessionID, error)

InteractionIDFromString parses an interaction ID from its string form.

func NewInteractionID

func NewInteractionID() InteractionSessionID

NewInteractionID mints an interaction session ID.

type Lifetimes added in v0.0.12

type Lifetimes struct {
	AccessToken       time.Duration
	RefreshToken      time.Duration
	AuthorizationCode time.Duration
	Interaction       time.Duration
	DeviceCode        time.Duration
	PAR               time.Duration
}

Lifetimes groups the relying-party protocol lifetimes. A zero field keeps the package default, so a caller sets only what it configures.

type MetadataDocument

type MetadataDocument struct {
	ClientID                string   `json:"client_id"`
	ClientName              string   `json:"client_name"`
	ClientDescription       string   `json:"client_description"`
	RedirectURIs            []string `json:"redirect_uris"`
	PostLogoutRedirectURIs  []string `json:"post_logout_redirect_uris"`
	TokenEndpointAuthMethod string   `json:"token_endpoint_auth_method"`
	GrantTypes              []string `json:"grant_types"`
}

MetadataDocument is the RFC 7591-shaped subset tango consumes.

type OAuth2Session

type OAuth2Session struct {
	Kind        string
	Key         string
	RequestID   string
	Active      bool
	RequestData map[string]any
	ClientID    string
	ExpiresAt   *time.Time
	CreatedAt   *time.Time
}

OAuth2Session is one oauth2_sessions row (Fosite-style); Key is the token's jti (or code hash), RequestID ties a token family together for reuse revocation.

type OIDCClientID

type OIDCClientID = typeid.TypeID[oidcClientPrefix]

Typed IDs for the OIDC/OAuth 2.0 tables: UUIDv7 suffix, snake_case prefix matching the singular table name (lowercase letters and underscores only, per the TypeID spec). Only IDs that appear in URLs or cross-module references carry a TypeID — token/code rows are addressed by their SHA-256 key and keep their DB uuidv7 IDs.

func NewID

func NewID() OIDCClientID

NewID mints a client ID; its string form is the clients.id TEXT primary key and the JWT client_id claim.

func OIDCClientIDFromClientKey

func OIDCClientIDFromClientKey(raw string) OIDCClientID

OIDCClientIDFromClientKey parses a stored client key back into the typed form.

func OIDCParseClientID

func OIDCParseClientID(raw string) (OIDCClientID, error)

OIDCParseClientID parses the clients.id TEXT key into the typed form.

type Option

type Option func(*Service)

Option customizes the service.

func WithAPIAccess

func WithAPIAccess(p APIAccessProvider) Option

WithAPIAccess attaches the resource-API resolver used to enforce RFC 8707 resource audiences and permission scopes.

func WithAccessAuthenticator added in v0.0.12

func WithAccessAuthenticator(auth kernel.AccessAuthenticator) Option

WithAccessAuthenticator attaches the internal bearer resolver used by the consent surface's per-procedure guard.

func WithAudit

func WithAudit(l AuditLogger) Option

WithAudit attaches the audit adapter.

func WithCIMDAllowlist

func WithCIMDAllowlist(get func() []string) Option

WithCIMDAllowlist wires the operator allowlist getter (default deny when nil).

func WithImages

func WithImages(images ClientImageStore) Option

WithImages wires the blob backend for the client-logo surface.

func WithLifetimes added in v0.0.12

func WithLifetimes(l Lifetimes) Option

WithLifetimes overrides the protocol lifetimes.

func WithMetadataFetcher

func WithMetadataFetcher(f DocumentFetcher) Option

WithMetadataFetcher wires the outbound document downloader.

func WithScimBinding added in v0.0.12

func WithScimBinding(fn func(ctx context.Context, clientID string) (*ScimBinding, error)) Option

WithScimBinding attaches the per-client SCIM provider lookup used by the GetScimProvider procedure.

type PostgresStore

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

PostgresStore implements Store over the shared pool. Methods live in the per-entity store files (store_client.go, store_authz.go, store_token.go, store_identity.go).

func NewPostgresStore

func NewPostgresStore(store datastore.Store) *PostgresStore

NewPostgresStore builds the production store.

func (*PostgresStore) AccessibleClients

func (s *PostgresStore) AccessibleClients(ctx context.Context, userID string) ([]Client, error)

AccessibleClients lists the clients a user may reach: everything unrestricted plus restricted clients where the user is in at least one allowed group.

func (*PostgresStore) AddClientSecret

func (s *PostgresStore) AddClientSecret(ctx context.Context, clientID OIDCClientID, entry ClientSecret, rawHash string) error

AddClientSecret appends a credentials entry.

func (*PostgresStore) ApproveDeviceCode

func (s *PostgresStore) ApproveDeviceCode(ctx context.Context, userCodeHash, userID string) error

ApproveDeviceCode binds the user, marks the authorization approved, and returns nothing: the device poll sees the status flip.

func (*PostgresStore) AuthorizedClients

func (s *PostgresStore) AuthorizedClients(ctx context.Context, userID *string) ([]AuthorizedClient, error)

AuthorizedClients lists consent records; a nil userID means all users (admin view).

func (*PostgresStore) ConsumeCode

func (s *PostgresStore) ConsumeCode(ctx context.Context, codeHash string) (AuthorizationCode, error)

ConsumeCode deletes and returns the code row atomically — one-time use is enforced by the DELETE itself.

func (*PostgresStore) ConsumeDeviceCode

func (s *PostgresStore) ConsumeDeviceCode(ctx context.Context, deviceCodeHash string) (DeviceCode, error)

ConsumeDeviceCode atomically flips an approved, unexpired authorization to consumed and returns it: the poll that sees approved wins the tokens, every other poll sees consumed.

func (*PostgresStore) CreateClient

func (s *PostgresStore) CreateClient(ctx context.Context, params ClientCreateParams) (Client, error)

CreateClient inserts a client; the caller supplies the initial secret hash (may be empty for public clients). The hash becomes the single active credentials entry.

func (*PostgresStore) CreateInteraction

func (s *PostgresStore) CreateInteraction(ctx context.Context, session InteractionSession) error

CreateInteraction inserts an interaction row (state machine seed).

func (*PostgresStore) CustomClaims

func (s *PostgresStore) CustomClaims(ctx context.Context, userID string) (map[string]string, error)

CustomClaims merges user-scoped and group-scoped claims.

func (*PostgresStore) DeactivateFamily

func (s *PostgresStore) DeactivateFamily(ctx context.Context, requestID string) error

DeactivateFamily revokes every active session row in a token family — refresh-token reuse kills the whole grant.

func (*PostgresStore) DeactivateSession

func (s *PostgresStore) DeactivateSession(ctx context.Context, kind, key string) error

DeactivateSession flips one session row inactive (single-rotation refresh).

func (*PostgresStore) DeleteAuthorization

func (s *PostgresStore) DeleteAuthorization(ctx context.Context, userID, clientID string) error

DeleteAuthorization drops the consent row.

func (*PostgresStore) DeleteClient

func (s *PostgresStore) DeleteClient(ctx context.Context, id OIDCClientID) error

DeleteClient removes a client (cascades to grants and codes).

func (*PostgresStore) DeleteClientSecret

func (s *PostgresStore) DeleteClientSecret(ctx context.Context, clientID OIDCClientID, secretID string) error

DeleteClientSecret removes one credentials entry.

func (*PostgresStore) DeleteInteraction

func (s *PostgresStore) DeleteInteraction(ctx context.Context, id InteractionSessionID) error

DeleteInteraction drops a finished interaction.

func (*PostgresStore) DenyDeviceCode

func (s *PostgresStore) DenyDeviceCode(ctx context.Context, userCodeHash string) error

DenyDeviceCode marks the authorization denied (user clicked deny).

func (*PostgresStore) GetClient

func (s *PostgresStore) GetClient(ctx context.Context, id OIDCClientID) (Client, error)

GetClient resolves one client by ID.

func (*PostgresStore) GetDeviceCode

func (s *PostgresStore) GetDeviceCode(ctx context.Context, deviceCodeHash string) (DeviceCode, error)

GetDeviceCode loads one authorization by its device-code hash (the token poll path); an unknown or expired code is invalid.

func (*PostgresStore) GetDeviceCodeByUserCode

func (s *PostgresStore) GetDeviceCodeByUserCode(ctx context.Context, userCodeHash string) (DeviceCode, error)

GetDeviceCodeByUserCode resolves one authorization by its user-code hash (the browser approve path).

func (*PostgresStore) GetInteraction

GetInteraction resolves one interaction row.

func (*PostgresStore) GetSession

func (s *PostgresStore) GetSession(ctx context.Context, kind, key string) (OAuth2Session, error)

GetSession resolves one session row by kind + key.

func (*PostgresStore) HasAuthorizedClient

func (s *PostgresStore) HasAuthorizedClient(ctx context.Context, userID, clientID string) (bool, error)

HasAuthorizedClient reports whether the user ever granted the client.

func (*PostgresStore) InsertCode

func (s *PostgresStore) InsertCode(ctx context.Context, code AuthorizationCode) error

InsertCode stores a one-time code (hashed) with its TTL.

func (*PostgresStore) InsertDeviceCode

func (s *PostgresStore) InsertDeviceCode(ctx context.Context, code DeviceCode) error

InsertDeviceCode stores a fresh device authorization.

func (*PostgresStore) JTIExists

func (s *PostgresStore) JTIExists(ctx context.Context, jti string) (bool, error)

JTIExists reports whether jti was already spent.

func (*PostgresStore) ListClients

func (s *PostgresStore) ListClients(ctx context.Context) ([]Client, error)

ListClients returns every client with group grants attached.

func (*PostgresStore) PruneDeviceCodes

func (s *PostgresStore) PruneDeviceCodes(ctx context.Context, before time.Time) (int64, error)

PruneDeviceCodes deletes expired device authorizations.

func (*PostgresStore) PutSession

func (s *PostgresStore) PutSession(ctx context.Context, session OAuth2Session) error

PutSession upserts one oauth2_sessions row (kind, key) unique.

func (*PostgresStore) RecordJTI

func (s *PostgresStore) RecordJTI(ctx context.Context, jti string, expiresAt time.Time) error

RecordJTI registers a replayed token ID until it expires.

func (*PostgresStore) RefreshClientMetadata

func (s *PostgresStore) RefreshClientMetadata(ctx context.Context, id OIDCClientID, params ClientUpdateParams) error

RefreshClientMetadata writes the document-owned columns for a CIMD client after a re-fetch (the refresh endpoint; admin updates may never touch these — see UpdateClient's guard).

func (*PostgresStore) RevokeClientTokens

func (s *PostgresStore) RevokeClientTokens(ctx context.Context, clientID, userID string) error

RevokeClientTokens deactivates every active token session row of one user for one client (authorization revocation cascade).

func (*PostgresStore) SetClientGroups

func (s *PostgresStore) SetClientGroups(ctx context.Context, id OIDCClientID, groupIDs []string) error

SetClientGroups replaces the group allowlist for restricted clients. Group IDs arrive as typeid strings (or UUIDs).

func (*PostgresStore) SetClientLogoPath

func (s *PostgresStore) SetClientLogoPath(ctx context.Context, id OIDCClientID, logoPath *string) error

SetClientLogoPath stores or clears (nil) the logo blob path; the meta view's has_logo derives from it.

func (*PostgresStore) TouchDevicePoll

func (s *PostgresStore) TouchDevicePoll(ctx context.Context, deviceCodeHash string, at time.Time) error

TouchDevicePoll stamps the last poll time for slow-down tracking.

func (*PostgresStore) UpdateClient

func (s *PostgresStore) UpdateClient(ctx context.Context, id OIDCClientID, params ClientUpdateParams) (Client, error)

UpdateClient patches the row; nil fields keep their values.

func (*PostgresStore) UpdateDeviceCodeGrant

func (s *PostgresStore) UpdateDeviceCodeGrant(ctx context.Context, userCodeHash, audience, scope string) error

UpdateDeviceCodeGrant stamps the resolved audience and granted scope subset on an approval, so the poll mints exactly what the user consented to.

func (*PostgresStore) UpdateInteraction

func (s *PostgresStore) UpdateInteraction(ctx context.Context, id InteractionSessionID, userID *string, consentRequired *bool) error

UpdateInteraction links the signed-in user / flips consent.

func (*PostgresStore) UpsertAuthorizedClient

func (s *PostgresStore) UpsertAuthorizedClient(ctx context.Context, userID, clientID string, scopes []string) error

UpsertAuthorizedClient remembers the scopes a user granted.

func (*PostgresStore) UserByID

func (s *PostgresStore) UserByID(ctx context.Context, userID string) (UserProfile, error)

UserByID reads the token-claim source user row.

func (*PostgresStore) UserGroups

func (s *PostgresStore) UserGroups(ctx context.Context, userID string) ([]string, error)

UserGroups lists group names for the groups claim.

func (*PostgresStore) UserInGroup

func (s *PostgresStore) UserInGroup(ctx context.Context, userID, groupID string) bool

UserInGroup checks membership (restricted-client allowlist).

type ScimBinding added in v0.0.12

type ScimBinding struct {
	ID           string
	Endpoint     string
	OIDCClientID string
	LastSyncedAt *time.Time
	CreatedAt    time.Time
}

ScimBinding is the per-client SCIM provider projection the GetScimProvider procedure serves; the scimsync feature owns the data and adapts its store onto the lookup function.

type SecretEntryView

type SecretEntryView struct {
	ID        string     `json:"id"`
	CreatedAt time.Time  `json:"created_at"`
	ExpiresAt *time.Time `json:"expires_at,omitzero"`
	IsActive  bool       `json:"is_active"`
}

SecretEntryView is the API shape of a stored secret: metadata only, plus an optional clear prefix.

type Service

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

Service implements the provider flows using injected identity and session providers.

func NewService

func NewService(store Store, keys jwtutils.KeyProvider, issuer string, opts ...Option) *Service

NewService builds the provider service.

func (*Service) AccessTokenTTL added in v0.0.12

func (s *Service) AccessTokenTTL() time.Duration

AccessTokenTTL is the provider default access-token lifetime.

func (*Service) AuthorizationCodeTTL added in v0.0.12

func (s *Service) AuthorizationCodeTTL() time.Duration

AuthorizationCodeTTL bounds the one-time code lifetime.

func (*Service) DeviceCodeTTL added in v0.0.12

func (s *Service) DeviceCodeTTL() time.Duration

DeviceCodeTTL bounds the device authorization window.

func (*Service) EndSession

func (s *Service) EndSession(ctx context.Context, hint, clientID, postLogoutRedirectURI string) (string, error)

EndSession verifies the ID-token hint against the published key set, revokes the token family the ID token was minted with, and resolves the post-logout redirect target. An empty callback means the client registered no logout URLs; the caller falls back to the instance logout page.

func (*Service) HandleDeviceAuthorize

func (s *Service) HandleDeviceAuthorize(w http.ResponseWriter, r *http.Request)

HandleDeviceAuthorize implements POST /api/oidc/device/authorize (client-authenticated, form-encoded). Bare OAuth errors: the caller is a device, not a browser.

func (*Service) HandleDeviceInfo

func (s *Service) HandleDeviceInfo(w http.ResponseWriter, r *http.Request)

HandleDeviceInfo implements GET /api/oidc/device/info: what the consent page shows for a user code.

func (*Service) HandleDeviceVerify

func (s *Service) HandleDeviceVerify(w http.ResponseWriter, r *http.Request)

HandleDeviceVerify implements POST /api/oidc/device/verify: the browser session approves or denies the pending authorization. 204 on success; the SPA re-checks /device/info for the consent state.

func (*Service) HandlePAR

func (s *Service) HandlePAR(w http.ResponseWriter, r *http.Request)

HandlePAR implements POST /api/oidc/par (client-authenticated, form-encoded).

func (*Service) InteractionTTL added in v0.0.12

func (s *Service) InteractionTTL() time.Duration

InteractionTTL bounds the sign-in/consent bridge.

func (*Service) PARTTL added in v0.0.12

func (s *Service) PARTTL() time.Duration

PARTTL bounds a pushed authorization request_uri lifetime.

func (*Service) RefreshTokenTTL added in v0.0.12

func (s *Service) RefreshTokenTTL() time.Duration

RefreshTokenTTL is the provider default refresh-token lifetime.

type Store

type Store interface {
	// Clients.
	CreateClient(ctx context.Context, params ClientCreateParams) (Client, error)
	ListClients(ctx context.Context) ([]Client, error)
	GetClient(ctx context.Context, id OIDCClientID) (Client, error)
	UpdateClient(ctx context.Context, id OIDCClientID, params ClientUpdateParams) (Client, error)
	DeleteClient(ctx context.Context, id OIDCClientID) error
	SetClientGroups(ctx context.Context, id OIDCClientID, groupIDs []string) error

	// One-time authorization codes.
	InsertCode(ctx context.Context, code AuthorizationCode) error
	ConsumeCode(ctx context.Context, codeHash string) (AuthorizationCode, error)

	// Device authorizations (RFC 8628).
	InsertDeviceCode(ctx context.Context, code DeviceCode) error
	GetDeviceCode(ctx context.Context, deviceCodeHash string) (DeviceCode, error)
	GetDeviceCodeByUserCode(ctx context.Context, userCodeHash string) (DeviceCode, error)
	ApproveDeviceCode(ctx context.Context, userCodeHash, userID string) error
	DenyDeviceCode(ctx context.Context, userCodeHash string) error
	// ConsumeDeviceCode atomically flips an approved authorization to
	// consumed and returns it; any other state is ErrInvalidGrant.
	ConsumeDeviceCode(ctx context.Context, deviceCodeHash string) (DeviceCode, error)
	// UpdateDeviceCodeGrant stamps the resolved audience and granted
	// scope on approval.
	UpdateDeviceCodeGrant(ctx context.Context, userCodeHash, audience, scope string) error
	TouchDevicePoll(ctx context.Context, deviceCodeHash string, at time.Time) error
	PruneDeviceCodes(ctx context.Context, before time.Time) (int64, error)

	// OAuth 2.0 session bookkeeping + replay registry.
	PutSession(ctx context.Context, session OAuth2Session) error
	GetSession(ctx context.Context, kind, key string) (OAuth2Session, error)
	DeactivateSession(ctx context.Context, kind, key string) error
	DeactivateFamily(ctx context.Context, requestID string) error
	RecordJTI(ctx context.Context, jti string, expiresAt time.Time) error
	JTIExists(ctx context.Context, jti string) (bool, error)

	// Consent memory: scopes the user already granted a client.
	UpsertAuthorizedClient(ctx context.Context, userID, clientID string, scopes []string) error
	// HasAuthorizedClient reports whether the user ever granted the
	// client; the end-session contract requires an existing grant.
	HasAuthorizedClient(ctx context.Context, userID, clientID string) (bool, error)

	// Interaction bridge for the SPA flow.
	CreateInteraction(ctx context.Context, session InteractionSession) error
	GetInteraction(ctx context.Context, id InteractionSessionID) (InteractionSession, error)
	UpdateInteraction(ctx context.Context, id InteractionSessionID, userID *string, consentRequired *bool) error
	DeleteInteraction(ctx context.Context, id InteractionSessionID) error

	// Claim readers for token issuance.
	UserByID(ctx context.Context, userID string) (UserProfile, error)
	UserGroups(ctx context.Context, userID string) ([]string, error)
	CustomClaims(ctx context.Context, userID string) (map[string]string, error)
	UserInGroup(ctx context.Context, userID, groupID string) bool

	// Client-facing surfaces.
	AccessibleClients(ctx context.Context, userID string) ([]Client, error)
	// AuthorizedClients lists consent records; a nil userID means all users (admin view).
	AuthorizedClients(ctx context.Context, userID *string) ([]AuthorizedClient, error)
	DeleteAuthorization(ctx context.Context, userID, clientID string) error
	// RevokeClientTokens kills a user's active token family rows for one client (authorization revocation cascade).
	RevokeClientTokens(ctx context.Context, clientID, userID string) error

	// Multi-secret management (credentials JSONB is the only storage).
	AddClientSecret(ctx context.Context, clientID OIDCClientID, entry ClientSecret, rawHash string) error
	DeleteClientSecret(ctx context.Context, clientID OIDCClientID, secretID string) error

	// Client logo: blob path and image type.
	SetClientLogoPath(ctx context.Context, id OIDCClientID, path *string) error

	// RefreshClientMetadata rewrites the document-owned columns for a
	// CIMD client.
	RefreshClientMetadata(ctx context.Context, id OIDCClientID, params ClientUpdateParams) error
}

Store owns every OIDC/OAuth 2.0 read and write: client CRUD, the one-time codes, the Fosite-style session bookkeeping (kinds: access, refresh, authorize_code), the replay registry (oauth2_jtis), and the interaction bridge. Token hashes are stored, never raw.

type UserClaims

type UserClaims struct {
	Subject           string
	SessionID         string
	Email             string
	EmailVerified     bool
	Name              string
	PreferredUsername string
	Groups            []string
	Custom            map[string]any
}

UserClaims is the assembled identity payload for tokens.

type UserProfile

type UserProfile struct {
	ID              string
	Username        string
	Email           string
	DisplayName     string
	EmailVerifiedAt *time.Time
	Disabled        bool
}

UserProfile is the claim source for tokens.

Jump to

Keyboard shortcuts

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