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
- Variables
- type APIAccessProvider
- type AuditLogger
- type AuthorizationCode
- type AuthorizedClient
- type Client
- type ClientCreateParams
- type ClientImageStore
- type ClientSecret
- type ClientUpdateParams
- type DeviceCode
- type DocumentFetcher
- type Feature
- type InteractionSession
- type InteractionSessionID
- type Lifetimes
- type MetadataDocument
- type OAuth2Session
- type OIDCClientID
- type Option
- func WithAPIAccess(p APIAccessProvider) Option
- func WithAccessAuthenticator(auth kernel.AccessAuthenticator) Option
- func WithAudit(l AuditLogger) Option
- func WithCIMDAllowlist(get func() []string) Option
- func WithImages(images ClientImageStore) Option
- func WithLifetimes(l Lifetimes) Option
- func WithMetadataFetcher(f DocumentFetcher) Option
- func WithScimBinding(fn func(ctx context.Context, clientID string) (*ScimBinding, error)) Option
- type PostgresStore
- func (s *PostgresStore) AccessibleClients(ctx context.Context, userID string) ([]Client, error)
- func (s *PostgresStore) AddClientSecret(ctx context.Context, clientID OIDCClientID, entry ClientSecret, rawHash string) error
- func (s *PostgresStore) ApproveDeviceCode(ctx context.Context, userCodeHash, userID string) error
- func (s *PostgresStore) AuthorizedClients(ctx context.Context, userID *string) ([]AuthorizedClient, error)
- func (s *PostgresStore) ConsumeCode(ctx context.Context, codeHash string) (AuthorizationCode, error)
- func (s *PostgresStore) ConsumeDeviceCode(ctx context.Context, deviceCodeHash string) (DeviceCode, error)
- func (s *PostgresStore) CreateClient(ctx context.Context, params ClientCreateParams) (Client, error)
- func (s *PostgresStore) CreateInteraction(ctx context.Context, session InteractionSession) error
- func (s *PostgresStore) CustomClaims(ctx context.Context, userID string) (map[string]string, error)
- func (s *PostgresStore) DeactivateFamily(ctx context.Context, requestID string) error
- func (s *PostgresStore) DeactivateSession(ctx context.Context, kind, key string) error
- func (s *PostgresStore) DeleteAuthorization(ctx context.Context, userID, clientID string) error
- func (s *PostgresStore) DeleteClient(ctx context.Context, id OIDCClientID) error
- func (s *PostgresStore) DeleteClientSecret(ctx context.Context, clientID OIDCClientID, secretID string) error
- func (s *PostgresStore) DeleteInteraction(ctx context.Context, id InteractionSessionID) error
- func (s *PostgresStore) DenyDeviceCode(ctx context.Context, userCodeHash string) error
- func (s *PostgresStore) GetClient(ctx context.Context, id OIDCClientID) (Client, error)
- func (s *PostgresStore) GetDeviceCode(ctx context.Context, deviceCodeHash string) (DeviceCode, error)
- func (s *PostgresStore) GetDeviceCodeByUserCode(ctx context.Context, userCodeHash string) (DeviceCode, error)
- func (s *PostgresStore) GetInteraction(ctx context.Context, id InteractionSessionID) (InteractionSession, error)
- func (s *PostgresStore) GetSession(ctx context.Context, kind, key string) (OAuth2Session, error)
- func (s *PostgresStore) HasAuthorizedClient(ctx context.Context, userID, clientID string) (bool, error)
- func (s *PostgresStore) InsertCode(ctx context.Context, code AuthorizationCode) error
- func (s *PostgresStore) InsertDeviceCode(ctx context.Context, code DeviceCode) error
- func (s *PostgresStore) JTIExists(ctx context.Context, jti string) (bool, error)
- func (s *PostgresStore) ListClients(ctx context.Context) ([]Client, error)
- func (s *PostgresStore) PruneDeviceCodes(ctx context.Context, before time.Time) (int64, error)
- func (s *PostgresStore) PutSession(ctx context.Context, session OAuth2Session) error
- func (s *PostgresStore) RecordJTI(ctx context.Context, jti string, expiresAt time.Time) error
- func (s *PostgresStore) RefreshClientMetadata(ctx context.Context, id OIDCClientID, params ClientUpdateParams) error
- func (s *PostgresStore) RevokeClientTokens(ctx context.Context, clientID, userID string) error
- func (s *PostgresStore) SetClientGroups(ctx context.Context, id OIDCClientID, groupIDs []string) error
- func (s *PostgresStore) SetClientLogoPath(ctx context.Context, id OIDCClientID, logoPath *string) error
- func (s *PostgresStore) TouchDevicePoll(ctx context.Context, deviceCodeHash string, at time.Time) error
- func (s *PostgresStore) UpdateClient(ctx context.Context, id OIDCClientID, params ClientUpdateParams) (Client, error)
- func (s *PostgresStore) UpdateDeviceCodeGrant(ctx context.Context, userCodeHash, audience, scope string) error
- func (s *PostgresStore) UpdateInteraction(ctx context.Context, id InteractionSessionID, userID *string, ...) error
- func (s *PostgresStore) UpsertAuthorizedClient(ctx context.Context, userID, clientID string, scopes []string) error
- func (s *PostgresStore) UserByID(ctx context.Context, userID string) (UserProfile, error)
- func (s *PostgresStore) UserGroups(ctx context.Context, userID string) ([]string, error)
- func (s *PostgresStore) UserInGroup(ctx context.Context, userID, groupID string) bool
- type ScimBinding
- type SecretEntryView
- type Service
- func (s *Service) AccessTokenTTL() time.Duration
- func (s *Service) AuthorizationCodeTTL() time.Duration
- func (s *Service) DeviceCodeTTL() time.Duration
- func (s *Service) EndSession(ctx context.Context, hint, clientID, postLogoutRedirectURI string) (string, error)
- func (s *Service) HandleDeviceAuthorize(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleDeviceInfo(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleDeviceVerify(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandlePAR(w http.ResponseWriter, r *http.Request)
- func (s *Service) InteractionTTL() time.Duration
- func (s *Service) PARTTL() time.Duration
- func (s *Service) RefreshTokenTTL() time.Duration
- type Store
- type UserClaims
- type UserProfile
Constants ¶
const ( SubjectUser = "user" SubjectClient = "client" )
Subject types mirror the apiaccess grant sides; plain strings keep this package free of the identity tree.
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.
const ( ScopeOpenID = "openid" ScopeEmail = "email" ScopeProfile = "profile" ScopeGroups = "groups" )
Scopes understood by this provider.
const ( DeviceStatusPending = "pending" DeviceStatusApproved = "approved" DeviceStatusDenied = "denied" DeviceStatusConsumed = "consumed" )
Device authorization statuses; status drives the token poll.
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.
const MaxMetadataDocument = 1 << 20 // 1 MiB, matches fosite's default
MaxMetadataDocument bounds one metadata document fetch.
const MetadataDocumentTTL = 24 * time.Hour
MetadataDocumentTTL controls the metadata refresh window.
Variables ¶
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).
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.
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") // 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 ¶
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 ¶
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 (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
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) RPCService ¶ added in v0.0.12
RPCService returns the client administration registration; the composition root wraps it with the admin guard.
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 ¶
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 ¶
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 WithCIMDAllowlist ¶
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
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
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 ¶
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 ¶
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 ¶
func (s *PostgresStore) GetInteraction(ctx context.Context, id InteractionSessionID) (InteractionSession, error)
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) ListClients ¶
func (s *PostgresStore) ListClients(ctx context.Context) ([]Client, error)
ListClients returns every client with group grants attached.
func (*PostgresStore) PruneDeviceCodes ¶
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) 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 ¶
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 ¶
NewService builds the provider service.
func (*Service) AccessTokenTTL ¶ added in v0.0.12
AccessTokenTTL is the provider default access-token lifetime.
func (*Service) AuthorizationCodeTTL ¶ added in v0.0.12
AuthorizationCodeTTL bounds the one-time code lifetime.
func (*Service) DeviceCodeTTL ¶ added in v0.0.12
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
InteractionTTL bounds the sign-in/consent bridge.
func (*Service) PARTTL ¶ added in v0.0.12
PARTTL bounds a pushed authorization request_uri lifetime.
func (*Service) RefreshTokenTTL ¶ added in v0.0.12
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.
Source Files
¶
- authorize.go
- cimd.go
- client.go
- device.go
- end_session.go
- handler.go
- handler_authorize.go
- handler_device.go
- handler_logo.go
- handler_meta.go
- handler_par.go
- handler_rpc.go
- handler_surfaces.go
- handler_token.go
- par.go
- resource.go
- schema.go
- service.go
- store.go
- store_authz.go
- store_client.go
- store_device.go
- store_identity.go
- store_token.go
- token.go
- userinfo.go