authn

package
v1.2.4 Latest Latest
Warning

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

Go to latest
Published: May 12, 2026 License: MIT Imports: 28 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FailureCategory added in v1.2.0

func FailureCategory(err error) string

FailureCategory classifies an authentication error into a coarse-grained label for logs/metrics. It must never echo the raw error text to clients — callers strip details before responding (e.g. mcp.writeAuthFailure or the gRPC interceptor) and use this only for server-side observability.

The categorisation is intentionally pattern-matched against lowercased substrings of err.Error() rather than typed sentinels because authn errors are constructed via fmt.Errorf at multiple sites (oidc, mtls, forward_auth, static_bearer). Adding a new sentinel everywhere would be churn for a helper whose only consumer is structured logging.

func ProtectedResourceHandler

func ProtectedResourceHandler(cfg Config) http.Handler

ProtectedResourceHandler returns an http.Handler that serves the metadata document for an OIDC-protected MCP resource. The endpoint MUST be unauthenticated per RFC 9728 §3.

Returns nil when the supplied config has no resource URI configured — callers should not mount the endpoint in that case.

func UnauthorizedHeaderValue added in v1.2.1

func UnauthorizedHeaderValue(errCode, errDesc string) string

UnauthorizedHeaderValue returns the RFC 6750 §3 WWW-Authenticate header used by MCP endpoints that need a JSON-RPC body while preserving OAuth bearer challenge semantics.

func WithPrincipal

func WithPrincipal(ctx context.Context, p *Principal) context.Context

WithPrincipal attaches p to ctx so downstream handlers (enforcement, rate limiters, audit, tools) can read the authenticated identity.

func WriteUnauthorized

func WriteUnauthorized(w http.ResponseWriter, errCode, errDesc string)

WriteUnauthorized emits an HTTP 401 with a WWW-Authenticate header in RFC 6750 §3 form. errCode and errDesc become the `error` and `error_description` parameters; pass empty strings to omit them. The realm is fixed to "clockify-mcp" to match the metadata document.

Types

type Authenticator

type Authenticator interface {
	Authenticate(context.Context, *http.Request) (Principal, error)
}

func New

func New(cfg Config) (Authenticator, error)

type Config

