Documentation
¶
Overview ¶
Package edgeauth provides middleware that trusts headers set by the Cloudflare Edge Router after Auth0 JWT validation.
The Cloudflare Edge Router (kombify-Gateway/cloudflare-edge):
- Validates the incoming Bearer JWT against Auth0 JWKS (RS256).
- Strips any client-injected x-user-*, x-org-*, and x-kombify-edge-* headers.
- Injects x-user-*, x-org-*, x-kombify-edge-auth, route context, and HMAC signature headers from the validated JWT claims or service identity.
In production, configure EDGE_AUTH_SECRET so the origin verifies that the downstream identity headers were set by the edge after real JWT validation.
New code must use identity.FromContext() for the canonical identity. The legacy kong package has been removed (Kong decommissioned 2026-04-17).
Self-hosted deployments (StackKits, Simulate CLI, SpeechKit) do not go through the CF Edge Router. They should set Enabled=false and handle auth locally.
Index ¶
- Constants
- Variables
- func BuildDecisionSignaturePayload(...) string
- func FlagsToContext(ctx context.Context, fs FlagSet) context.Context
- func IsEdgeAuthenticated(r *http.Request) bool
- func Middleware(cfg Config) func(next http.Handler) http.Handler
- func SignDecisionHeaders(input DecisionSignInput) (http.Header, error)
- func SignFlagHeaders(secret, keyID string, flags map[string]bool, budgets map[string]any, ...) (http.Header, error)
- type CloudRuntimeCredits
- type Config
- type DecisionBinding
- type DecisionSignInput
- type DecisionVerifyConfig
- type FlagSet
- type ManagedServerCredit
- type ManagedServerCreditMode
Constants ¶
const ( HeaderEdgeAuth = "X-Kombify-Edge-Auth" EdgeAuthValueJWT = "auth0-jwt" EdgeAuthValueAPIKey = "api-key" )
HeaderEdgeAuth is the edge identity marker set by the CF Edge Router after successful edge validation. Marker presence alone is not authorization proof; middleware must also verify the signed edge envelope.
const ( HeaderUserID = "X-User-ID" HeaderOrgID = "X-Org-ID" HeaderUserEmail = "X-User-Email" HeaderUserRoles = "X-User-Roles" HeaderUserTier = "X-User-Tier" HeaderUserScope = "X-User-Scope" HeaderRequestID = "X-Request-ID" HeaderEdgeService = "X-Kombify-Edge-Service" HeaderPublicPrefix = "X-Kombify-Public-Prefix" HeaderEdgeSignature = "X-Kombify-Edge-Signature" HeaderEdgeTimestamp = "X-Kombify-Edge-Timestamp" HeaderEdgeNonce = "X-Kombify-Edge-Nonce" HeaderEdgeSignedPath = "X-Kombify-Edge-Signed-Path" HeaderEdgeKeyID = "X-Kombify-Edge-Key-ID" // HeaderEntitlements and HeaderKnowledgeTier are forwarded from the edge and // consumed by AI-Platform access policy / knowledge tiering. They are bound by // the v2 edge signature so they cannot be forged downstream. HeaderEntitlements = "X-Kombify-Entitlements" HeaderKnowledgeTier = "X-Kombify-Knowledge-Tier" )
Headers forwarded from the edge (same names as Kong used).
const ( // HeaderFlags is a JSON object {"<dotted.key>": bool, ...}. HeaderFlags = "X-Kombify-Flags" // HeaderBudgets is a JSON object {"<dotted.key>": {<budget object>}, ...}. HeaderBudgets = "X-Kombify-Budgets" // HeaderFlagsSignature is "v1=<base64url-hmac>" over the flags payload. HeaderFlagsSignature = "X-Kombify-Flags-Signature" // HeaderFlagsTimestamp is the unix-seconds timestamp bound into the signature. HeaderFlagsTimestamp = "X-Kombify-Flags-Timestamp" // HeaderFlagsKeyID identifies which EDGE_AUTH secret signed the payload. HeaderFlagsKeyID = "X-Kombify-Flags-Key-ID" )
Signed feature-flag / token-budget delivery headers.
ENTITLEMENTS-ARCHITECTURE §8.1 removes the feature_flags claim array from the session JWT. Instead the Cloudflare Edge Router evaluates Flagship per request (sub-ms) and forwards the result to the origin as signed per-request headers. This file is the origin-side reference verifier and the test/edge-side signer.
The flag headers carry their OWN signature (HeaderFlagsSignature), separate from the identity-header signature in edgeauth.go, so the two evolve and verify independently. Both reuse EDGE_AUTH_SECRET[_NEXT] and the same HMAC.
Variables ¶
var ( ErrCloudRuntimeCreditsInvalid = errors.New("edgeauth: cloud runtime credits invalid") ErrVerifiedDecisionAuthorityAbsent = errors.New("edgeauth: verified decision authority absent") )
Functions ¶
func BuildDecisionSignaturePayload ¶
func BuildDecisionSignaturePayload( keyID, method, signedPath, audience, publicPrefix, subjectID, tenantID, requestID, edgeKeyID, timestamp, nonce, edgeSignature, flagsJSON, budgetsJSON string, ) string
BuildDecisionSignaturePayload is the canonical v2 flags/budgets payload. Its field order is a cross-language wire contract shared with kombify-Gateway.
func FlagsToContext ¶
FlagsToContext stores a verified FlagSet for downstream handlers.
func IsEdgeAuthenticated ¶
IsEdgeAuthenticated reports whether the request carries a supported Cloudflare Edge Router identity marker. It does not verify the signature and must not be used as an authorization decision.
func Middleware ¶
Middleware returns an http.Handler middleware that:
- Skips if Enabled=false.
- Requires a supported X-Kombify-Edge-Auth marker value.
- Verifies the signed edge envelope fail-closed.
- On success: extracts identity, stores in context via identity.NewContext.
- On missing header: rejects (401) if RequireEdgeAuth=true, else passes through.
Use identity.FromContext(ctx) to retrieve the identity in handlers.
func SignDecisionHeaders ¶
func SignDecisionHeaders(input DecisionSignInput) (http.Header, error)
SignDecisionHeaders signs a request-bound v2 decision. It does not mint the Edge identity envelope; callers must pass fields from the exact envelope already created for the same upstream request.
func SignFlagHeaders ¶
func SignFlagHeaders(secret, keyID string, flags map[string]bool, budgets map[string]any, ts time.Time) (http.Header, error)
SignFlagHeaders produces the signed flag-delivery headers. Edge/runtime and tests use this; secret is an EDGE_AUTH secret, keyID its identifier. Map keys are marshalled in sorted order by encoding/json, so signing is deterministic.
Types ¶
type CloudRuntimeCredits ¶
type CloudRuntimeCredits struct {
ManagedServers ManagedServerCredit
}
CloudRuntimeCredits is the closed Cloud-owned runtime budget projection.
func ParseCloudRuntimeCredits ¶
func ParseCloudRuntimeCredits(raw json.RawMessage) (CloudRuntimeCredits, error)
ParseCloudRuntimeCredits parses the only supported budget variants: {"managed_servers":{"mode":"limited","limit":N}} or {"managed_servers":{"mode":"unlimited"}}. Unknown fields, nulls, fractional/zero limits, and a limit on unlimited mode fail closed.
type Config ¶
type Config struct {
// Enabled controls whether the middleware is active.
// Defaults to false so self-hosted (StackKits, Simulate, SpeechKit) are safe.
Enabled bool
// RequireEdgeAuth forces a 401 when the X-Kombify-Edge-Auth header is
// absent or has an unexpected value. Set to true for SaaS-only services.
// When false, requests without the header proceed without identity context.
RequireEdgeAuth bool
// EdgeAuthSecret verifies HMAC-signed identity headers from the Cloudflare
// Edge Router. When empty, EDGE_AUTH_SECRET is read from the environment.
EdgeAuthSecret string
// EdgeAuthNextSecret is accepted during dual-key rotation. When empty,
// EDGE_AUTH_SECRET_NEXT is read from the environment.
EdgeAuthNextSecret string
// EdgeAuthKeyID identifies EdgeAuthSecret in the signed envelope. When
// empty, EDGE_AUTH_KEY_ID is read from the environment, then "primary".
EdgeAuthKeyID string
// EdgeAuthNextKeyID identifies EdgeAuthNextSecret in the signed envelope.
// When empty, EDGE_AUTH_KEY_ID_NEXT is read from the environment, then "next".
EdgeAuthNextKeyID string
// RequireSignature is retained for source compatibility. Edge-authenticated
// requests and unsigned identity headers are always verified fail-closed.
RequireSignature bool
// SignatureWindow is the maximum allowed skew for X-Kombify-Edge-Timestamp.
// Defaults to 5 minutes.
SignatureWindow time.Duration
}
Config configures the edge auth middleware.
type DecisionBinding ¶
type DecisionBinding struct {
Version string
KeyID string
SubjectID string
TenantID string
Audience string
PublicPrefix string
Method string
SignedPath string
RequestID string
EdgeKeyID string
Nonce string
IssuedAt time.Time
}
DecisionBinding is the immutable provenance of a request-bound v2 flag and budget decision. Origins must compare SubjectID and TenantID with their authenticated resource operation before treating a budget as authority.
type DecisionSignInput ¶
type DecisionSignInput struct {
Secret string
KeyID string
Method string
SignedPath string
Audience string
PublicPrefix string
SubjectID string
TenantID string
RequestID string
EdgeKeyID string
EdgeTimestamp string
EdgeNonce string
EdgeSignature string
Flags map[string]bool
Budgets map[string]any
}
DecisionSignInput binds one flags/budgets decision to the already minted Edge identity envelope and exact origin request. EdgeTimestamp, EdgeNonce, EdgeKeyID, and EdgeSignature must be copied from that same identity envelope.
type DecisionVerifyConfig ¶
type DecisionVerifyConfig struct {
PrimarySecret string
NextSecret string
PrimaryKeyID string
NextKeyID string
ExpectedAudience string
ExpectedPublicPrefix string
SignatureWindow time.Duration
Now func() time.Time
// IdentityConfig verifies the exact Edge identity envelope whose
// signature/timestamp/nonce are bound into the decision signature. It must
// use the Edge identity keys, which may differ from the decision keys.
IdentityConfig Config
}
DecisionVerifyConfig fixes the trusted signing keys and intended origin audience. ExpectedAudience is mandatory; ExpectedPublicPrefix is optional and should be set when one origin serves multiple public route prefixes.
type FlagSet ¶
type FlagSet struct {
Flags map[string]bool
Budgets map[string]json.RawMessage
// contains filtered or unexported fields
}
FlagSet is the verified set of per-request flags and budgets from the edge.
func FlagsFromContext ¶
FlagsFromContext retrieves a FlagSet previously stored by FlagsToContext.
func VerifyDecisionHeaders ¶
func VerifyDecisionHeaders(r *http.Request, cfg DecisionVerifyConfig) (FlagSet, error)
VerifyDecisionHeaders verifies a request-bound v2 flags/budgets decision. It rejects detached v1 headers and binds the decision to subject, tenant, intended audience, method/path, request id, and the exact Edge identity timestamp/nonce/signature. Exact HTTP retries remain the origin's durable idempotency responsibility; changing any bound field invalidates the HMAC.
func VerifyFlagHeaders ¶
VerifyFlagHeaders verifies and parses the signed flag headers on r. It is fail-closed: any missing header, bad signature, or stale timestamp yields an error and an empty FlagSet. Reuses the EDGE_AUTH secrets/window from Config.
func (FlagSet) Bool ¶
Bool returns the flag value, or def if absent (fail-closed: callers pass false for entitlement flags).
func (FlagSet) VerifiedBudget ¶
func (f FlagSet) VerifiedBudget(key string) (json.RawMessage, DecisionBinding, bool)
VerifiedBudget returns a detached copy of one budget together with its request binding. It fails closed if application code changed any flag or budget after verification.
func (FlagSet) VerifiedCloudRuntimeCredits ¶
func (f FlagSet) VerifiedCloudRuntimeCredits() (CloudRuntimeCredits, DecisionBinding, error)
VerifiedCloudRuntimeCredits atomically requires intact request-bound provenance, the canonical budget key, and the closed runtime-credit schema. Cost-bearing origins should use this accessor instead of reading Budgets directly.
func (FlagSet) VerifiedDecisionBinding ¶
func (f FlagSet) VerifiedDecisionBinding() (DecisionBinding, bool)
VerifiedDecisionBinding returns request-bound commercial-decision provenance only when FlagSet came from VerifyDecisionHeaders. A FlagSet assembled by application code or restored from a job payload has no such provenance and must fail closed for cost-bearing authorization.
type ManagedServerCredit ¶
type ManagedServerCredit struct {
Mode ManagedServerCreditMode
Limit int
}
ManagedServerCredit is the validated managed-server portion of cloud.runtime.credits. Limit is zero only for explicit unlimited mode.
type ManagedServerCreditMode ¶
type ManagedServerCreditMode string
ManagedServerCreditMode is the closed managed-server capacity mode.
const ( // CloudRuntimeCreditsBudgetName is the Cloud-owned commercial capacity // decision delivered in the request-bound Edge decision envelope. CloudRuntimeCreditsBudgetName = "cloud.runtime.credits" // #nosec G101 -- public product budget name, not credential material. // ManagedServerCreditModeLimited carries a positive hard-accounting limit. ManagedServerCreditModeLimited ManagedServerCreditMode = "limited" // ManagedServerCreditModeUnlimited explicitly removes the managed-server // ceiling. Absence or zero never implies unlimited. ManagedServerCreditModeUnlimited ManagedServerCreditMode = "unlimited" )