Documentation
¶
Index ¶
- Variables
- func MintResourceToken(userID, appClientID string, signingKey any, customClaims map[string]any, ...) (string, error)
- type APIKeyAuth
- type AdminAuth
- type AppRegistrar
- func (h *AppRegistrar) DeleteClient(ctx context.Context, req *DeleteClientRequest) (*DeleteClientResponse, error)
- func (h *AppRegistrar) DeleteRegistration(ctx context.Context, req *DeleteRegistrationRequest) (*DeleteRegistrationResponse, error)
- func (h *AppRegistrar) GetAppRegistration(ctx context.Context, clientID string) (*core.AppRegistration, bool)
- func (h *AppRegistrar) GetClient(ctx context.Context, req *GetClientRequest) (*GetClientResponse, error)
- func (h *AppRegistrar) GetRegistration(ctx context.Context, req *GetRegistrationRequest) (*GetRegistrationResponse, error)
- func (h *AppRegistrar) Handler() http.Handler
- func (h *AppRegistrar) ListClients(ctx context.Context, req *ListClientsRequest) (*ListClientsResponse, error)
- func (h *AppRegistrar) RLockApps(fn func(map[string]*core.AppRegistration))
- func (h *AppRegistrar) Register(ctx context.Context, req *RegisterRequest) (*RegisterResponse, error)
- func (h *AppRegistrar) RotateSecret(ctx context.Context, req *RotateSecretRequest) (*RotateSecretResponse, error)
- func (h *AppRegistrar) SaveRegistration(ctx context.Context, reg *core.AppRegistration) error
- func (h *AppRegistrar) UpdateRegistration(ctx context.Context, req *UpdateRegistrationRequest) (*UpdateRegistrationResponse, error)
- type ClientRegistrar
- type ClientRegistrationManager
- type DCRHandler
- type DCRManagementHandler
- type DCRRequest
- type DCRResponse
- type DeleteClientRequest
- type DeleteClientResponse
- type DeleteRegistrationRequest
- type DeleteRegistrationResponse
- type GetClientRequest
- type GetClientResponse
- type GetRegistrationRequest
- type GetRegistrationResponse
- type ListClientsRequest
- type ListClientsResponse
- type NoAuth
- type RegisterRequest
- type RegisterResponse
- type RotateSecretRequest
- type RotateSecretResponse
- type UpdateRegistrationRequest
- type UpdateRegistrationResponse
Constants ¶
This section is empty.
Variables ¶
var ( ErrAdminForbidden = fmt.Errorf("admin access denied") )
Common errors for admin auth
var ErrInvalidClientMetadata = errors.New("invalid client metadata")
ErrInvalidClientMetadata signals that a management request was authenticated successfully but the request body fails RFC 7591 / 7592 client-metadata validation. HTTP wrappers map this to 400 Bad Request. Distinct from ErrUnauthorized because, by the time we get here, the caller has already proven possession of the registration access token — refusing to distinguish would be needlessly cryptic.
var ErrInvalidPublicKey = errors.New("invalid public key")
ErrInvalidPublicKey indicates that a provided PEM public key failed parsing or did not match the registered signing algorithm. Wrapper layers map this to HTTP 400 invalid_request.
var ErrPublicKeyRequired = errors.New("public key required for asymmetric algorithm")
ErrPublicKeyRequired indicates that an asymmetric registration or rotation was attempted without supplying the public_key field. Wrapper layers map this to HTTP 400 invalid_request.
ErrUnauthorized is the single failure mode returned by ClientRegistrationManager for any authentication problem on the management protocol. The uniform error is intentional: distinguishing "unknown client_id" from "wrong token" would turn /apps/dcr/{client_id} into a probe for valid identifiers.
Functions ¶
func MintResourceToken ¶
func MintResourceToken(userID, appClientID string, signingKey any, customClaims map[string]any, scopes []string, authzDetails []core.AuthorizationDetail) (string, error)
MintResourceToken creates a resource-scoped JWT for a user on behalf of a registered App, signed with the app's own signing key (federated token pattern — the resource server verifies with the same key, no AS callback needed). The signing algorithm is auto-detected from the key's Go type:
- []byte → HS256
- *rsa.PrivateKey → RS256
- *ecdsa.PrivateKey → ES256
customClaims is merged into the JWT alongside the standard claims (sub, client_id, type, scopes, iat, exp). Keys colliding with standard claims are logged and dropped — standard claims are owned by the minter.
Types ¶
type APIKeyAuth ¶
type APIKeyAuth struct {
// contains filtered or unexported fields
}
APIKeyAuth authenticates requests using a shared API key passed in the X-Admin-Key header.
func NewAPIKeyAuth ¶
func NewAPIKeyAuth(key string) *APIKeyAuth
NewAPIKeyAuth creates an AdminAuth that validates the X-Admin-Key header.
func (*APIKeyAuth) Authenticate ¶
func (a *APIKeyAuth) Authenticate(r *http.Request) error
type AdminAuth ¶
type AdminAuth interface {
// Authenticate checks whether the request is authorized.
// Returns nil if authorized, or an error describing why not.
Authenticate(r *http.Request) error
}
AdminAuth authenticates admin requests to protected endpoints (e.g., Host registration, key rotation).
type AppRegistrar ¶
type AppRegistrar struct {
KeyStore keys.KeyStorage
Auth AdminAuth
// KidStore retains old keys during rotation grace periods so that
// in-flight tokens signed with the previous key remain verifiable.
// If nil, rotation replaces the key immediately with no grace period.
// Typed as the KidStorage interface so deployments can plug in a
// persistent backend (FS/GORM/GAE) without losing in-memory KidStore
// compatibility (*KidStore satisfies KidStorage).
KidStore keys.KidStorage
// DefaultGracePeriod is the default grace period for key rotation
// when not specified in the request. Defaults to 24h.
DefaultGracePeriod time.Duration
// Store persists app registration metadata. It is the source of truth;
// the apps map below is a hot-path cache hydrated on construction and
// updated synchronously on every write.
Store core.AppRegistrationStore
// AllowPrivateBCLHosts opts in to registering a backchannel_logout_uri
// whose host resolves (or is literally) loopback / RFC1918 / link-local
// / unspecified / multicast. Off by default so a client cannot register
// a URI that, on session revoke, makes the AS POST to internal services
// on its behalf (classic SSRF). Closed-network deployments where the AS
// and the RS receivers share a private network can flip this on. When
// flipped, the dispatcher's HTTP client must also opt in via
// BCLDispatcher.AllowPrivateHosts or the dial-time guard still blocks.
AllowPrivateBCLHosts bool
// contains filtered or unexported fields
}
AppRegistrar is an embeddable HTTP handler for App registration CRUD. Mount it on any admin service's mux to let apps register and obtain signing credentials. Create with NewAppRegistrar() (in-memory store) or NewAppRegistrarWithStore.
func NewAppRegistrar ¶ added in v0.0.53
func NewAppRegistrar(keyStore keys.KeyStorage, auth AdminAuth) *AppRegistrar
NewAppRegistrar creates an AppRegistrar backed by an in-memory store. Equivalent to NewAppRegistrarWithStore(keyStore, auth, core.NewInMemoryAppStore()).
For deployments that need registrations to survive restart, use NewAppRegistrarWithStore with a persistent backend (FSAppStore, GORMAppStore).
func NewAppRegistrarWithStore ¶ added in v0.0.81
func NewAppRegistrarWithStore(keyStore keys.KeyStorage, auth AdminAuth, store core.AppRegistrationStore) *AppRegistrar
NewAppRegistrarWithStore creates an AppRegistrar backed by the given store. Existing registrations in the store are loaded into the in-memory cache so that subsequent reads (RLockApps, GET /apps) reflect the persisted state immediately after construction.
If store.ListApps returns an error during hydration, the AppRegistrar is returned with an empty cache; subsequent writes proceed normally. (We do not panic — a transient store error at startup should not crash the host process. The error is intentionally swallowed here since there is no caller-visible context to report it through; callers wanting strict startup semantics should call store.ListApps themselves first.)
func (*AppRegistrar) DeleteClient ¶ added in v0.0.82
func (h *AppRegistrar) DeleteClient(ctx context.Context, req *DeleteClientRequest) (*DeleteClientResponse, error)
DeleteClient implements ClientRegistrar — admin delete. Removes the registration from Store + in-memory cache and invalidates the KeyStore entry. Returns core.ErrAppNotFound if the client does not exist.
Distinct from ClientRegistrationManager.DeleteRegistration which authenticates via the client's own registration_access_token. This admin path is reachable only by AdminAuth-passing callers.
func (*AppRegistrar) DeleteRegistration ¶ added in v0.0.81
func (h *AppRegistrar) DeleteRegistration(ctx context.Context, req *DeleteRegistrationRequest) (*DeleteRegistrationResponse, error)
DeleteRegistration implements ClientRegistrationManager (RFC 7592 §2.3). On success it removes the registration from the core.AppRegistrationStore (and the in-memory cache), and deletes the client's signing key from KeyStore so any tokens already issued under this client_id fail subsequent signature-validation — satisfying the spec requirement that "the authorization server MUST invalidate" all tokens for a deleted client.
Failure ordering mirrors handleDeleteApp: we persist the deletion in the store first; only on success do we drop the cache entry and the KeyStore key. If the store write fails we return early without touching the in-memory cache or the credentials, so the registration remains authoritatively present (deletion can be retried).
ctx is currently unused but threaded through for cancellation / deadline propagation once stores grow async ops.
func (*AppRegistrar) GetAppRegistration ¶ added in v0.1.20
func (h *AppRegistrar) GetAppRegistration(ctx context.Context, clientID string) (*core.AppRegistration, bool)
GetAppRegistration returns a clone of the cached registration for clientID, or (nil, false) if no such client is registered. It satisfies the narrow AppRegistrationLookup interface defined in apiauth/ so the BCL dispatcher can resolve registered receiver URIs without admin/ → apiauth/ → admin/ import cycles. Returns a clone so callers cannot mutate the in-memory cache.
func (*AppRegistrar) GetClient ¶ added in v0.0.82
func (h *AppRegistrar) GetClient(ctx context.Context, req *GetClientRequest) (*GetClientResponse, error)
GetClient implements ClientRegistrar — admin read of a single registration. Returns core.ErrAppNotFound if the client does not exist.
func (*AppRegistrar) GetRegistration ¶ added in v0.0.81
func (h *AppRegistrar) GetRegistration(ctx context.Context, req *GetRegistrationRequest) (*GetRegistrationResponse, error)
GetRegistration implements ClientRegistrationManager. It returns the RFC 7591 registration for req.ClientID iff req.AccessToken matches the stored registration_access_token. Returns ErrUnauthorized for every failure mode — wrong/missing token, unknown client_id, or a registration that lacks a management token — so the management endpoint cannot be used to probe for valid client_ids.
The returned DCRResponse intentionally omits client_secret. RFC 7592 §3 permits but does not require echoing the secret on read; re-emitting symmetric credentials on every read enlarges the disclosure window if the registration access token is ever logged or proxied. Clients that lose the secret can rotate via PUT (#169).
ctx is currently unused but threaded through for cancellation / deadline propagation once stores grow async ops, and for parity with the rest of the manager interface.
func (*AppRegistrar) Handler ¶
func (h *AppRegistrar) Handler() http.Handler
Handler returns an http.Handler for app registration + management endpoints. Registration is RFC 7591 DCR only — the legacy proprietary `POST /apps/register` was retired under issue 189; all callers go through `/apps/dcr` now.
POST /apps/dcr — RFC 7591 Dynamic Client Registration
GET /apps/dcr/{client_id} — RFC 7592 DCR Management read (issue #168)
GET /apps — List all apps
GET /apps/{id} — Get app metadata
DELETE /apps/{id} — Delete app
POST /apps/{id}/rotate — Rotate secret/key
Routing precedence note: Go's ServeMux uses longest-prefix matching, so the "/apps/dcr/" prefix below wins over the "/apps/" catch-all without further fiddling. The exact-match "/apps/dcr" route handles RFC 7591 registration.
func (*AppRegistrar) ListClients ¶ added in v0.0.82
func (h *AppRegistrar) ListClients(ctx context.Context, req *ListClientsRequest) (*ListClientsResponse, error)
ListClients implements ClientRegistrar — admin read of every registered app. Reads from the in-memory cache hydrated from core.AppRegistrationStore on construction. Returned entries are clones; callers cannot mutate the cache via this value.
func (*AppRegistrar) RLockApps ¶
func (h *AppRegistrar) RLockApps(fn func(map[string]*core.AppRegistration))
RLockApps calls fn with a read-locked view of all registered apps.
func (*AppRegistrar) Register ¶ added in v0.0.82
func (h *AppRegistrar) Register(ctx context.Context, req *RegisterRequest) (*RegisterResponse, error)
Register implements ClientRegistrar — RFC 7591 Dynamic Client Registration. Generates a client_id, allocates either a symmetric secret or stores the caller-supplied JWK public key, issues RFC 7592 §3 management credentials, and persists the resulting core.AppRegistration. Returns an error mapped by the wrapper:
- ErrInvalidClientMetadata: missing JWKS for private_key_jwt, or invalid JWK → HTTP 400
- other errors (KeyStore failures, RNG failures): bubble up → HTTP 500
ctx is currently unused but threaded through for cancellation / deadline propagation once stores grow async ops.
func (*AppRegistrar) RotateSecret ¶ added in v0.0.82
func (h *AppRegistrar) RotateSecret(ctx context.Context, req *RotateSecretRequest) (*RotateSecretResponse, error)
RotateSecret implements ClientRegistrar — rotates the signing key for req.ClientID. For symmetric algs a fresh secret is generated and returned. For asymmetric algs the caller MUST supply req.PublicKey (PEM); there is no server-side keypair generation today.
When KidStore is configured on AppRegistrar, the previous key material is retained for req.GracePeriod (defaulting to AppRegistrar.DefaultGracePeriod or 24h) so in-flight tokens stay verifiable. The returned PreviousKid / GracePeriod fields are populated only when retention actually happened.
Errors:
- core.ErrAppNotFound: req.ClientID not registered
- ErrPublicKeyRequired: asymmetric alg without PublicKey
- ErrInvalidPublicKey: PublicKey fails PEM parse for the registered alg
func (*AppRegistrar) SaveRegistration ¶ added in v0.0.81
func (h *AppRegistrar) SaveRegistration(ctx context.Context, reg *core.AppRegistration) error
SaveRegistration persists the registration to the store and updates the in-memory cache. Used by Register, DCRHandler, and (in #157) the RFC 7592 management endpoints. If the store write fails, the cache is not updated and the error is returned.
func (*AppRegistrar) UpdateRegistration ¶ added in v0.0.81
func (h *AppRegistrar) UpdateRegistration(ctx context.Context, req *UpdateRegistrationRequest) (*UpdateRegistrationResponse, error)
UpdateRegistration implements ClientRegistrationManager (RFC 7592 §2.2). It performs a full replacement of the editable metadata fields and rotates the registration_access_token; the new token is returned in the response. The old token becomes invalid as soon as SaveRegistration succeeds.
Editable fields (overwritten from req.Metadata on success): ClientName, ClientURI, RedirectURIs, GrantTypes, Scope, AuthorizationDetailsTypes, ClientDomain (derived from ClientURI / ClientName, mirroring DCRHandler's logic).
Locked fields (return ErrInvalidClientMetadata on attempted change):
- TokenEndpointAuthMethod — would require re-keying; out of scope for #169.
Locked fields (silently retained):
- SigningAlg, ClientID, CreatedAt, RegistrationClientURI, the key material in KeyStore.
req.Metadata is treated as RFC 7591 client metadata; req.Metadata.JWKS is currently ignored (auth-method changes are rejected, so the JWKS cannot usefully change either).
ctx is currently unused but threaded through for cancellation / deadline propagation once stores grow async ops, and for parity with the rest of the manager interface.
type ClientRegistrar ¶ added in v0.0.82
type ClientRegistrar interface {
// Register handles RFC 7591 Dynamic Client Registration. The wire format
// of the request and response is the RFC 7591 / 7592 client metadata
// shape — RegisterRequest.Metadata is the standard DCR request body,
// RegisterResponse.Registration is the standard DCR response body
// (extended with the RFC 7592 §3 management credentials).
Register(ctx context.Context, req *RegisterRequest) (*RegisterResponse, error)
// ListClients returns every registration in the registry. Reads from
// the in-memory cache hydrated from the core.AppRegistrationStore on
// AppRegistrar construction.
ListClients(ctx context.Context, req *ListClientsRequest) (*ListClientsResponse, error)
// GetClient returns the registration for req.ClientID, or
// core.ErrAppNotFound. This is the ADMIN read — distinct from
// ClientRegistrationManager.GetRegistration which authenticates with
// the client's own registration_access_token.
GetClient(ctx context.Context, req *GetClientRequest) (*GetClientResponse, error)
// DeleteClient removes the registration for req.ClientID and
// invalidates its KeyStore entry. Returns core.ErrAppNotFound if the
// client does not exist. This is the ADMIN delete — distinct from
// ClientRegistrationManager.DeleteRegistration which authenticates
// with the client's own token.
DeleteClient(ctx context.Context, req *DeleteClientRequest) (*DeleteClientResponse, error)
// RotateSecret rotates the signing key for req.ClientID. For symmetric
// algorithms a fresh secret is generated and returned. For asymmetric
// algorithms the caller MUST supply a new PublicKey (PEM) — there is
// no server-side keypair generation today. When KidStore is configured
// on AppRegistrar, the previous key is retained for the grace period
// so in-flight tokens stay verifiable.
RotateSecret(ctx context.Context, req *RotateSecretRequest) (*RotateSecretResponse, error)
}
ClientRegistrar is the transport-agnostic core of OneAuth's client administration surface — RFC 7591 registration, listing, admin reads / deletes, and secret/key rotation. HTTP handlers in admin/ are thin wrappers around this interface (see DCRHandler.ServeHTTP and AppRegistrar.handleX), the same shape as ClientRegistrationManager (#168/#169/#170) for self-service management and the same convention apiauth/ adopts under #175.
Auth boundary ¶
Methods on this interface are post-auth: the wrapper enforces AdminAuth (X-Admin-Key, etc.) before invoking the manager. ClientRegistrar itself is unauthenticated by design — it expresses *what* admin operations exist, not *who* may invoke them.
Distinction from ClientRegistrationManager ¶
ClientRegistrationManager is the SELF-SERVICE management surface (RFC 7592, authed by registration_access_token) — only the registered client can act on its own registration. ClientRegistrar is the ADMIN surface — operators acting across all registrations. Same domain (registrations) but different security models, hence two interfaces.
type ClientRegistrationManager ¶ added in v0.0.81
type ClientRegistrationManager interface {
// GetRegistration returns the registration for req.ClientID iff
// req.AccessToken matches its stored registration_access_token. Any
// auth failure — wrong token, missing token, unknown client_id — yields
// ErrUnauthorized so callers (and attackers) cannot distinguish them.
GetRegistration(ctx context.Context, req *GetRegistrationRequest) (*GetRegistrationResponse, error)
// UpdateRegistration replaces the metadata for req.ClientID per RFC 7592
// §2.2 (full replacement, not PATCH-style merge). The request's body-level
// client_id (req.Metadata.ClientID) MUST equal req.ClientID — mismatch
// returns ErrInvalidClientMetadata, mapped to HTTP 400 by the wrapper.
// Auth failures return ErrUnauthorized as with GetRegistration.
//
// On success the registration_access_token is rotated (RFC 7592 §2.2
// recommended) and the new token is returned in the response; the old
// token is invalid for any subsequent management request.
//
// Out of scope for #169: changing token_endpoint_auth_method (which would
// require re-keying / new secret). Such requests return
// ErrInvalidClientMetadata. Clients that need to change auth method
// DELETE and re-register.
UpdateRegistration(ctx context.Context, req *UpdateRegistrationRequest) (*UpdateRegistrationResponse, error)
// DeleteRegistration removes the registration for req.ClientID iff
// req.AccessToken matches the stored registration_access_token, and
// invalidates the client's signing credentials so already-issued tokens
// fail subsequent validation (RFC 7592 §2.3 — "the authorization server
// MUST invalidate" all tokens for the deleted client). The same uniform
// ErrUnauthorized envelope as the other methods covers every auth failure.
//
// Idempotency is intentional but limited: a second DELETE on the same
// client_id returns ErrUnauthorized (the registration is gone, so the
// token check fails at lookup) rather than a special "already deleted"
// error — preserves the no-enumeration guard.
DeleteRegistration(ctx context.Context, req *DeleteRegistrationRequest) (*DeleteRegistrationResponse, error)
}
ClientRegistrationManager is the transport-agnostic core of OAuth 2.0 Dynamic Client Registration Management (RFC 7592). HTTP handlers in admin/ (and any future gRPC / in-process callers) are thin wrappers around this interface — they parse the request, call into the manager, then format the response. Mirrors the apiauth/ pattern (TokenIssuer / TokenValidator / TokenIntrospector / TokenRevoker — see issue #110).
Method shape — every method follows the convention:
MethodName(ctx context.Context, req *XRequest) (*XResponse, error)
Two-arg / two-return signatures map cleanly to gRPC codegen if we ever generate stubs from these interfaces. The first parameter is plain context.Context for now; a typed library context (carrying stores, loggers, request-scoped deps) can replace it later without changing the shape — that's the point of standardizing on a single ctx parameter rather than expanding the parameter list ad hoc.
#168 ships GetRegistration. #169 ships UpdateRegistration. #170 ships DeleteRegistration — completing the verb trio.
type DCRHandler ¶ added in v0.0.62
type DCRHandler struct {
// KeyStore stores client credentials (same KeyStore as AppRegistrar).
KeyStore keys.KeyStorage
// Auth authenticates the caller. Supports both:
// - Initial access token via Authorization: Bearer header (RFC 7591 §3)
// - X-Admin-Key header (OneAuth custom, backward-compatible)
// If nil, registration is open (not recommended for production).
Auth AdminAuth
// Registrar is the underlying AppRegistrar for metadata storage.
// If nil, client metadata is not persisted (only KeyStore is used).
Registrar *AppRegistrar
// IssuerBaseURL is the public base URL used to construct
// registration_client_uri values returned in DCR responses
// (e.g. "https://auth.example.com"). When empty, the URI is
// built from the incoming request's scheme + Host header — fine
// for tests but unreliable behind proxies, so production
// deployments should set this explicitly.
IssuerBaseURL string
}
DCRHandler implements OAuth 2.0 Dynamic Client Registration (RFC 7591) as a conformance wrapper around AppRegistrar. It accepts standard DCR request format and maps to/from the internal AppRegistrar model.
Automatically mounted by AppRegistrar.Handler() at POST /apps/dcr. Existing /apps/* endpoints continue working unchanged.
On successful registration the response includes a registration access token + management URI (RFC 7592 §3) which the client uses to read, update, or delete its own registration via /apps/dcr/{client_id}.
See: https://www.rfc-editor.org/rfc/rfc7591 See: https://www.rfc-editor.org/rfc/rfc7592
func (*DCRHandler) ServeHTTP ¶ added in v0.0.62
func (h *DCRHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP is the HTTP wrapper for ClientRegistrar.Register (RFC 7591 DCR). All protocol logic lives behind the interface; this method just parses, authenticates, calls the manager, and formats the response. See #172 for the convention this follows (the same shape used by ClientRegistrationManager in #168 / #169 / #170).
type DCRManagementHandler ¶ added in v0.0.81
type DCRManagementHandler struct {
// Manager is the transport-agnostic core. Required.
Manager ClientRegistrationManager
}
DCRManagementHandler is the HTTP transport adapter for the RFC 7592 management protocol — it parses the request, hands off to a ClientRegistrationManager, then formats the response. All protocol / authorization decisions live behind the interface so the same logic is usable from gRPC, in-process callers, and tests without HTTP machinery.
#168 ships GET; #169 ships PUT; #170 ships DELETE — completing the verb trio. Other HTTP methods return 405 with an Allow header advertising the supported set.
See: https://www.rfc-editor.org/rfc/rfc7592
func (*DCRManagementHandler) ServeHTTP ¶ added in v0.0.81
func (h *DCRManagementHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP routes /apps/dcr/{client_id} requests by HTTP method.
type DCRRequest ¶ added in v0.0.62
type DCRRequest struct {
// ClientID is required for PUT (RFC 7592 §2.2), ignored for register.
ClientID string `json:"client_id,omitempty"`
// Client metadata
ClientName string `json:"client_name,omitempty"`
ClientURI string `json:"client_uri,omitempty"`
RedirectURIs []string `json:"redirect_uris,omitempty"`
GrantTypes []string `json:"grant_types,omitempty"`
Scope string `json:"scope,omitempty"`
// Authentication method
TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty"`
// RFC 9396 — authorization details types this client intends to use
AuthorizationDetailsTypes []string `json:"authorization_details_types,omitempty"`
// Keys — for asymmetric auth methods (private_key_jwt)
JWKS *utils.JWKSet `json:"jwks,omitempty"`
// OIDC Back-Channel Logout 1.0 §3.1 — clients advertise a logout receiver.
// Empty disables BCL dispatch for this client.
BackchannelLogoutURI string `json:"backchannel_logout_uri,omitempty"`
BackchannelLogoutSessionRequired bool `json:"backchannel_logout_session_required,omitempty"`
}
DCRRequest is the RFC 7591 client registration request, also reused as the RFC 7592 §2.2 update request body.
On RFC 7591 registration the ClientID field is unused — the server assigns the value. On RFC 7592 PUT the client MUST include its existing client_id; the HTTP wrapper validates that it matches the URL path before invoking ClientRegistrationManager.UpdateRegistration.
See: https://www.rfc-editor.org/rfc/rfc7591#section-2 See: https://www.rfc-editor.org/rfc/rfc7592#section-2.2
type DCRResponse ¶ added in v0.0.62
type DCRResponse struct {
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret,omitempty"`
ClientIDIssuedAt int64 `json:"client_id_issued_at"`
ClientSecretExpiresAt int64 `json:"client_secret_expires_at"`
ClientName string `json:"client_name,omitempty"`
ClientURI string `json:"client_uri,omitempty"`
RedirectURIs []string `json:"redirect_uris,omitempty"`
GrantTypes []string `json:"grant_types,omitempty"`
TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty"`
Scope string `json:"scope,omitempty"`
AuthorizationDetailsTypes []string `json:"authorization_details_types,omitempty"` // RFC 9396
// RFC 7592 §3 — management credentials.
RegistrationAccessToken string `json:"registration_access_token,omitempty"`
RegistrationClientURI string `json:"registration_client_uri,omitempty"`
// OIDC Back-Channel Logout 1.0 §3.1 — echo of the registered receiver
// metadata so clients can confirm what the AS persisted.
BackchannelLogoutURI string `json:"backchannel_logout_uri,omitempty"`
BackchannelLogoutSessionRequired bool `json:"backchannel_logout_session_required,omitempty"`
}
DCRResponse is the RFC 7591 client registration response, extended with the RFC 7592 §3 management credentials (registration_access_token + registration_client_uri) so that registered clients can subsequently call /apps/dcr/{client_id} to read, update, or delete their own registration. See: https://www.rfc-editor.org/rfc/rfc7591#section-3.2.1 See: https://www.rfc-editor.org/rfc/rfc7592#section-3
type DeleteClientRequest ¶ added in v0.0.82
type DeleteClientRequest struct {
ClientID string
}
DeleteClientRequest is the input to ClientRegistrar.DeleteClient.
type DeleteClientResponse ¶ added in v0.0.82
type DeleteClientResponse struct{}
DeleteClientResponse is intentionally empty — the proprietary endpoint returns {"deleted": true, "client_id": ...}, but that's a wire-format concern handled by the wrapper. The manager-level signal is "no error = success".
type DeleteRegistrationRequest ¶ added in v0.0.81
type DeleteRegistrationRequest struct {
// ClientID identifies the registration to remove.
ClientID string
// AccessToken is the registration_access_token currently on the
// registration — must match for the deletion to be authorized.
AccessToken string
}
DeleteRegistrationRequest is the input to ClientRegistrationManager.DeleteRegistration.
type DeleteRegistrationResponse ¶ added in v0.0.81
type DeleteRegistrationResponse struct{}
DeleteRegistrationResponse is intentionally empty today: RFC 7592 §2.3 returns 204 No Content with no body. The struct exists so future forward-compat fields (e.g., a deletion confirmation token) can be added without changing the method signature — same rationale as the other response types.
type GetClientRequest ¶ added in v0.0.82
type GetClientRequest struct {
ClientID string
}
GetClientRequest is the input to ClientRegistrar.GetClient.
type GetClientResponse ¶ added in v0.0.82
type GetClientResponse struct {
Registration *core.AppRegistration
}
GetClientResponse is the registration metadata for the requested client.
type GetRegistrationRequest ¶ added in v0.0.81
type GetRegistrationRequest struct {
// ClientID identifies the registration to read.
ClientID string
// AccessToken is the registration_access_token issued at registration time
// (RFC 7592 §3). Must match the value stored on the registration.
AccessToken string
}
GetRegistrationRequest is the input to ClientRegistrationManager.GetRegistration.
type GetRegistrationResponse ¶ added in v0.0.81
type GetRegistrationResponse struct {
Registration *DCRResponse
}
GetRegistrationResponse wraps the registration metadata. Wrapped (rather than returning *DCRResponse directly) so that future forward-compat fields can be added without changing the method signature — same reason gRPC requires dedicated response messages per method.
type ListClientsRequest ¶ added in v0.0.82
type ListClientsRequest struct{}
ListClientsRequest is intentionally empty today — the proprietary endpoint has no filtering or pagination. Wrapper struct exists for the convention (ctx, *Req → *Resp, error) and so future fields (paging, filters) can be added without changing the method signature.
type ListClientsResponse ¶ added in v0.0.82
type ListClientsResponse struct {
Apps []*core.AppRegistration
}
ListClientsResponse returns every registration. Each entry is a clone — callers cannot mutate the in-memory cache via this value.
type RegisterRequest ¶ added in v0.0.82
type RegisterRequest struct {
// Metadata is the RFC 7591 §2 client metadata payload. Required.
Metadata *DCRRequest
// IssuerBaseURL is the public-facing AS base URL used to construct the
// registration_client_uri returned in the response (RFC 7592 §3). When
// empty, callers are expected to substitute a value (e.g., the HTTP
// wrapper falls back to scheme + r.Host) before invoking the manager.
IssuerBaseURL string
}
RegisterRequest is the input to ClientRegistrar.Register.
type RegisterResponse ¶ added in v0.0.82
type RegisterResponse struct {
Registration *DCRResponse
}
RegisterResponse wraps the RFC 7591 / 7592 response body.
type RotateSecretRequest ¶ added in v0.0.82
type RotateSecretRequest struct {
ClientID string
// PublicKey is the new public key in PEM form. Required for
// asymmetric (RS256 / ES256 / etc.) clients; ignored for symmetric.
PublicKey string
// GracePeriod retains the OLD key in KidStore for this duration so
// in-flight tokens signed with the previous material remain verifiable.
// When zero, AppRegistrar.DefaultGracePeriod is used (default 24h).
// Has no effect when KidStore is nil.
GracePeriod time.Duration
}
RotateSecretRequest is the input to ClientRegistrar.RotateSecret.
type RotateSecretResponse ¶ added in v0.0.82
type RotateSecretResponse struct {
ClientID string
ClientSecret string // present only for symmetric algs
Kid string // computed from the new key material
PreviousKid string // only when the previous key was retained
GracePeriod time.Duration // grace period actually applied
}
RotateSecretResponse is the new key material. Fields populated depend on the algorithm: ClientSecret only for symmetric, PreviousKid + GracePeriod only when KidStore retained the old key.
type UpdateRegistrationRequest ¶ added in v0.0.81
type UpdateRegistrationRequest struct {
// ClientID identifies the registration to update. The wrapper guarantees
// this matches Metadata.ClientID before invoking the manager.
ClientID string
// AccessToken is the registration_access_token issued at registration time
// (or the rotated value from a previous UpdateRegistration). Must match
// the value currently stored on the registration.
AccessToken string
// Metadata is the RFC 7591 / 7592 client metadata to replace the existing
// registration with. Treated as a full-replacement payload per
// RFC 7592 §2.2 — fields omitted from Metadata are cleared on the server.
Metadata *DCRRequest
}
UpdateRegistrationRequest is the input to ClientRegistrationManager.UpdateRegistration.
type UpdateRegistrationResponse ¶ added in v0.0.81
type UpdateRegistrationResponse struct {
Registration *DCRResponse
}
UpdateRegistrationResponse wraps the post-update registration. Registration includes the rotated registration_access_token, which supersedes the one passed in via the request. Callers MUST persist the new token before discarding the old one.