authzserver

package
v0.22.1 Latest Latest
Warning

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

Go to latest
Published: Jun 28, 2026 License: EUPL-1.2 Imports: 35 Imported by: 0

Documentation

Index

Constants

View Source
const (
	GrantTypeAuthorizationCode = "authorization_code"
	GrantTypeClientCredentials = "client_credentials"
	GrantTypeRefreshToken      = "refresh_token"
)
View Source
const ClientAssertionTypeJWTBearer = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"

ClientAssertionTypeJWTBearer is the client_assertion_type for private_key_jwt (RFC 7523 §2.2).

Variables

This section is empty.

Functions

func GenerateJwkSet

func GenerateJwkSet(num int) (jwk.Set, error)

func GenerateRandomJwk

func GenerateRandomJwk() (jwk.Key, error)

func Logger

func Logger(next http.Handler) http.Handler

Logger logs one line per request (method, path, status, duration).

func OAuthErrors

func OAuthErrors(next http.Handler) http.Handler

OAuthErrors normalizes any error response that is not already JSON (e.g. ServeMux's plain-text 404 / 405) into the OAuth JSON error shape, so ALL errors are OAuth-formatted.

func PublicJwkSet

func PublicJwkSet(set jwk.Set) (jwk.Set, error)

func Recover

func Recover(next http.Handler) http.Handler

Recover recovers from a handler panic and returns a 500 OAuth JSON error.

func ThumbprintS256

func ThumbprintS256(jwk jwk.Key) (string, error)

Types

type AuthzServerSession

type AuthzServerSession struct {
	ID                  string                   `json:"id"`
	CreatedAt           time.Time                `json:"created_at"`
	ExpiresAt           time.Time                `json:"expires_at"`
	AccessTokenDuration time.Duration            `json:"access_token_duration"`
	Audience            []string                 `json:"audience"`
	ClientID            string                   `json:"client_id"`
	RedirectURI         string                   `json:"redirect_uri"`
	CodeChallenge       string                   `json:"code_challenge"`
	CodeChallengeMethod string                   `json:"code_challenge_method"`
	State               string                   `json:"state"`
	Scopes              []string                 `json:"scopes"`
	IDPIss              string                   `json:"idp_iss"`
	RequestUri          string                   `json:"request_uri"`
	AuthnClientSession  *oidc.AuthnClientSession `json:"authn_client_session"`
	Code                string                   `json:"code"`
	RefreshToken        string                   `json:"refresh_token"`
	RefreshCount        int                      `json:"refresh_count"`
	LastRefreshAt       time.Time                `json:"last_refresh_at"`
	DPoPThumbprint      string                   `json:"dpop_thumbprint"`
}

type AuthzServerSessionStore

type AuthzServerSessionStore interface {
	GetAuthzServerSessionByID(id string) (*AuthzServerSession, error)
	GetAuthzServerSessionByAuthnState(authnState string) (*AuthzServerSession, error)
	GetAutzhServerSessionByRequestURI(requestURI string) (*AuthzServerSession, error)
	GetAuthzServerSessionByCode(code string) (*AuthzServerSession, error)
	GetAuthzServerSessionByRefreshToken(token string) (*AuthzServerSession, error)
	DeleteRefreshIndex(refreshTokenHash string) error
	SaveAutzhServerSession(session *AuthzServerSession) error
	DeleteAuthzServerSessionByID(id string) error
}

type Client added in v0.22.0

type Client struct {
	ClientID  string `yaml:"client_id" json:"client_id" validate:"required"`
	ProductID string `yaml:"product_id" json:"product_id" validate:"required"`
	// PublicJWK is the client's assertion-verification key as a nested JWK object. It is parsed into
	// a jwk.Key once at registry-build time (see Key).
	PublicJWK map[string]any `yaml:"public_jwk" json:"public_jwk" validate:"required"`
	// contains filtered or unexported fields
}

Client is a registered instance of a Product. A product may have many client instances; each authenticates with private_key_jwt (RFC 7523), proving itself with a client_assertion verified against PublicJWK. Redirect-URI and scope policy live on the Product the client belongs to.

func (*Client) Key added in v0.22.0

func (c *Client) Key() jwk.Key

Key returns the parsed public JWK used to verify the client's assertions.

type ClientAssertionClaims

type ClientAssertionClaims struct {
	Nonce string   `json:"nonce" validate:"required"`
	Iss   string   `json:"iss" validate:"required"`
	Sub   string   `json:"sub" validate:"required"`
	Aud   []string `json:"aud" validate:"required"`
	Iat   int      `json:"iat" validate:"required"`
	Exp   int      `json:"exp" validate:"required"`
	Cnf   struct {
		Jkt string `json:"jkt" validate:"required"`
	} `json:"cnf" validate:"required"`
}

ClientAssertionClaims are the claims of a private_key_jwt client assertion. Beyond the standard RFC 7523 claims it keeps the gematik extensions: a nonce (redeemed against the AS nonce service) and cnf.jkt (the client's DPoP key, to which the issued access token is sender-constrained).

func (*ClientAssertionClaims) Validate

func (c *ClientAssertionClaims) Validate() error

type ClientsRegistry

type ClientsRegistry interface {
	GetClient(clientID string) (*Client, error)
}

type Config

type Config struct {
	BaseDir                    string                   `yaml:"-"`
	Issuer                     string                   `yaml:"issuer" validate:"required"`
	SignJwkPath                string                   `yaml:"sign_jwk_path"`
	ScopesSupported            []string                 `yaml:"scopes_supported"`
	MetadataTemplate           ExtendedMetadata         `yaml:"metadata_template"`
	DefaultIDPIss              string                   `yaml:"default_idp_iss"`
	OidcProviders              []oidc.Config            `yaml:"oidc_providers" validate:"dive"`
	GematikIdp                 []gemidp.ClientConfig    `yaml:"gematik_idp" validate:"dive"`
	ClientsPolicyPath          string                   `yaml:"clients_policy_path"`
	Products                   []Product                `yaml:"products" validate:"omitempty,dive"`
	Clients                    []Client                 `yaml:"clients" validate:"omitempty,dive"`
	OidfRelyingPartyConfigPath string                   `yaml:"oidf_relying_party_path"`
	OidfRelyingPartyConfig     *oidf.RelyingPartyConfig `yaml:"oidf_relying_party" validate:"omitempty"`
	Endpoints                  EndpointsConfig          `yaml:"endpoints" validate:"omitempty"`
	// some values may be set programmatically
	NonceService nonce.Service
	// Store is the kv backend for sessions + nonces. The command opens the Postgres store from
	// DATABASE_URL (it owns the driver dependency) and injects it here; nil ⇒ an in-memory store
	// (tests and dev).
	Store kv.Store
}

type EndpointsConfig

type EndpointsConfig struct {
	AuthorizationServerMetadata string `yaml:"authorization_server_metadata"`
	Jwks                        string `yaml:"jwks"`
	Nonce                       string `yaml:"nonce"`
	OpenIDProviders             string `yaml:"openid_providers"`
	Authorization               string `yaml:"authorization"`
	PushedAuthorizationRequest  string `yaml:"pushed_authorization_request"`
	OPCallback                  string `yaml:"op_callback"`
	GemIDPCallback              string `yaml:"gemidp_callback"`
	Token                       string `yaml:"token"`
	Introspection               string `yaml:"introspection"`
	EntityStatement             string `yaml:"entity_statement"`
	Registration                string `yaml:"registration"`
}

type Error

type Error struct {
	HttpStatus  int    `json:"-"`
	Code        string `json:"error"`
	Description string `json:"error_description,omitempty"`
	URI         string `json:"error_uri,omitempty"`
}

Error is an OAuth 2.0 error response (RFC 6749 §5.2). HttpStatus carries the HTTP status code and is not serialized.

func (Error) Error

func (e Error) Error() string

type ExtendedMetadata

type ExtendedMetadata struct {
	Metadata
	NonceEndpoint           string `json:"nonce_endpoint"`
	OpenidProvidersEndpoint string `json:"openid_providers_endpoint"`
}

Extend the standard OAuth2 server metadata from RFC8414

type IntrospectedSession added in v0.22.0

type IntrospectedSession struct {
	CreatedAt      time.Time `json:"created_at"`
	ExpiresAt      time.Time `json:"expires_at"`
	IDPIss         string    `json:"idp_iss,omitempty"`
	RedirectURI    string    `json:"redirect_uri,omitempty"`
	DPoPThumbprint string    `json:"dpop_thumbprint,omitempty"`
}

type IntrospectionResponse added in v0.22.0

type IntrospectionResponse struct {
	Active    bool           `json:"active"`
	Scope     string         `json:"scope,omitempty"`
	ClientID  string         `json:"client_id,omitempty"`
	Username  string         `json:"username,omitempty"`
	TokenType string         `json:"token_type,omitempty"`
	Exp       int64          `json:"exp,omitempty"`
	Iat       int64          `json:"iat,omitempty"`
	Sub       string         `json:"sub,omitempty"`
	Aud       []string       `json:"aud,omitempty"`
	Iss       string         `json:"iss,omitempty"`
	Jti       string         `json:"jti,omitempty"`
	Cnf       map[string]any `json:"cnf,omitempty"`

	// Extension members: the upstream identity this session was established with, and session details.
	Identity map[string]any       `json:"identity,omitempty"`
	IDToken  string               `json:"id_token,omitempty"`
	Session  *IntrospectedSession `json:"session,omitempty"`
}

IntrospectionResponse is the OAuth 2.0 Token Introspection response (RFC 7662), extended per §2.2 with the brokered upstream OIDC identity and session metadata as service-specific members.

type Metadata

type Metadata struct {
	Issuer                                             string   `json:"issuer" yaml:"issuer"`
	AuthorizationEndpoint                              string   `json:"authorization_endpoint" yaml:"authorization_endpoint"`
	TokenEndpoint                                      string   `json:"token_endpoint" yaml:"token_endpoint"`
	JwksURI                                            string   `json:"jwks_uri,omitempty" yaml:"jwks_uri"`
	RegistrationEndpoint                               string   `json:"registration_endpoint,omitempty" yaml:"registration_endpoint"`
	ScopesSupported                                    []string `json:"scopes_supported" yaml:"scopes_supported"`
	ResponseTypesSupported                             []string `json:"response_types_supported" yaml:"response_types_supported"`
	ResponseModesSupported                             []string `json:"response_modes_supported" yaml:"response_modes_supported"`
	GrantTypesSupported                                []string `json:"grant_types_supported" yaml:"grant_types_supported"`
	TokenEndpointAuthMethodsSupported                  []string `json:"token_endpoint_auth_methods_supported" yaml:"token_endpoint_auth_methods_supported"`
	TokenEndpointAuthSigningAlgValuesSupported         []string `json:"token_endpoint_auth_signing_alg_values_supported" yaml:"token_endpoint_auth_signing_alg_values_supported"`
	ServiceDocumentation                               string   `json:"service_documentation,omitempty" yaml:"service_documentation"`
	UILocalesSupported                                 []string `json:"ui_locales_supported,omitempty" yaml:"ui_locales_supported"`
	OPPolicyURI                                        string   `json:"op_policy_uri,omitempty" yaml:"op_policy_uri"`
	OPTosURI                                           string   `json:"op_tos_uri,omitempty" yaml:"op_tos_uri"`
	RevocationEndpoint                                 string   `json:"revocation_endpoint,omitempty" yaml:"revocation_endpoint"`
	RevocationEndpointAuthMethodsSupported             []string `json:"revocation_endpoint_auth_methods_supported,omitempty" yaml:"revocation_endpoint_auth_methods_supported"`
	RevocationEndpointAuthSigningAlgValuesSupported    []string `` /* 131-byte string literal not displayed */
	IntrospectionEndpoint                              string   `json:"introspection_endpoint,omitempty" yaml:"introspection_endpoint"`
	IntrospectionEndpointAuthMethodsSupported          []string `json:"introspection_endpoint_auth_methods_supported,omitempty" yaml:"introspection_endpoint_auth_methods_supported"`
	IntrospectionEndpointAuthSigningAlgValuesSupported []string `` /* 137-byte string literal not displayed */
	CodeChallengeMethodsSupported                      []string `json:"code_challenge_methods_supported" yaml:"code_challenge_methods_supported"`
	PushedAuthorizationRequestEndpoint                 string   `json:"pushed_authorization_request_endpoint,omitempty" yaml:"pushed_authorization_request_endpoint"`
	RequirePushedAuthorizationRequests                 bool     `json:"require_pushed_authorization_requests,omitempty" yaml:"require_pushed_authorization_requests"`
}

OAuth2 Authorization Server Metadata See https://datatracker.ietf.org/doc/html/rfc8414

type OpenidProviderInfo

type OpenidProviderInfo struct {
	Issuer  string `json:"iss"`
	LogoURI string `json:"logo_uri"`
	Name    string `json:"name"`
	Type    string `json:"type"`
}

OpenidProviderInfo represents the information about an OpenID Provider

type Product added in v0.22.0

type Product struct {
	ProductID         string   `yaml:"product_id" json:"product_id" validate:"required"`
	ProductName       string   `yaml:"product_name" json:"product_name"`
	ManufacturerID    string   `yaml:"manufacturer_id" json:"manufacturer_id"`
	ManufacturerName  string   `yaml:"manufacturer_name" json:"manufacturer_name"`
	Platform          string   `yaml:"platform" json:"platform"`
	PlatformProductID any      `yaml:"platform_product_id" json:"platform_product_id"`
	RedirectURIs      []string `yaml:"redirect_uris" json:"redirect_uris"`
	Scopes            []string `yaml:"scopes" json:"scopes"`
	// OIDCRedirectURIs are the redirect_uri(s) the upstream IdP sends the user back to the AS at (gematik
	// oidc_redirect_uri), instead of the default /op-callback — so the IdP redirects straight to the AS with
	// no app-side intermediary popup. Registered into the OIDF entity statement (see initRelyingParty).
	OIDCRedirectURIs []string `yaml:"oidc_redirect_uris" json:"oidc_redirect_uris"`
	PushGateway      any      `yaml:"push_gateway" json:"push_gateway"`
}

Product is an application registered with the authorization server. It owns the redirect-URI and scope policy that all of its client instances share. Products are loaded inline from the config (products:) and/or from a gematik clients-policy file (clients_policy_path).

func LoadProductsFile added in v0.22.0

func LoadProductsFile(path string) ([]*Product, error)

LoadProductsFile reads a gematik clients-policy file, whose top-level key is `clients`.

func (*Product) IsAllowedRedirectURI added in v0.22.0

func (p *Product) IsAllowedRedirectURI(redirectURI string) bool

func (*Product) IsAllowedScope added in v0.22.0

func (p *Product) IsAllowedScope(scope string) bool

func (*Product) IsAllowedScopes added in v0.22.0

func (p *Product) IsAllowedScopes(scopes []string) bool

type ProductsRegistry added in v0.22.0

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

func NewProductsRegistry added in v0.22.0

func NewProductsRegistry(products []*Product) *ProductsRegistry

func (*ProductsRegistry) AllOIDCRedirectURIs added in v0.22.0

func (r *ProductsRegistry) AllOIDCRedirectURIs() []string

AllOIDCRedirectURIs returns the deduplicated oidc_redirect_uri across every product — the redirect URIs the upstream IdP may send the user back to the AS at. They are injected into the OIDF entity statement.

func (*ProductsRegistry) GetProduct added in v0.22.0

func (r *ProductsRegistry) GetProduct(productID string) (*Product, error)

type Server

type Server struct {
	Metadata ExtendedMetadata
	// contains filtered or unexported fields
}

func New

func New(cfg Config) (*Server, error)

func NewFromConfigFile

func NewFromConfigFile(filename string) (*Server, error)

func (*Server) AuthorizationEndpoint

func (s *Server) AuthorizationEndpoint(w http.ResponseWriter, r *http.Request) error

func (*Server) GetOpenidClient

func (s *Server) GetOpenidClient(issuer string) (oidc.Client, error)

GetOpenidClient returns an OpenID Connect client for the given issuer

func (*Server) IntrospectionEndpoint added in v0.22.0

func (s *Server) IntrospectionEndpoint(w http.ResponseWriter, r *http.Request) error

IntrospectionEndpoint implements OAuth 2.0 Token Introspection (RFC 7662). The caller must authenticate as a client, and only the client a token was issued to may introspect it; any other outcome answers {"active": false} so a token is never revealed to a party that does not own it.

func (*Server) JWKS

func (s *Server) JWKS(w http.ResponseWriter, r *http.Request) error

JWKS serves the JSON Web Key Set for the server.

func (*Server) MetadataEndpoint

func (s *Server) MetadataEndpoint(w http.ResponseWriter, r *http.Request) error

MetadataEndpoint serves the authorization server metadata document (RFC 8414).

func (*Server) MountRoutes

func (s *Server) MountRoutes(mux *http.ServeMux)

MountRoutes registers the authorization server routes on the given ServeMux. Each handler is wrapped by s.handle, which renders returned errors as OAuth JSON (RFC 6749 §5.2).

func (*Server) NonProdIssueTokens

func (s *Server) NonProdIssueTokens(sessionId string) (*TokenResponse, error)

func (*Server) NonProdStartSession

func (s *Server) NonProdStartSession(session *AuthzServerSession) error

Non-production code for the OAuth2 server

func (*Server) NonceEndpoint

func (s *Server) NonceEndpoint(w http.ResponseWriter, r *http.Request) error

NonceEndpoint returns a fresh replay nonce as a text/plain body (GET only).

func (*Server) OPCallbackEndpoint

func (s *Server) OPCallbackEndpoint(w http.ResponseWriter, r *http.Request) error

OPCallbackEndpoint handles the callback from the OpenID Provider

func (*Server) OpenidProviders

func (s *Server) OpenidProviders() ([]OpenidProviderInfo, error)

OpenidProviders returns the list of OpenID Providers supported by the server

func (*Server) OpenidProvidersEndpoint

func (s *Server) OpenidProvidersEndpoint(w http.ResponseWriter, r *http.Request) error

OpenidProvidersEndpoint serves the list of OpenID Providers supported by the server

func (*Server) PAREndpoint

func (s *Server) PAREndpoint(w http.ResponseWriter, r *http.Request) error

PAREndpoint (RFC 9126) accepts a client-authenticated pushed authorization request: it validates the parameters exactly as /authorize would, stores them, and returns a single-use request_uri the client then hands to /authorize. FAPI 2.0 requires this — the request is integrity-protected (back-channel, private_key_jwt) rather than carried in the browser URL.

func (*Server) RegistrationEndpoint

func (s *Server) RegistrationEndpoint(w http.ResponseWriter, r *http.Request) error

RegistrationEndpoint is intentionally closed. Clients are provisioned out-of-band (operator config now, federation later), so dynamic client registration (RFC 7591) is not offered. The endpoint stays advertised in the metadata and returns a coherent OAuth error — the same shape as every other endpoint — rather than a bare 404, leaving a clean seam to enable gated registration later (swap this body, add storage).

func (*Server) TokenEndpoint

func (s *Server) TokenEndpoint(w http.ResponseWriter, r *http.Request) error

TokenEndpoint handles the token request for various grant types

type StaticClientsRegistry

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

func NewStaticClientsRegistry added in v0.22.0

func NewStaticClientsRegistry(clients []Client) (*StaticClientsRegistry, error)

NewStaticClientsRegistry parses each client's public JWK and indexes the clients by client_id.

func (*StaticClientsRegistry) GetClient added in v0.22.0

func (r *StaticClientsRegistry) GetClient(clientID string) (*Client, error)

type TokenResponse

type TokenResponse struct {
	AccessToken      string `json:"access_token"`
	TokenType        string `json:"token_type"`
	ExpiresIn        int    `json:"expires_in"`
	Scope            string `json:"scope,omitempty"`
	RefreshToken     string `json:"refresh_token,omitempty"`
	RefreshExpiresIn int    `json:"refresh_expires_in,omitempty"`
}

Jump to

Keyboard shortcuts

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