Documentation
¶
Index ¶
- Constants
- func GetActorIdentityFromRequest(r *http.Request) (string, bool)
- func GetAudienceFromRequest(r *http.Request) (string, bool)
- func GetEmailFromRequest(r *http.Request) (string, bool)
- func GetFirstNameFromRequest(r *http.Request) (string, bool)
- func GetLastNameFromRequest(r *http.Request) (string, bool)
- func GetOrgHandleFromRequest(r *http.Request) (string, bool)
- func GetOrgNameFromRequest(r *http.Request) (string, bool)
- func GetOrganizationFromRequest(r *http.Request) (string, bool)
- func GetPlatformRolesFromRequest(r *http.Request) ([]string, bool)
- func GetRolesFromRequest(r *http.Request) ([]string, bool)
- func GetScopeFromRequest(r *http.Request) (string, bool)
- func GetSubClaimFromRequest(r *http.Request) (string, bool)
- func GetUserIDFromRequest(r *http.Request) (string, bool)
- func GetUsernameFromRequest(r *http.Request) (string, bool)
- func HasEffectiveScope(r *http.Request, mode, scope string) bool
- func InitClaimsAuthz()
- func InitScopeAuthz()
- func LoadRoleScopeMap(path string) (map[string][]string, error)
- func LocalJWTAuthMiddleware(config AuthConfig) func(http.Handler) http.Handler
- func MapErrors(slogger *slog.Logger, next ErrorHandlerFunc) http.HandlerFunc
- func NewTestContextMiddleware(next http.Handler) http.Handler
- func OrganizationResolverMiddleware(resolve OrgUUIDResolver) func(http.Handler) http.Handler
- func PlatformClaimsMiddleware(claimNames ClaimMappings) func(http.Handler) http.Handler
- func RequireOrganization(organizationParam string) func(http.Handler) http.Handler
- func ScopeEnforcer(registry *ScopeRegistry, cfg ScopeEnforcerConfig) (func(http.Handler) http.Handler, error)
- func ValidateRoleScopeMap(m map[string][]string, registry *ScopeRegistry) error
- func ValidateScopeRegistryRoutes(routes RouteMatcher, registry *ScopeRegistry) error
- func WithOrganization(r *http.Request, org string) *http.Request
- func WithScope(r *http.Request, scope string) *http.Request
- func WithUserID(r *http.Request, id string) *http.Request
- type AuthConfig
- type Authenticator
- type ClaimMappings
- type CustomClaims
- type ErrorHandlerFunc
- type JWTAuthenticator
- type Operation
- type OrgUUIDResolver
- type RouteMatcher
- type ScopeEnforcerConfig
- type ScopeRegistry
Constants ¶
const ( // ValidationModeScope validates using the JWT scope claim directly. ValidationModeScope = "scope" // ValidationModeRole validates by expanding IDP roles into platform roles. ValidationModeRole = "role" )
const PlatformScopePrefix = "ap:"
PlatformScopePrefix is the namespace of the scopes this server declares and enforces. Scopes in any other namespace belong to a sibling component that trusts the same token (the Developer Portal's "dp:" scopes, for example): this server only mints them, so it can neither confirm nor deny that they exist.
Variables ¶
This section is empty.
Functions ¶
func GetActorIdentityFromRequest ¶
GetActorIdentityFromRequest resolves the raw identity-provider identifier for the actor behind r, preferring the token's "sub" claim and falling back to GetUserIDFromRequest (configured-claim/user_id/sub) when sub is unavailable (e.g. non-OIDC IdPs, or test/internal callers that only set the legacy context key). ok is false only when neither source has a value — callers that must reject unauthenticated requests should treat that as 401. This mirrors the precedence used by service.IdentityService.InternalUserID; use it at call sites that need the raw id before mapping (e.g. to distinguish "claim missing" from "mapping failed").
func GetAudienceFromRequest ¶
GetAudienceFromRequest extracts the audience from the request context.
func GetEmailFromRequest ¶
GetEmailFromRequest extracts the email from the request context.
func GetFirstNameFromRequest ¶
GetFirstNameFromRequest extracts the first name from the request context.
func GetLastNameFromRequest ¶
GetLastNameFromRequest extracts the last name from the request context.
func GetOrgHandleFromRequest ¶
GetOrgHandleFromRequest extracts the organization handle from the request context.
func GetOrgNameFromRequest ¶
GetOrgNameFromRequest extracts the organization display name from the request context.
func GetOrganizationFromRequest ¶
GetOrganizationFromRequest extracts the organization claim from the request context.
func GetPlatformRolesFromRequest ¶
GetPlatformRolesFromRequest extracts platform roles from the request context.
func GetRolesFromRequest ¶
GetRolesFromRequest parses the space-separated scope string into individual roles.
func GetScopeFromRequest ¶
GetScopeFromRequest extracts the scope from the request context.
func GetSubClaimFromRequest ¶
GetSubClaimFromRequest extracts the token's OIDC "sub" (subject) claim from the request context. This is the raw IdP subject identifier — distinct from GetUserIDFromRequest, which returns the configured-claim/user_id/sub precedence value used historically for audit columns. Prefer this for keying the internal-UUID mapping (see service.IdentityService).
func GetUserIDFromRequest ¶
GetUserIDFromRequest extracts the user ID from the request context.
func GetUsernameFromRequest ¶
GetUsernameFromRequest extracts the username from the request context.
func HasEffectiveScope ¶ added in v0.12.0
HasEffectiveScope reports whether the request's effective scopes (the scope claim in "scope" mode, or IDP roles expanded via the role-scope map in "role" mode) satisfy the given scope, honoring ":*" wildcards the same way ScopeEnforcer does.
func LoadRoleScopeMap ¶
LoadRoleScopeMap reads a role-to-scope-mapping.yaml file and returns a map from IDP role name to the list of platform scopes that role grants. Each user token may carry multiple roles; the caller is expected to union the scope lists at request time.
func LocalJWTAuthMiddleware ¶
func LocalJWTAuthMiddleware(config AuthConfig) func(http.Handler) http.Handler
LocalJWTAuthMiddleware returns a middleware for locally-issued JWT validation. Used only when IDP mode is disabled.
func MapErrors ¶ added in v0.11.2
func MapErrors(slogger *slog.Logger, next ErrorHandlerFunc) http.HandlerFunc
MapErrors adapts an ErrorHandlerFunc to http.HandlerFunc for registration on the mux. It is the single catch point for handler errors: it recovers panics into a structured 500, maps *apperror.Error values onto the wire via apperror.WriteHTTP, and collapses any other error into a generic 500 INTERNAL_ERROR so internal details never reach the client.
func NewTestContextMiddleware ¶
NewTestContextMiddleware creates an http.Handler middleware for integration tests. It reads X-Test-Org, X-Test-User, and X-Test-Scope request headers and injects the values into the request context so that GetOrganizationFromRequest / GetUsernameFromRequest / GetScopeFromRequest work without a real JWT. Never use this in production code.
func OrganizationResolverMiddleware ¶
func OrganizationResolverMiddleware(resolve OrgUUIDResolver) func(http.Handler) http.Handler
OrganizationResolverMiddleware resolves the organization claim populated by the authentication middleware into the platform organization UUID, and stores that UUID back under the organization context key. Downstream handlers therefore scope their queries by the correct UUID regardless of whether the token carries the platform UUID (file-based auth) or the IDP's organization id (IDP auth).
When the claim does not resolve to an existing organization (e.g. during registration of a brand-new organization), the context is left holding the raw claim, so GetOrganizationFromRequest still returns the IDP's original organization reference in that case.
func PlatformClaimsMiddleware ¶
func PlatformClaimsMiddleware(claimNames ClaimMappings) func(http.Handler) http.Handler
PlatformClaimsMiddleware extracts platform-specific values from the AuthContext set by common/authenticators.AuthMiddleware (IDP mode) and populates per-key context entries.
func RequireOrganization ¶
RequireOrganization returns a middleware that aborts with 403 if the token's organization does not match the organization ID in the URL path value.
func ScopeEnforcer ¶
func ScopeEnforcer(registry *ScopeRegistry, cfg ScopeEnforcerConfig) (func(http.Handler) http.Handler, error)
ScopeEnforcer returns a middleware that reads the required scopes for each request from the OpenAPI ScopeRegistry and enforces them.
The matched route template (e.g. "GET /api/v0.9/rest-apis/{restApiId}") is resolved via cfg.Routes, then looked up in the registry. Enforcement is deny-by-default: a request that matches a registered route carrying no scope requirement is rejected rather than passed through, so a route that is added without a corresponding OpenAPI security block fails closed instead of becoming a silently unprotected endpoint. Requests under cfg.SkipPaths, and requests the authenticator flagged as authz-skipped, bypass the check.
It returns an error when the configuration cannot enforce anything it claims to (GO-AUTH-011) — enforcement enabled with no registry or no route matcher — so the server refuses to start rather than degrading to a pass-through.
func ValidateRoleScopeMap ¶
func ValidateRoleScopeMap(m map[string][]string, registry *ScopeRegistry) error
ValidateRoleScopeMap checks the scopes referenced in the map, failing fast at startup rather than at request time — an unrecognized scope name is almost certainly a typo that would otherwise surface as a silent 403.
Validation is namespace-scoped. A scope in this server's own namespace (PlatformScopePrefix) must be declared in the OpenAPI spec — that includes scopes contributed by compiled-in plugins, so this must run after the plugin specs are merged. A scope in another component's namespace is checked only for well-formedness: the roles file is where a role's grants across the whole platform are described, and it is the only place a file-mode user's grants can be expressed, so refusing scopes this server does not declare would make them ungrantable.
func ValidateScopeRegistryRoutes ¶ added in v0.14.0
func ValidateScopeRegistryRoutes(routes RouteMatcher, registry *ScopeRegistry) error
ValidateScopeRegistryRoutes checks that every operation the scope registry declares, when probed against the router, resolves to a route whose pattern the registry can find again.
ScopeEnforcer is deny-by-default: a matched route with no registry entry is rejected. So a declared operation whose probe lands on a *structurally different* route — one registered under a different path shape or base path — means the real route has no entry of its own and would be denied at runtime. Checking the mapping at startup turns that into a loud refusal to start (GO-AUTH-011) rather than a 403 discovered on a live endpoint.
A declared operation the router matches nothing for is not an error: it is a dead registry entry protecting a route that does not exist, and requests to that path already get the router's 404. The reverse direction — a route registered with no declared scope — needs no check here either: ScopeEnforcer already fails those closed at request time.
func WithOrganization ¶
WithOrganization is a helper for tests to inject an organization into the request context.
Types ¶
type AuthConfig ¶
type AuthConfig struct {
// PublicKey is the RSA public key used to verify token signatures (RS256).
// Only asymmetric verification is supported; symmetric (HMAC) and unsigned
// ("none") tokens are rejected.
PublicKey *rsa.PublicKey
TokenIssuer string
SkipPaths []string
SkipValidation bool
// ClaimMappings is the same claim-name mapping used by IDP mode
// (PlatformClaimsMiddleware) and by the file-mode login endpoint when it
// signs tokens — one mapping shared by issuance and validation.
ClaimMappings ClaimMappings
}
AuthConfig holds the configuration for the local JWT (non-IDP) authentication path.
type Authenticator ¶
type Authenticator interface {
// Middleware returns the ordered handler chain that validates credentials
// and populates the request context with identity values.
Middleware() []func(http.Handler) http.Handler
}
Authenticator abstracts credential validation. Implementations populate the request context with identity values (user_id, organization, scope, platform_roles, …) when authentication succeeds, and write an error response on failure.
type ClaimMappings ¶ added in v0.13.0
type ClaimMappings struct {
OrganizationClaim string
OrgNameClaim string
OrgHandleClaim string
UserIDClaim string
UsernameClaim string
EmailClaim string
ScopeClaim string
RolesClaimPath string
RoleScopeMap map[string][]string
}
ClaimMappings holds the JWT claim names used to extract identity values, shared by the local-JWT (internal_token/file) and IDP auth paths.
type CustomClaims ¶
type CustomClaims struct {
Audience string `json:"aud"`
Email string `json:"email"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
JTI string `json:"jti"`
Organization string `json:"organization"`
Scope string `json:"scope"`
Username string `json:"username"`
jwt.RegisteredClaims
}
CustomClaims represents the JWT claims structure used in local JWT (non-IDP) mode.
func GetClaimsFromRequest ¶
func GetClaimsFromRequest(r *http.Request) (*CustomClaims, bool)
GetClaimsFromRequest extracts the full CustomClaims object from the request context.
type ErrorHandlerFunc ¶ added in v0.11.2
type ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request) error
ErrorHandlerFunc is the handler signature for routes that participate in centralized error mapping: instead of writing HTTP error responses inline, the handler returns an error (ideally an *apperror.Error) and MapErrors logs it and writes the standard apperror.ErrorResponse. Success responses are still written directly by the handler — the mapper only owns the error path.
type JWTAuthenticator ¶
type JWTAuthenticator struct {
// contains filtered or unexported fields
}
JWTAuthenticator wraps one or more middleware functions that perform JWT validation and claims extraction behind the Authenticator interface.
func NewJWTAuthenticator ¶
func NewJWTAuthenticator(handlers ...func(http.Handler) http.Handler) *JWTAuthenticator
NewJWTAuthenticator creates an Authenticator backed by the given middleware handlers.
func (*JWTAuthenticator) Middleware ¶
func (a *JWTAuthenticator) Middleware() []func(http.Handler) http.Handler
type Operation ¶ added in v0.14.0
Operation is a single (method, path pattern) entry in the registry.
type OrgUUIDResolver ¶
OrgUUIDResolver maps a token's organization claim to the platform organization UUID, returning true when a matching organization exists.
type RouteMatcher ¶ added in v0.14.0
RouteMatcher resolves the route pattern a request will match, without serving it. *http.ServeMux satisfies this interface (Go 1.22+).
type ScopeEnforcerConfig ¶
type ScopeEnforcerConfig struct {
// ValidationMode selects how authorization is enforced: "scope" (default) or "role".
ValidationMode string
// Enabled controls whether scope checks are enforced.
Enabled bool
// Routes resolves the route pattern a request will match. It is required
// whenever Enabled is set: ScopeEnforcer runs as an outer middleware, ahead
// of the router, so r.Pattern is still empty when it executes and the
// registry lookup can only be keyed off a pattern resolved from the router
// itself. Pass the *http.ServeMux the routes are registered on.
Routes RouteMatcher
// SkipPaths are path prefixes exempt from scope enforcement — health/metrics
// probes, the login endpoint, and the internal routes authenticated by a
// gateway token rather than a user JWT. Mirrors config.Auth.SkipPaths, which
// is the same list the authentication middleware bypasses — through the same
// hasPathPrefix matcher, so both exempt exactly the same requests.
SkipPaths []string
}
ScopeEnforcerConfig holds options for the ScopeEnforcer middleware.
type ScopeRegistry ¶
type ScopeRegistry struct {
// contains filtered or unexported fields
}
ScopeRegistry maps (HTTP method, net/http path pattern) to the acceptable scopes for that operation. Scopes are OR-evaluated: the caller needs at least one.
func LoadScopeRegistry ¶
func LoadScopeRegistry(specPath string) (*ScopeRegistry, error)
LoadScopeRegistry parses the OpenAPI spec at specPath and returns a ScopeRegistry populated from the standard security field on each operation. The first servers[].url is used to derive the base path prefix that maps spec paths to actual net/http route patterns (e.g. /api/v1 + /projects → /api/v1/projects).
func LoadScopeRegistryFromBytes ¶
func LoadScopeRegistryFromBytes(data []byte) (*ScopeRegistry, error)
LoadScopeRegistryFromBytes parses an OpenAPI 3.x YAML document from in-memory bytes and returns a populated ScopeRegistry. Intended for plugins that embed their own OpenAPI spec via go:embed.
func (*ScopeRegistry) AllScopes ¶
func (r *ScopeRegistry) AllScopes() map[string]struct{}
AllScopes returns the set of every scope name declared across all operations. Used at startup to validate that role-to-scope-mapping.yaml only references known scopes.
func (*ScopeRegistry) Len ¶ added in v0.13.1
func (r *ScopeRegistry) Len() int
Len returns the number of (method, path) operations that carry a scope requirement. Operations with no security block contribute no entry.
func (*ScopeRegistry) Lookup ¶
func (r *ScopeRegistry) Lookup(method, path string) ([]string, bool)
Lookup returns the required scopes for the given HTTP method and path pattern (e.g. r.Method and the path portion of r.Pattern). found is false when the route is not in the OpenAPI spec, meaning no scope requirement was declared.
func (*ScopeRegistry) Merge ¶
func (r *ScopeRegistry) Merge(other *ScopeRegistry)
Merge copies all scope entries from other into r, overwriting on key conflicts. Used to merge plugin-contributed OpenAPI specs into the main registry.
func (*ScopeRegistry) Operations ¶ added in v0.14.0
func (r *ScopeRegistry) Operations() []Operation
Operations returns every (method, path) the registry declares a scope requirement for. Used at startup to verify each declared operation actually resolves to a registered route.