type Config struct {
	Mode            Mode
	BearerToken     string
	DefaultTenantID string
	TenantClaim     string
	SubjectClaim    string
	OIDCIssuer      string
	OIDCAudience    string
	OIDCJWKSURL     string
	OIDCJWKSPath    string
	// OIDCJWKSAllowPrivate permits an explicit OIDCJWKSURL to target
	// loopback, private, link-local, or reserved addresses. Default false
	// rejects those hosts so a misconfigured JWKS URL cannot probe local
	// or cloud-metadata networks. Enable only for local tests or a trusted
	// private IdP endpoint.
	OIDCJWKSAllowPrivate bool
	ForwardTenantHeader  string
	ForwardSubjectHeader string
	// RequireForwardTenantClaim rejects forward_auth requests when
	// ForwardTenantHeader is absent or empty, instead of falling back to
	// DefaultTenantID. Default false preserves self-hosted single-tenant
	// behaviour; hosted profiles should enable it.
	RequireForwardTenantClaim bool
	// ForwardAuthTrustedProxies, when non-empty, gates the
	// forward_auth authenticator: a request whose direct source
	// (r.RemoteAddr) is not inside one of these networks is
	// rejected before X-Forwarded-User / X-Forwarded-Tenant are
	// inspected. Empty preserves the historical "trust every
	// source" posture for self-hosted single-tenant deployments
	// where the operator owns the network boundary.
	ForwardAuthTrustedProxies []*net.IPNet
	MTLSTenantHeader          string
	// MTLSTenantSource selects how the mtls authenticator derives the
	// tenant identifier. Valid values:
	//   "cert"           — verified client certificate only (URI SAN
	//                      patterns clockify-mcp://tenant/<id> or
	//                      spiffe://.../tenant/<id>, then Subject
	//                      Organization fallback). Default; the only
	//                      sound choice for direct native mTLS because
	//                      a client-controlled header would let any
	//                      authenticated client claim any tenant.
	//   "header"         — request header (MTLSTenantHeader) only.
	//                      Reserve for deployments where an upstream
	//                      proxy terminates mTLS, validates it, and
	//                      stamps the tenant header from a trusted
	//                      source after stripping any client copy.
	//   "header_or_cert" — header first, then cert. Hybrid; mainly
	//                      useful for the brief window of migrating
	//                      from header-based to cert-based identity.
	// Empty string is treated as "cert" (the safe default).
	MTLSTenantSource string
	// RequireMTLSTenant rejects authentication when no tenant could be
	// derived from the configured source(s). Default false retains the
	// historical "fall back to DefaultTenantID" behaviour for
	// self-hosted single-tenant deployments.
	RequireMTLSTenant bool
	// OIDCResourceURI is the canonical resource URI this server represents
	// per RFC 8707 (OAuth 2.0 Resource Indicators) and the MCP OAuth 2.1
	// profile. When set, every OIDC token must list this URI in its `aud`
	// claim — token-binding to the protected resource. Empty disables the
	// extra check (back-compat with the simple OIDCAudience match).
	OIDCResourceURI string
	// OIDCStrict enables hosted-service-grade claim validation: tokens
	// missing an `exp` claim are rejected. Config.Load enforces the
	// audience/resource binding requirement at startup; this flag
	// covers the per-token claim checks. Default false preserves
	// self-hosted behaviour.
	OIDCStrict bool
	// RequireTenantClaim, when true, makes the OIDC authenticator
	// reject any token whose tenant claim is empty — instead of
	// quietly falling back to DefaultTenantID. Default false preserves
	// self-hosted single-tenant behaviour.
	RequireTenantClaim bool
	// OIDCRequireKID rejects JWTs that omit the JOSE kid header instead of
	// falling back to the lone key in a JWKS. Hosted profiles enable this to
	// make key rotation and provenance explicit.
	OIDCRequireKID bool
	// OIDCVerifyCacheTTL is the hard ceiling on cached verify results.
	// Zero selects the default (oidcVerifyCacheMaxTTL); values are
	// clamped to [oidcVerifyCacheMinTTL, oidcVerifyCacheTTLCeiling].
	// Larger values amortise the ~54µs verify cost further, but extend
	// the window after a token is revoked before the next Authenticate
	// call re-checks the claims. Operators should pick this
	// consciously; the default stays conservative at 60s.
	OIDCVerifyCacheTTL time.Duration
	// OIDCJWKSCacheTTL is the lifetime of the in-memory JWKS document
	// cache. Zero selects the conservative 5-minute default; values
	// are clamped to [jwksCacheMinTTL, jwksCacheMaxTTL] (1 minute to
	// 24 hours). Hosted services that rotate keys frequently can
	// shorten the window so a rotation lands without waiting for the
	// next periodic reload; F2's kid-miss-triggered refresh covers
	// the rotation-in-flight case independently. Wired from
	// MCP_OIDC_JWKS_CACHE_TTL.
	OIDCJWKSCacheTTL time.Duration
	// HTTPClient overrides the JWKS fetcher's transport. Tests inject
	// httptest-backed clients here; production code leaves it nil and
	// uses http.DefaultClient.
	HTTPClient *http.Client
}

type Mode

type Mode string
const (
	ModeStaticBearer Mode = "static_bearer"
	ModeOIDC         Mode = "oidc"
	ModeForwardAuth  Mode = "forward_auth"
	ModeMTLS         Mode = "mtls"
)

type Principal

type Principal struct {
	Subject   string
	TenantID  string
	AuthMode  Mode
	Claims    map[string]string
	SessionID string
}

func PrincipalFromContext

func PrincipalFromContext(ctx context.Context) (*Principal, bool)

PrincipalFromContext returns the principal attached by WithPrincipal, or (nil, false) when the request is unauthenticated (static-bearer without context installation, tests, etc.).

type ProtectedResourceMetadata

type ProtectedResourceMetadata struct {
	Resource               string   `json:"resource"`
	AuthorizationServers   []string `json:"authorization_servers,omitempty"`
	BearerMethodsSupported []string `json:"bearer_methods_supported,omitempty"`
	ScopesSupported        []string `json:"scopes_supported,omitempty"`
	ResourceName           string   `json:"resource_name,omitempty"`
}

ProtectedResourceMetadata is the body served at /.well-known/oauth-protected-resource per the MCP OAuth 2.1 profile (RFC 9728). Clients fetch this document unauthenticated to discover which authorization server issues tokens for the resource and which bearer methods the resource accepts.

Jump to

Keyboard shortcuts

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