auth

package
v0.0.3 Latest Latest
Warning

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

Go to latest
Published: Jul 16, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package auth provides JWT and API-key authentication for forge-generated Connect RPC services.

The library exposes a Validator that authenticates incoming Connect requests against a configured provider ("jwt", "api_key", "both", or "none") and a Connect interceptor that attaches the resulting Claims to the request context.

Generated forge projects import this package via a thin shim in pkg/middleware/auth_gen.go. The shim wires the project's Config from forge.yaml and exposes a project-local Claims alias (type Claims = auth.Claims).

Index

Constants

View Source
const (
	ProviderNone   = "none"
	ProviderJWT    = "jwt"
	ProviderAPIKey = "api_key"
	ProviderBoth   = "both"
)

Provider names recognized by Config.

Variables

This section is empty.

Functions

This section is empty.

Types

type APIKeyConfig

type APIKeyConfig struct {
	// Header is the HTTP header name carrying the API key.
	// Defaults to "X-API-Key" when empty.
	Header string
}

APIKeyConfig holds API-key-specific settings.

func (APIKeyConfig) EffectiveHeader

func (a APIKeyConfig) EffectiveHeader() string

EffectiveHeader returns Header or the "X-API-Key" default.

type Claims

type Claims struct {
	UserID string         `json:"user_id"`
	Email  string         `json:"email"`
	OrgID  string         `json:"org_id"`
	Role   string         `json:"role"`
	Roles  []string       `json:"roles"`
	Raw    map[string]any `json:"raw,omitempty"`
}

Claims is the canonical claims shape produced by all forge auth flows.

The struct mirrors the per-project pkg/middleware/Claims that earlier forge versions generated. The generated shim re-exports it via type Claims = auth.Claims so existing project code (referring to middleware.Claims) keeps compiling.

Raw carries the full decoded JWT payload (or any provider-specific map a UserResolver chooses to attach). Projects that need access to claims outside the fixed fields — Supabase user_metadata, Auth0 app_metadata, Clerk org_permissions, etc. — should read from Raw instead of forking the Claims type.

type Config

type Config struct {
	// Provider is one of "jwt", "api_key", "both", "none". Required.
	Provider string

	// JWT configures JWT validation. Used when Provider is "jwt" or "both".
	JWT JWTConfig

	// APIKey configures API key validation. Used when Provider is
	// "api_key" or "both". A [KeyValidator] must be supplied separately
	// via [Validator.SetKeyValidator] or [Config.KeyValidator].
	APIKey APIKeyConfig

	// SkipMethods is the list of fully-qualified Connect procedure names
	// that bypass auth (in addition to the built-in /Health/ skip).
	SkipMethods []string

	// KeyValidator validates API keys. Required when APIKey auth is enabled.
	KeyValidator KeyValidator

	// TokenValidators, when non-empty, replaces the built-in single-secret
	// JWT path with an ordered fallback chain. Each entry validates the
	// bearer token independently; the first to accept wins. Use this when
	// a service must accept tokens from more than one issuer — typically
	// during an auth-provider migration (e.g. Supabase HMAC tokens
	// alongside Auth0 JWKS tokens).
	//
	// When empty the legacy JWT (single secret / JWKSURL) path is used,
	// preserving backwards compatibility for projects that only need one
	// validator.
	TokenValidators []TokenValidator

	// UserResolver, when non-nil, projects the raw decoded JWT payload onto
	// [Claims]. Use it when provider-specific claim shapes (Supabase
	// user_metadata, Auth0 app_metadata, Clerk org_permissions, etc.) need
	// to drive the Claims you hand to downstream code.
	//
	// When nil the validator uses built-in shape extraction (sub, email,
	// org_id, role, roles) and still attaches the full payload to
	// Claims.Raw so downstream code can inspect it.
	UserResolver UserResolver
}

Config configures a Validator.

Provider selects the authentication scheme. JWT, APIKey and SkipMethods further customize each scheme. The library reads JWT_SECRET from the environment when JWTConfig.Secret is empty (preserving the legacy behaviour of the generated auth_gen.go template).

type HMACValidator

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

HMACValidator validates JWTs signed with an HMAC algorithm (HS256/384/512).

Zero value is not usable; construct with NewHMACValidator. Secret is resolved from SecretEnv at construction time; rotating secrets requires constructing a new validator (which is cheap — no I/O).

func NewHMACValidator

func NewHMACValidator(cfg HMACValidatorConfig) (*HMACValidator, error)

NewHMACValidator constructs a validator for HMAC-signed JWTs.

Returns an error when no secret can be resolved (neither Secret, SecretEnv, nor JWT_SECRET is set) or when SigningMethod is not an HS* algorithm.

func (*HMACValidator) ValidateToken

func (v *HMACValidator) ValidateToken(tokenString string) (*Claims, error)

ValidateToken implements TokenValidator.

type HMACValidatorConfig

type HMACValidatorConfig struct {
	// SigningMethod is the expected JWT alg (e.g. "HS256"). Required.
	SigningMethod string

	// Secret is the symmetric secret. If empty, SecretEnv is consulted; if
	// SecretEnv is also empty, "JWT_SECRET" is consulted as a last resort.
	Secret string

	// SecretEnv is the environment variable name to read the secret from
	// when Secret is empty. Useful for the canonical config-file shape:
	//
	//   - type: hmac
	//     secret_env: SUPABASE_JWT_SECRET
	SecretEnv string

	// Issuer, when set, is enforced via jwt.WithIssuer.
	Issuer string

	// Audience, when set, is enforced via jwt.WithAudience.
	Audience string

	// Resolver, when non-nil, is given the raw JWT payload so projects can
	// project provider-specific shapes onto [Claims] (and into Claims.Raw).
	// Nil means use the built-in shape extraction.
	Resolver UserResolver
}

HMACValidatorConfig configures an HMACValidator.

type InterceptorOptions

type InterceptorOptions struct {
	// SkipMethods overrides Config.SkipMethods when non-nil.
	SkipMethods []string

	// AllowDevMode, when true, skips real authentication and injects
	// DevClaims when no Authorization header is present. Only honour this
	// in non-production builds; the constructor does not gate on env.
	AllowDevMode bool

	// DevClaims is injected when AllowDevMode is true and the request has
	// no credentials. Defaults to a non-nil empty *Claims.
	DevClaims *Claims
}

InterceptorOptions configures Validator.Interceptor behaviour beyond the fields already on Config.

type JWKSValidator

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

JWKSValidator validates JWTs whose signing key comes from a JWKS endpoint (or any other jwt.Keyfunc source — the pkg/auth library deliberately does not depend on a JWKS client implementation; the jwt-auth pack supplies one).

Construct via NewJWKSValidator with a pre-built jwt.Keyfunc (e.g. one returned by github.com/MicahParks/keyfunc/v3.NewDefaultCtx).

func NewJWKSValidator

func NewJWKSValidator(cfg JWKSValidatorConfig) (*JWKSValidator, error)

NewJWKSValidator constructs a validator that delegates key resolution to cfg.KeyFunc (typically a JWKS-backed implementation).

func (*JWKSValidator) ValidateToken

func (v *JWKSValidator) ValidateToken(tokenString string) (*Claims, error)

ValidateToken implements TokenValidator.

type JWKSValidatorConfig

type JWKSValidatorConfig struct {
	// SigningMethod is the expected JWT alg. Defaults to "RS256".
	SigningMethod string

	// KeyFunc resolves the signing key for the token. Required.
	KeyFunc jwt.Keyfunc

	// Issuer, when set, is enforced via jwt.WithIssuer.
	Issuer string

	// Audience, when set, is enforced via jwt.WithAudience.
	Audience string

	// Resolver, when non-nil, projects the raw JWT payload onto [Claims].
	Resolver UserResolver
}

JWKSValidatorConfig configures a JWKSValidator.

type JWTConfig

type JWTConfig struct {
	// SigningMethod is the expected JWT alg value (e.g. "HS256", "RS256").
	// Defaults to "RS256" when empty.
	SigningMethod string

	// Issuer, when set, is enforced via jwt.WithIssuer.
	Issuer string

	// Audience, when set, is enforced via jwt.WithAudience.
	Audience string

	// JWKSURL, when set, signals JWKS-based key resolution. The current
	// implementation does not auto-fetch JWKS (matching the legacy template
	// behaviour) — callers that need JWKS should populate Secret with a
	// fetched key or supply a KeyFunc.
	JWKSURL string

	// Secret is the symmetric secret (HS*) or PEM-encoded public key
	// (RS*/ES*). When empty the validator falls back to os.Getenv("JWT_SECRET").
	Secret string

	// KeyFunc, when non-nil, fully overrides key resolution. Useful for tests
	// and for callers wiring custom JWKS clients.
	KeyFunc jwt.Keyfunc
}

JWTConfig holds JWT-specific settings.

func (JWTConfig) EffectiveSigningMethod

func (j JWTConfig) EffectiveSigningMethod() string

EffectiveSigningMethod returns SigningMethod or the "RS256" default.

type KeyValidator

type KeyValidator interface {
	ValidateKey(ctx context.Context, key string) (*Claims, error)
}

KeyValidator validates an API key and returns the associated claims. Implementations typically look up the key in a database or cache.

type MultiValidator

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

MultiValidator tries each underlying validator in order and returns the first set of claims that parses cleanly.

When every validator rejects the token, MultiValidator returns the LAST validator's error (the chain typically ends with the strictest validator, so its error is the most informative). Use NewMultiValidator to construct.

func NewMultiValidator

func NewMultiValidator(validators ...TokenValidator) (*MultiValidator, error)

NewMultiValidator constructs an ordered fallback chain. At least one underlying validator is required.

func (*MultiValidator) ValidateToken

func (m *MultiValidator) ValidateToken(tokenString string) (*Claims, error)

ValidateToken implements TokenValidator by trying each underlying validator in order.

type TokenValidator

type TokenValidator interface {
	// ValidateToken parses tokenString and returns the parsed Claims. The
	// returned error must be non-nil iff the token is invalid for THIS
	// validator. MultiValidator uses error to decide whether to try the
	// next validator in the chain.
	ValidateToken(tokenString string) (*Claims, error)
}

TokenValidator validates a single bearer JWT and returns parsed Claims.

Implementations are expected to be safe for concurrent use. Each Validator represents one issuer/key configuration (e.g. one HMAC secret, one JWKS endpoint); compose multiple via MultiValidator when a service needs to accept tokens from more than one issuer (typical during an auth-provider migration — Supabase HMAC tokens alongside Auth0 JWKS tokens).

type UserResolver

type UserResolver interface {
	// Resolve receives the full decoded JWT payload (as a map, the
	// shape jwt-go hands us) and must return a populated [Claims]. The
	// raw payload should typically be copied onto Claims.Raw so
	// downstream callers can still inspect provider-specific fields.
	Resolve(rawClaims map[string]any) (*Claims, error)
}

UserResolver is an optional hook that translates a raw decoded JWT payload into Claims. Projects implement it when they need to consume provider-specific claim shapes (e.g. Supabase user_metadata, Auth0 app_metadata, Clerk org_role/org_permissions) without forking the Claims type.

The resolver is wired via Config.UserResolver; when nil the validator falls back to the built-in shape extraction (sub, email, org_id, role, roles) and still populates Claims.Raw with the full payload.

type Validator

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

Validator authenticates Connect RPC requests against a configured provider. Construct with NewValidator; reuse a single Validator per service.

func NewValidator

func NewValidator(cfg Config) (*Validator, error)

NewValidator returns a Validator wired for cfg.Provider.

It returns an error when Provider is unrecognized. The "none" provider is allowed and produces a Validator whose Interceptor is a no-op (useful for tests and for projects that haven't enabled auth yet).

func (*Validator) AuthenticateHeaders

func (v *Validator) AuthenticateHeaders(ctx context.Context, headers http.Header, opts InterceptorOptions) (*Claims, error)

AuthenticateHeaders authenticates a request given its headers and returns the parsed claims (or an error). It is the testable core of Validator.Interceptor — the interceptor wraps this with the procedure-skip logic and context plumbing.

func (*Validator) Close

func (v *Validator) Close() error

Close releases any resources held by the Validator. Currently a no-op; kept for forward compatibility with JWKS-cache implementations.

func (*Validator) ConnectInterceptor

func (v *Validator) ConnectInterceptor(opts InterceptorOptions, withClaims func(context.Context, *Claims) context.Context) connect.Interceptor

ConnectInterceptor returns a full connect.Interceptor (unary + streaming handler) that authenticates each request and stores the resulting claims on ctx via withClaims.

Use this in preference to Validator.Interceptor for any new wiring — connect-go silently bypasses connect.UnaryInterceptorFunc for streaming RPCs, which would otherwise leave streaming endpoints unauthenticated.

Streaming clients are pass-through: the auth interceptor is server-side.

func (*Validator) Interceptor

Interceptor returns a unary-only Connect interceptor that authenticates each request and stores the resulting claims in the context using the supplied claims-context helper.

withClaims is the function the user's pkg/middleware exposes for putting claims into context (typically middleware.ContextWithClaims).

This entrypoint is kept for backwards compatibility with projects that wired auth as a connect.UnaryInterceptorFunc. New code should prefer Validator.ConnectInterceptor, which also covers streaming RPCs.

func (*Validator) IsUnauthenticatedProcedure

func (v *Validator) IsUnauthenticatedProcedure(procedure string, skipMethods []string) bool

IsUnauthenticatedProcedure reports whether procedure should bypass auth. This includes the built-in /Health/ skip plus any explicit skip list.

func (*Validator) Provider

func (v *Validator) Provider() string

Provider returns the configured provider name.

func (*Validator) SetKeyValidator

func (v *Validator) SetKeyValidator(kv KeyValidator)

SetKeyValidator replaces the configured KeyValidator. Useful when the validator is constructed before the storage backend is ready.

func (*Validator) Validate

func (v *Validator) Validate(token string) (*Claims, error)

Validate authenticates a single bearer JWT and returns the parsed claims. Useful outside the interceptor (e.g. webhook auth, CLI tooling).

Jump to

Keyboard shortcuts

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