Documentation
¶
Overview ¶
Package securex — OIDC asymmetric verifier.
VerifyJWTWithJWKS is the resource-server counterpart to signerx.SignJWT. It is the runtime piece that closes the GENERATOR_BUGS.md "OIDC capability gap" — see Recommended generator work step (1) "Port/adapt htpx/oidc.go JWKS verification into the securex verifier". Specifically:
- Accepts asymmetric algorithms only (RS256/384/512, ES256/384/512, PS256/384/512). The `none` algorithm and every HMAC family are rejected unconditionally — RFC 7518 alg confusion is the primary practical OIDC attack class and the verifier MUST refuse to even attempt HMAC verification under an asymmetric profile.
- Honors the same Require{Expiry,Issuer,Audience} options that VerifyJWTWithOptions does, so OIDC profiles get strict-claim enforcement by default.
- Delegates signature verification to jwx v2's jwt.Parse, which is the same library that signerx.SignJWT signs with.
Index ¶
- Constants
- Variables
- func APIKeyAuth(r *http.Request, secret []byte, window time.Duration, seen *sync.Map) error
- func APIKeyAuthHTTP(w http.ResponseWriter, r *http.Request, secret []byte, window time.Duration, ...) bool
- func APIKeyAuthHTTPWithLimit(w http.ResponseWriter, r *http.Request, secret []byte, window time.Duration, ...) bool
- func APIKeyAuthWithLimit(r *http.Request, secret []byte, window time.Duration, seen *sync.Map, ...) error
- func AllowInsecureDev(r *http.Request, debugMode bool) bool
- func AllowInsecureDevAtStartup(debugMode bool) bool
- func ContextWithClaims(ctx context.Context, claims *Claims) context.Context
- func EvalComposite(r *http.Request, groups [][]Gate) (*http.Request, error)
- func ExtractCookieToken(r *http.Request, cookieName string) (string, error)
- func NewTestVerifier() func(*http.Request) error
- func RequestWithAuth(r *http.Request) *http.Request
- func RequestWithAuthPolicy(r *http.Request, policy RequestAuthPolicy) *http.Request
- func RequireABAC(secret []byte, policies ...ABACPolicy) func(http.Handler) http.Handler
- func RequireABACWithOptions(secret []byte, opts JWTVerifyOptions, policies ...ABACPolicy) func(http.Handler) http.Handler
- func RequireOwnership(secret []byte, extract OwnerExtractor, next http.Handler) http.Handler
- func RequireOwnershipWithOptions(secret []byte, opts JWTVerifyOptions, extract OwnerExtractor, ...) http.Handler
- func RequireRole(minimum Role, secret []byte, next http.Handler) http.Handler
- func RequireRoleWithOptions(minimum Role, secret []byte, opts JWTVerifyOptions, next http.Handler) http.Handler
- func SetRateHeaders(w http.ResponseWriter, limit, remaining int, reset time.Time)
- func SetRoleHierarchy(names []string)
- func SignAPIKeyRequest(r *http.Request, secret []byte, now time.Time) error
- func SignAPIKeyRequestWithValues(r *http.Request, secret []byte, nonce, ts string) error
- func SignWebhookRequest(r *http.Request, secret []byte, alg WebhookAlg, now time.Time) error
- func StartNonceGC(ctx context.Context, seen *sync.Map, interval time.Duration) chan struct{}
- func StartNonceGCLegacy(seen *sync.Map, interval time.Duration) chan struct{}deprecated
- func VerifyJWT(token string, secret []byte) error
- func VerifyJWTAllowNoExpiry(token string, secret []byte) error
- func VerifyJWTWithJWKS(_ context.Context, token string, keys jwk.Set, opts JWKSVerifyOptions) (jwt.Token, error)
- func VerifyJWTWithOptions(token string, secret []byte, opts JWTVerifyOptions) error
- func VerifyMTLS(r *http.Request, policy MTLSPolicy) error
- func VerifyWebhook(r *http.Request, secret []byte, pol WebhookPolicy) error
- func WebhookReplayStorePtr() *sync.Map
- func WriteForbidden(w http.ResponseWriter, internalErr error)
- func WriteUnauthorized(w http.ResponseWriter, internalErr error)
- type ABACPolicy
- type ABACPolicyFunc
- type AuthSource
- type Bucket
- type BucketSnapshot
- type CertPolicyVerifier
- type CertPolicyVerifierFunc
- type Claims
- type Gate
- type JWKSVerifyOptions
- type JWTVerifyOptions
- type MTLSPolicy
- type MTLSRuntime
- type OwnerExtractor
- type RequestAuthPolicy
- type RevocationChecker
- type Role
- type WebhookAlg
- type WebhookPolicy
Constants ¶
const DevEnvVar = "APIC_ENV"
DevEnvVar names the explicit-environment signal (A-03). The insecure-dev bypass refuses to engage unless this is set to "development" (case insensitive), so that debug mode alone — which is gin's DEFAULT and ships by omission unless GIN_MODE=release — can never silently enable it. Operators must make a positive development assertion.
const GenericForbiddenBody = `{"error":"forbidden"}`
GenericForbiddenBody is the exact response body that securex writes on every 403. Sibling of GenericUnauthorizedBody — same rationale, same lock-the-literal contract for downstream regression tests. Pentest follow-up A-005 (2026-05-24, LOW): the F-CR-004 closure scrubbed the 401 surface but the 403 paths in RequireRole / RequireABAC and the generator template's role/scope/attribute branches still rendered `securex: insufficient role` verbatim, leaving the same fingerprinting surface for any authenticated-but-under-privileged caller. This constant + WriteForbidden close that gap.
GenericUnauthorizedBody is the exact response body that securex writes on every 401. It deliberately omits the `securex:` framework prefix so an unauthenticated caller cannot fingerprint the auth-middleware library from the response payload alone. Pinned to the literal string so downstream regression tests (in apic and every consumer) can assert byte-for-byte equality.
Pentest finding F-CR-004 (2026-05-24, LOW). Prior to the fix, the generator's writeJSONError helper rendered ErrAuthFailed.Error() verbatim — i.e. `{"error":"securex: authentication failed"}` — which gave the pentester a free fingerprint for the auth middleware. The sentinel error values themselves still carry the `securex:` prefix so operator log triage stays natural; the prefix simply never reaches the wire on the 401 path.
const InsecureDevEnv = "APIC_INSECURE_DEV"
InsecureDevEnv is the canonical environment variable that opts into the development-only insecure bypass flow. Callers should never rely on this being set in production.
Variables ¶
var ( ErrInvalidConfig = errors.New("securex: invalid configuration") ErrAuthFailed = errors.New("securex: authentication failed") ErrRateLimited = errors.New("securex: rate limit exceeded") ErrInsufficientRole = errors.New("securex: insufficient role") // ErrOwnershipDenied is returned (server-side, in audit logs) when an // object-level authorization check fails — i.e. the authenticated // subject does not own the requested resource (SEC-0027 BOLA defense). // The wire response is a generic 403 via WriteForbidden; the sentinel // is logged only. ErrOwnershipDenied = errors.New("securex: object ownership denied") // ErrInsufficientAMR is returned (server-side, in audit logs) when a // request reaches a webauthnx.RequireAMR-gated route without the // required RFC 8176 Authentication Methods References claim — i.e. // the bearer token authenticated but the principal did not complete // the required step-up method (typical value: "webauthn"). The wire // surface is the generic 403 envelope so the required AMR is never // disclosed to the caller. GENWA-R8. ErrInsufficientAMR = errors.New("securex: insufficient amr (step-up required)") // ErrBodyTooLarge is returned by HMAC verification paths when a request // body exceeds the per-call limit. Streaming the body through sha256 // via io.LimitReader prevents an attacker from forcing the server to // buffer arbitrarily large payloads to be signed. (PERF-0012.) ErrBodyTooLarge = errors.New("securex: request body exceeds signing limit") // ErrAuthVerifierRequired is the bootstrap sentinel raised (wrapped in // a panic) by the generated RegisterGeneratedAPI / RegisterGeneratedWS // calls when the configuration declares at least one route with // auth: "jwt" but the caller supplied a nil JWT verifier in // APIOptions.AuthJWT / WSOptions.AuthJWT. Closing GAP-0076: previously // a missing verifier silently 401'd every request, which masked // configuration errors. The generator now panics at registration so // the misconfiguration cannot ship. Use pkg/securex.NewTestVerifier // in unit-test harnesses where you want to accept any non-empty // Bearer. ErrAuthVerifierRequired = errors.New("securex: AUTH_VERIFIER_REQUIRED: apic requires a JWT verifier when any route declares auth: \"jwt\" (set APIOptions.AuthJWT / WSOptions.AuthJWT; see pkg/securex.NewTestVerifier for the test escape hatch)") // ErrAuthAPIKeyRequired mirrors ErrAuthVerifierRequired for routes that // declare auth: "api_key". The generator refuses to register such a // route when APIOptions.Auth / WSOptions.Auth is nil. GAP-0076. ErrAuthAPIKeyRequired = errors.New("securex: AUTH_API_KEY_REQUIRED: apic requires an API-key verifier when any route declares auth: \"api_key\" (set APIOptions.Auth / WSOptions.Auth; see pkg/securex.NewTestVerifier for the test escape hatch)") // ErrAuthWebhookRequired is returned by the generated boot-time guard // when a route declares auth="webhook" but the consumer-supplied // APIOptions.AuthWebhook is nil. Fail-closed: refuse to start rather // than silently accept every webhook delivery. ErrAuthWebhookRequired = errors.New("securex: APIOptions.AuthWebhook is nil but at least one route declares auth=webhook") // ErrAuthMTLSRequired is the bootstrap sentinel raised (wrapped in a // panic) by the generated RegisterGeneratedAPI when the configuration // declares at least one route with auth: "mtls" but the caller supplied // a nil APIOptions.AuthMTLS hook. securex.VerifyMTLS deliberately never // enforces SupportedIssuers (only opts.AuthMTLS enforces issuer/identity // policy), so a nil hook fails OPEN — accepting any valid CA-chain client // cert regardless of issuer label. Mirrors ErrAuthVerifierRequired / // ErrAuthWebhookRequired: the generator panics at registration so the // misconfiguration cannot ship. ErrAuthMTLSRequired = errors.New("securex: AUTH_MTLS_REQUIRED: apic requires an mTLS verifier when any route declares auth: \"mtls\" (set APIOptions.AuthMTLS; the generated server installs server.RequireClientCert / server.IssuerCNVerifier as secure defaults — see WithAuthMTLS)") // ErrMTLSRuntimeRequired is raised (wrapped in a panic) by the generated // RegisterGeneratedAPI when the configuration declares at least one // route whose mtls block sets crl/ocsp/cac_piv/principal_mapping (L-51) // but the caller supplied a nil APIOptions.MTLSRuntimes map. Unlike // AuthMTLS, MTLSRuntimes is populated automatically by server.Serve // (server_lib.go.tmpl constructs it once at boot from the same config // that declared these fields) — this guard exists for hand-built // APIOptions callers (custom entrypoints, tests) that would otherwise // silently get NO CRL/OCSP/CAC-PIV enforcement despite the config // asking for it, reproducing the exact false-assurance bug L-51 fixes. ErrMTLSRuntimeRequired = errors.New("securex: MTLS_RUNTIME_REQUIRED: apic requires APIOptions.MTLSRuntimes when any route's mtls block declares crl/ocsp/cac_piv/principal_mapping (server.Serve wires this automatically; hand-built APIOptions must set it explicitly or drop those fields)") // ErrCSRFSignerRequired is raised (wrapped in a panic) by the generated // RegisterGeneratedAPI boot guard when the configuration enables // security.csrf but the caller supplied a nil APIOptions.CSRF signer. // Fail-closed: refuse to start rather than silently skip CSRF // enforcement on cookie-authenticated state-changing requests. F2. ErrCSRFSignerRequired = errors.New("securex: CSRF signer required") )
Sentinel errors for security and auth. Kept at package level per repo conventions. Messages use a lowercase, package-prefixed, diagnostic form so log output reads naturally; errors.Is callers should rely on pointer equality rather than substring matching of Error().
var ( ErrMTLSCertMissing = errors.New("securex: client certificate missing") ErrMTLSCertNotVerified = errors.New("securex: client certificate not verified") ErrMTLSCertEKUMissing = errors.New("securex: client cert lacks clientAuth EKU") // ErrMTLSNoIssuer is returned when a route's MTLSPolicy.Runtime configures // a CRL or OCSP RevocationChecker but the verified chain carries no // issuer certificate to check the leaf against (r.TLS.VerifiedChains[0] // has fewer than two entries). This should not happen once a listener // has actually verified the peer chain; it fails closed rather than // skipping the revocation check. ErrMTLSNoIssuer = errors.New("securex: mtls: no issuer certificate in verified chain for revocation check") )
Sentinel errors raised by VerifyMTLS. Callers should match with errors.Is rather than substring-matching the messages.
var AllowedAsymmetricAlgs = []jwa.SignatureAlgorithm{ jwa.RS256, jwa.RS384, jwa.RS512, jwa.ES256, jwa.ES384, jwa.ES512, jwa.PS256, jwa.PS384, jwa.PS512, jwa.EdDSA, }
AllowedAsymmetricAlgs is the default alg allowlist for OIDC asymmetric verification. Callers may override via JWKSVerifyOptions.AllowedAlgs; in either case `none` and every HMAC alg remain hard-rejected.
var ErrJWKSEmpty = errors.New("securex: jwks empty (no keys to verify against)")
ErrJWKSEmpty is returned when the supplied jwk.Set has no keys. A live IdP outage that drained the cache is a separable failure mode from signature mismatch and worth surfacing distinctly to callers.
var ErrJWTUnsupported = errors.New("securex: jwt algorithm or token unsupported")
ErrJWTUnsupported indicates VerifyJWT cannot handle the given algorithm/token.
var ErrNoCookieToken = errors.New("securex: missing auth cookie")
ErrNoCookieToken indicates the named auth cookie was absent or empty.
var ErrVerifierMisconfigured = errors.New("securex: verifier misconfigured (Require* set without expected value)")
ErrVerifierMisconfigured is returned when the verifier options are internally inconsistent in a way that would silently weaken authentication. A-05: RequireIssuer/RequireAudience with no corresponding expected value (Issuer / Audience left empty) is a presence-only check that accepts ANY issuer/audience — it looks like a security control but enforces nothing. Rather than honour that footgun, the verifier fails closed with this configuration error so the misconfiguration is surfaced loudly at the first verification attempt.
var ErrWebhookFailed = errors.New("securex: webhook verification failed")
ErrWebhookFailed is returned by VerifyWebhook for every cause of rejection EXCEPT the body-cap case (which returns ErrBodyTooLarge directly so callers can map it to HTTP 413). The handler must NEVER include the wrapped reason in the response body — 401 responses go through WriteUnauthorized which writes the generic envelope. Internal reasons surface only via obsx.LogAudit.
Functions ¶
func APIKeyAuth ¶
APIKeyAuth verifies Authorization: Bearer <hmac-hex> using a canonical request signature bound to method/path/body/nonce/timestamp and secret. It enforces a time window and replay protection via the seen map.
The request body is streamed through the hash via io.LimitReader with defaultHMACBodyLimit so that a single oversized request cannot exhaust server memory. See APIKeyAuthWithLimit for the explicit-limit variant.
func APIKeyAuthHTTP ¶
func APIKeyAuthHTTP(w http.ResponseWriter, r *http.Request, secret []byte, window time.Duration, seen *sync.Map) bool
APIKeyAuthHTTP wraps APIKeyAuth with HTTP error mapping for PERF #171 (sign-or-413). On success it returns true and the caller continues dispatch with r.Body re-staged (pooledBuffer). On failure it writes the appropriate status (413 for ErrBodyTooLarge, 401 for any other auth error) and a JSON envelope carrying error_code = <status> so observability pipelines can route alerts off a structured field instead of regex-matching on Message.
The 413 status is reserved for the body-size condition; any other authentication failure (bad signature, missing headers, expired timestamp, replayed nonce, etc.) collapses to 401 with the same envelope shape. This is "Option B" from PERF_GAP_ANALYSIS row 13.
func APIKeyAuthHTTPWithLimit ¶
func APIKeyAuthHTTPWithLimit(w http.ResponseWriter, r *http.Request, secret []byte, window time.Duration, seen *sync.Map, maxBodyBytes int64) bool
APIKeyAuthHTTPWithLimit is the explicit-limit variant of APIKeyAuthHTTP. A maxBodyBytes of 0 resolves to defaultHMACBodyLimit so that callers who have not migrated to an explicit cap still get the 1 MiB safe default. (PERF #171.)
func APIKeyAuthWithLimit ¶
func APIKeyAuthWithLimit(r *http.Request, secret []byte, window time.Duration, seen *sync.Map, maxBodyBytes int64) error
APIKeyAuthWithLimit is the explicit-limit variant of APIKeyAuth. The maxBodyBytes parameter caps the number of bytes that will be read from r.Body when computing the canonical signature. A value of 0 resolves to defaultHMACBodyLimit. A body strictly larger than maxBodyBytes is rejected with an error that wraps ErrBodyTooLarge; callers can inspect via errors.Is.
func AllowInsecureDev ¶
AllowInsecureDev reports whether the per-request INSECURE_DEV bypass should be honoured for this request. FOUR conditions must all hold (A-03):
- The TCP peer is a loopback address. The decision is made against r.RemoteAddr ONLY. X-Forwarded-For, X-Real-IP, and other proxy hints are intentionally ignored so that a reverse proxy cannot be tricked into forging a loopback origin.
- debugMode is true. The caller derives this from its own runtime mode (e.g. gin.Mode()==gin.DebugMode). Because debug is gin's DEFAULT (active unless GIN_MODE=release), debug mode alone is deliberately NOT sufficient — see condition 4.
- The environment variable APIC_INSECURE_DEV is set to a truthy value (1, true, yes, y, on - case insensitive).
- The environment variable APIC_ENV is set to "development" (case insensitive). This is the EXPLICIT positive development assertion that A-03 requires so the bypass cannot engage merely because debug mode shipped by omission.
If any condition fails, the bypass is denied. As a fail-closed guard, when APIC_INSECURE_DEV is truthy while the runtime is in release mode (GIN_MODE=release) the function PANICS — refusing to serve — rather than silently denying, because that combination is an unambiguous misconfiguration. A warning is logged at most once per process lifetime when the bypass first activates.
func AllowInsecureDevAtStartup ¶
AllowInsecureDevAtStartup is the setup-time variant of AllowInsecureDev. No HTTP request is available yet, so only debugMode and the environment variable are checked. This is the correct helper for listener wiring, CORS configuration, and OIDC-missing startup branches - places where the per-request loopback check cannot yet run.
A-03: like AllowInsecureDev it refuses to start (panics) when APIC_INSECURE_DEV is truthy under release mode (GIN_MODE=release). The explicit APIC_ENV=development assertion is deliberately NOT required here: this helper governs the plain-HTTP-listener / TLS bypass (SEC-0002), a separate control from the per-request JWT-validation bypass that A-03 names. The release-mode hard-fail is the relevant fail-closed guard for the startup path; the stricter development-assertion gate is enforced on the per-request AllowInsecureDev, where the auth bypass actually decides a request.
func ContextWithClaims ¶
ContextWithClaims returns a copy of ctx carrying the supplied Claims pointer. The generated per-route middleware first checks ClaimsFromContext(r.Context()) before re-parsing the bearer token, so a verifier (e.g. APIOptions.AuthJWT) that calls this once per request avoids the second base64-decode-and-Unmarshal per route. (S-NEW-3.)
Callers MUST only stash claims they have cryptographically verified (signature and expiry checked). Stashing claims from an unverified token is a security defect: downstream RBAC trusts these claims without re-checking the signature.
Passing a nil claims pointer stores a typed-nil so subsequent ClaimsFromContext(ctx) returns (nil, false) — this mirrors the "auth ran, but no claims attached" sentinel used by mcpx's ContextWithRoles when called with a nil slice.
Passing a nil ctx is safe and returns a context.Background()-derived value so downstream code can always derive a new ctx from the result.
func EvalComposite ¶
EvalComposite evaluates a disjunctive-normal-form auth expression: the outer slice is OR-of-groups, each inner slice is an AND of gates. It tries groups left-to-right and returns the request from the FIRST group whose every gate passes (short-circuit). Each non-empty group starts from a deep clone of r (via r.Clone) so that header/trailer map mutations AND context-carried claims written by a partially-passing-then-failing group cannot leak into the next group or the handler. Returns the original request and ErrAuthFailed when no group fully passes (fail-closed: empty/nil groups also fail).
Note: r.Clone shares the request Body (io.ReadCloser) across groups — auth gates read TLS state, headers, and cookies, not the body, so this is intentional and out of scope for this isolation guarantee.
func ExtractCookieToken ¶
ExtractCookieToken returns the JWT carried in the named HttpOnly auth cookie.
func NewTestVerifier ¶
NewTestVerifier returns an Auth-style request verifier that accepts any request carrying a non-empty `Authorization: Bearer <token>` header. Empty Authorization headers, malformed headers, and missing tokens all return ErrAuthFailed.
The returned function is intended for unit-test ergonomics ONLY. It does not validate the bearer token; any non-empty value passes. Production code MUST NOT wire this verifier -- the apic-generated registration calls explicitly panic with ErrAuthVerifierRequired if the caller supplies nil for a route declaring auth: "jwt", which makes the "forgot to wire a real verifier" failure mode loud at boot.
Example use in a test:
api.RegisterGeneratedAPI(mux, srv, api.APIOptions{
AuthJWT: securex.NewTestVerifier(),
Auth: securex.NewTestVerifier(),
})
The same function value satisfies both APIKey and JWT verifier slots because both contracts are `func(*http.Request) error`.
func RequestWithAuth ¶
RequestWithAuth applies the hardened default request auth policy.
func RequestWithAuthPolicy ¶
func RequestWithAuthPolicy(r *http.Request, policy RequestAuthPolicy) *http.Request
RequestWithAuthPolicy clones r and fills auth headers from the configured transport sources, including websocket subprotocol auth markers when browser clients cannot set custom headers during the upgrade.
func RequireABAC ¶
RequireABAC returns an http.Handler middleware that enforces ABAC policies. The JWT is verified using the secret, and all policies must evaluate to true. Fails with 401 Unauthorized for invalid tokens, and 403 Forbidden for policy failures.
func RequireABACWithOptions ¶
func RequireABACWithOptions(secret []byte, opts JWTVerifyOptions, policies ...ABACPolicy) func(http.Handler) http.Handler
RequireABACWithOptions is the iss/aud-aware variant of RequireABAC (A-01). The supplied JWTVerifyOptions bind the in-handler HS256 fallback to an expected issuer/audience. Zero-value opts reproduce the legacy behavior.
func RequireOwnership ¶
RequireOwnership returns middleware enforcing object-level authorization (SEC-0027 / OWASP API1:2023 BOLA): the authenticated subject (JWT "sub") must equal the resource-owner id returned by extract. This is the building block the generated by-id handlers (e.g. GET/PATCH/DELETE /v1/users/{userId}) cannot supply themselves, because only the consumer knows which claim maps to which resource. Wire it per owner-scoped route.
Secure-by-default: a missing/invalid token is 401; a subject that does not match the resource owner is a generic 403 (ErrOwnershipDenied, logged server-side only). If extract reports no owner-scoped resource, the request passes through unchanged so non-owned routes are unaffected. An empty JWT subject can never match and is therefore always denied.
func RequireOwnershipWithOptions ¶
func RequireOwnershipWithOptions(secret []byte, opts JWTVerifyOptions, extract OwnerExtractor, next http.Handler) http.Handler
RequireOwnershipWithOptions is the iss/aud-aware variant of RequireOwnership (A-01). The supplied JWTVerifyOptions bind the in-handler HS256 fallback to an expected issuer/audience. Zero-value opts reproduce the legacy behavior.
func RequireRole ¶
RequireRole returns an http.Handler middleware that enforces a minimum role. The JWT is read from "Authorization: Bearer <token>" and verified with secret. Requests whose highest role is below minimum receive 403 Forbidden. Requests with missing/invalid tokens receive 401 Unauthorized.
func RequireRoleWithOptions ¶
func RequireRoleWithOptions(minimum Role, secret []byte, opts JWTVerifyOptions, next http.Handler) http.Handler
RequireRoleWithOptions is the iss/aud-aware variant of RequireRole (A-01). The supplied JWTVerifyOptions are honoured on the in-handler HS256 fallback, binding the token to an expected issuer/audience so a validly-signed token minted for a different service is rejected (401) rather than accepted. When opts is the zero value this behaves exactly like the legacy RequireRole; set Issuer/Audience (and the matching Require* flags) to enforce binding.
func SetRateHeaders ¶
func SetRateHeaders(w http.ResponseWriter, limit, remaining int, reset time.Time)
SetRateHeaders sets basic rate-limit headers on the response.
func SetRoleHierarchy ¶
func SetRoleHierarchy(names []string)
SetRoleHierarchy replaces the RBAC role vocabulary used by RoleFromString, Role.String, and (via RoleFromString) Claims.HighestRole. names are ordered MOST-PRIVILEGED FIRST (index 0 = highest privilege); they are matched case-insensitively. This lets a generated server enforce a custom role set (e.g. platform_admin > admin > assessor > viewer) instead of the built-in user/manager/admin. Intended to be called exactly once from a generated init(); a nil/empty list is ignored (keeps the default). NOT safe to call concurrently with request handling.
func SignAPIKeyRequest ¶
SignAPIKeyRequest signs r using the same canonical request rules enforced by APIKeyAuth. It sets Authorization, X-Nonce, and X-Timestamp on the request.
func SignAPIKeyRequestWithValues ¶
SignAPIKeyRequestWithValues signs r with explicit nonce and timestamp values. This is useful for deterministic tests and for flows that need to precompute signed WebSocket auth material.
func SignWebhookRequest ¶
SignWebhookRequest stamps r with X-Webhook-Id, X-Timestamp, and the HMAC signature header so the request passes VerifyWebhook against the same secret. Use this from outbound delivery code and from tests; the receiver does NOT depend on this helper.
The id is a 16-byte random nonce (base64-url, no padding) — the same shape APIKeyAuth uses for X-Nonce so consumers see a uniform id format across both auth modes. now is supplied so tests can pin a deterministic timestamp; production callers pass time.Now().
func StartNonceGC ¶
StartNonceGC starts a GC loop to remove expired nonces stored by APIKeyAuth. N-15: context-first (ctx is the first parameter, matching the repo's documented context-first convention) and clamps a non-positive interval to defaultNonceGCInterval instead of letting time.NewTicker panic. gen.SafeLoop recovers a panic and restarts the loop body, but interval is a closed-over value that never changes across restarts, so an invalid interval previously spun forever in a panic/backoff/restart cycle rather than ever making progress. The returned channel keeps its existing contract (closing it stops the loop); ctx cancellation stops it too, whichever comes first.
func StartNonceGCLegacy
deprecated
added in
v0.17.0
StartNonceGCLegacy is a back-compat shim for callers written against the pre-N-15 two-argument StartNonceGC(seen, interval) signature (Q-3).
Deprecated: use StartNonceGC(ctx, seen, interval). N-15 added a leading ctx parameter to StartNonceGC so the GC loop honors caller cancellation (the repo's documented context-first convention); that changed StartNonceGC's signature without a compatibility path or a BREAKING CHANGE footer, and it shipped uncompilable example code in docs/WEBHOOK_MODE.md as a result. StartNonceGCLegacy runs the loop with context.Background() -- identical to StartNonceGC's pre-N-15 behavior, which never observed cancellation either -- so old call sites keep compiling and behaving exactly as before while new code uses StartNonceGC directly for ctx-aware shutdown.
func VerifyJWT ¶
VerifyJWT verifies a compact JWS (HS256) with the provided secret. Returns ErrJWTUnsupported if algorithm is not HS256 or token malformed.
SECURE DEFAULT (SEC-0028): VerifyJWT REQUIRES the "exp" claim. A token that omits "exp" never expires, which turns a single leaked bearer token into a permanent authorization-server compromise primitive. RFC 7519 §4.1.4 marks "exp" OPTIONAL, but a resource server MUST NOT accept a non-expiring access token. Callers that truly need the legacy accept-no-exp behavior must opt in explicitly via VerifyJWTAllowNoExpiry (or VerifyJWTWithOptions with RequireExpiry:false) and own that risk.
func VerifyJWTAllowNoExpiry ¶
VerifyJWTAllowNoExpiry is the explicit, clearly-named opt-out from the VerifyJWT secure default (SEC-0028). It verifies the HS256 signature and any present "exp"/iss/aud claims, but — unlike VerifyJWT — it ACCEPTS a token that omits "exp" entirely (a never-expiring token).
This exists only for narrow legacy/back-compat call sites that knowingly issue or accept non-expiring tokens. Prefer VerifyJWT (or VerifyJWTWithOptions with the appropriate Require* fields) for anything exposed to untrusted input. Using this on an internet-facing resource server is an authorization weakness.
func VerifyJWTWithJWKS ¶
func VerifyJWTWithJWKS(_ context.Context, token string, keys jwk.Set, opts JWKSVerifyOptions) (jwt.Token, error)
VerifyJWTWithJWKS parses and verifies a compact JWS token using the supplied jwk.Set. Returns the parsed jwt.Token on success, or ErrAuthFailed / ErrJWTUnsupported / ErrJWKSEmpty on failure.
The asymmetric-only contract is enforced before signature verification: the JWS header `alg` is parsed and compared against the allowlist; any `none` or HMAC alg returns ErrJWTUnsupported without touching keys or running the underlying jwt.Parse signature path.
func VerifyJWTWithOptions ¶
func VerifyJWTWithOptions(token string, secret []byte, opts JWTVerifyOptions) error
VerifyJWTWithOptions verifies a compact JWS (HS256) with the provided secret and enforces optional issuer/audience checks when configured.
func VerifyMTLS ¶
func VerifyMTLS(r *http.Request, policy MTLSPolicy) error
VerifyMTLS enforces a per-route mTLS policy against an HTTP request whose connection terminated on a TLS listener. Call after the listener has completed the TLS handshake but before any handler logic runs.
Checks, in order:
- r and r.TLS are non-nil. Defends against an accidentally plaintext reverse-proxy hop in front of the server stripping mTLS state.
- If policy.Required, at least one peer certificate is present.
- The peer cert chain has been verified by the listener (r.TLS.VerifiedChains is non-empty).
- If policy.EKUValidation, the leaf certificate carries the clientAuth (or ExtKeyUsageAny) Extended Key Usage.
- If policy.Runtime.CRL and/or policy.Runtime.OCSP is configured (L-51), the leaf is checked against the issuer's CRL/OCSP revocation state. A zero-value Runtime is a no-op here.
- If policy.Runtime.CertPolicy is configured (L-51), the leaf is verified against the CAC/PIV certificate-classification policy.
SupportedIssuers is intentionally NOT enforced. Callers that need issuer-label validation should layer their own verifier on top after VerifyMTLS returns nil; the apic-emitted handler invokes opts.AuthMTLS for exactly that purpose.
func VerifyWebhook ¶
func VerifyWebhook(r *http.Request, secret []byte, pol WebhookPolicy) error
VerifyWebhook checks r against pol using the supplied secret. Returns nil on success. Returns ErrBodyTooLarge (detectable via errors.Is) when the request body exceeds pol.MaxBodyBytes so callers can map it to HTTP 413 — mirrors APIKeyAuthWithLimit. Returns ErrWebhookFailed for every other rejection: empty secret, missing/malformed headers, non-allowlisted alg, expired timestamp, bad signature, replay.
The body is rewrapped on success so the user handler can re-read it; on failure the body may be partially consumed and the caller should treat r as untrusted.
secret is supplied per-request (not stored on pol) so the generator can resolve it from APIOptions.AuthWebhook keyed on pol.Name. Zero (nil/empty) secret causes verification to fail regardless of any other input.
func WebhookReplayStorePtr ¶
WebhookReplayStorePtr returns a pointer to the package-shared webhook replay store. Pass this to StartNonceGC at server start so expired entries (entries whose value time.Time is in the past) are reaped on the same cadence as the API-key nonce store. Task 10 of the webhook-receiver plan wires this from server.go.tmpl.
Returning a *sync.Map (not a copy) is intentional: StartNonceGC mutates the map in place to delete expired keys.
func WriteForbidden ¶
func WriteForbidden(w http.ResponseWriter, internalErr error)
WriteForbidden writes the package-standard 403 response: Content-Type application/json, status 403, body GenericForbiddenBody. The supplied internalErr (typically ErrInsufficientRole, or a wrapped scope / attribute reason) is logged at WARN level via log/slog so operators can still triage the underlying cause — it is intentionally NOT echoed into the response body.
Mirrors WriteUnauthorized in shape so the two helpers stay behaviour-symmetric for downstream regression tests that diff the 401 and 403 surfaces side-by-side.
All package-internal call sites that previously emitted `http.Error(w, ErrInsufficientRole.Error(), 403)` or `writeJSONError(w, securex.ErrInsufficientRole.Error(), 403)` MUST route through this helper to guarantee the A-005 closure across the generated server set.
func WriteUnauthorized ¶
func WriteUnauthorized(w http.ResponseWriter, internalErr error)
WriteUnauthorized writes the package-standard 401 response: Content-Type application/json, status 401, body GenericUnauthorizedBody. The supplied internalErr (typically one of the package-level Err* sentinels) is logged at WARN level via log/slog so operators can still triage the underlying cause from server logs — it is intentionally NOT echoed into the response body.
All package-internal call sites that previously emitted `http.Error(w, ErrAuthFailed.Error(), 401)` or `writeJSONError(w, securex.ErrAuthFailed.Error(), 401)` MUST route through this helper to guarantee the F-CR-004 closure across the generated server set.
Types ¶
type ABACPolicy ¶
type ABACPolicy interface {
// Evaluate returns true if the claims satisfy the policy.
Evaluate(c *Claims) bool
}
ABACPolicy defines an attribute evaluation policy.
func HasAttribute ¶
func HasAttribute(key string) ABACPolicy
HasAttribute returns a policy that evaluates to true if the attribute key exists.
func MatchAttribute ¶
func MatchAttribute(key, value string) ABACPolicy
MatchAttribute returns a policy that evaluates to true if the attribute exactly matches the given value.
type ABACPolicyFunc ¶
ABACPolicyFunc is a function type that implements ABACPolicy.
func (ABACPolicyFunc) Evaluate ¶
func (f ABACPolicyFunc) Evaluate(c *Claims) bool
Evaluate calls the underlying function.
type AuthSource ¶
type AuthSource int
AuthSource identifies how a request presented its credential. The CSRF middleware enforces only AuthSourceCookie (ambient browser credential); AuthSourceHeader (Bearer) is exempt because a cross-origin page cannot set the Authorization header.
const ( AuthSourceNone AuthSource = iota AuthSourceHeader // Authorization: Bearer … AuthSourceCookie // HttpOnly auth cookie AuthSourceQuery // ?access_token=… )
func AuthSourceForRequest ¶
func AuthSourceForRequest(r *http.Request, cookieName string) AuthSource
AuthSourceForRequest reports the credential transport, mirroring the extraction priority: Authorization header > auth cookie > ?access_token=.
type Bucket ¶
type Bucket = ratebucket.Bucket
Bucket provides a simple token bucket limiter for per-route/tool scoping. Bucket aliases the canonical token bucket. securex previously carried a byte-identical private copy of this algorithm (QG-046/059/PERF-0044); Allow/Snapshot/AllowAndSnapshot now come from the shared ratebucket type.
type BucketSnapshot ¶
type BucketSnapshot = ratebucket.Snap
BucketSnapshot is the value form of (limit, remaining, resetTime) returned by AllowAndSnapshot. Aliases ratebucket.Snap (fields Limit, Remaining, Reset).
type CertPolicyVerifier ¶ added in v0.17.0
type CertPolicyVerifier interface {
Verify(leaf *x509.Certificate) (principal, classification string, err error)
}
CertPolicyVerifier enforces certificate-classification policy (e.g. DOD CAC / federal PIV require_person / required_policy_oids / reject_unknown_classification) against a verified leaf certificate and resolves the configured mtls.principal_mapping to an identity string. A non-nil error fails the request closed. Concrete implementations live in pkg/securex/cacpiv (Adapter.Verify).
type CertPolicyVerifierFunc ¶ added in v0.17.0
type CertPolicyVerifierFunc func(leaf *x509.Certificate) (principal, classification string, err error)
CertPolicyVerifierFunc adapts a plain function to CertPolicyVerifier, mirroring the standard library's http.HandlerFunc idiom. The generated server uses this to wrap a boot-constructed cacpiv.Adapter (whose Verify method takes an extra mapping argument baked in via closure) without requiring pkg/securex to import pkg/securex/cacpiv.
func (CertPolicyVerifierFunc) Verify ¶ added in v0.17.0
func (f CertPolicyVerifierFunc) Verify(leaf *x509.Certificate) (string, string, error)
Verify implements CertPolicyVerifier by calling f.
type Claims ¶
type Claims struct {
Subject string `json:"sub"`
Roles []string `json:"roles"`
Scopes []string `json:"scopes"`
Attributes map[string]string `json:"attributes,omitempty"`
// Email is the standard `email` claim, surfaced for handler convenience
// (F4). Empty when the token omits it.
Email string `json:"email,omitempty"`
// Exp is the standard `exp` (expiry) claim as a Unix timestamp (seconds).
// Zero when absent. Signature/expiry are enforced by VerifyJWT, not here.
Exp int64 `json:"exp,omitempty"`
// AMR is the RFC 8176 Authentication Methods References list, e.g.
// ["webauthn"], ["pwd","otp"], ["mfa"]. Populated by ParseClaims
// from the `amr` JSON array claim when present; consumed by
// webauthnx.RequireAMR (GENWA-R8) to gate step-up routes. An empty
// or absent claim leaves the slice nil — RequireAMR treats nil as
// "no method asserted" and 403s any required-AMR gate.
AMR []string `json:"amr,omitempty"`
}
Claims holds the JWT payload fields relevant to RBAC and scope enforcement. Call ParseClaims only after the token signature has been verified.
func ClaimsFrom ¶
ClaimsFrom is the public, ergonomically-named accessor for the verified claims a JWT-verifying gate stashed in ctx (F4). Thin alias of ClaimsFromContext: returns (claims, true) when present, else (nil, false).
func ClaimsFromContext ¶
ClaimsFromContext returns the Claims pointer stashed by ContextWithClaims, or (nil, false) if no claims have been attached. The returned pointer is the same value passed to ContextWithClaims (not a copy); callers MUST treat it as read-only or risk torn reads across goroutines that share the request context. (S-NEW-3.)
func ParseClaims ¶
ParseClaims decodes the payload of a compact JWS token and returns the Claims. It does NOT verify the token signature; callers must call VerifyJWT first.
func VerifyJWTWithClaims ¶ added in v0.17.0
func VerifyJWTWithClaims(token string, secret []byte, opts JWTVerifyOptions) (*Claims, error)
VerifyJWTWithClaims verifies a compact JWS (HS256) exactly as VerifyJWTWithOptions does, and on success returns the *Claims decoded from the SAME payload segment the verifier already base64-decoded.
NP-05: the pre-existing call pattern was VerifyJWTWithOptions (decodes the payload into map[string]any to run the exp/nbf/iat/iss/aud checks) immediately followed by ParseClaims(token) (re-splits the compact JWS, re-base64-decodes the SAME payload segment, and re-unmarshals it into Claims) on every authenticated request. VerifyJWTWithClaims shares the split + signature verification + base64 payload decode with VerifyJWTWithOptions via verifyJWTPayload, and pays only ONE extra json.Unmarshal (into the typed Claims struct, which is not interchangeable with the map[string]any the claim checks need — Claims has no iss/aud/nbf fields) instead of re-doing the split and base64 decode as well.
Every check below is byte-for-byte the same as VerifyJWTWithOptions (verifyJWTPayload IS that check body) — this is a security-sensitive path and callers must be able to rely on identical accept/reject behavior between the two entry points.
func (*Claims) HasAMR ¶
HasAMR reports whether c.AMR contains the given method reference. RFC 8176 §1 defines the claim as a JSON array of case-sensitive strings; matching is exact (no case-folding) to stay faithful to the registry (https://www.iana.org/assignments/amr-values).
func (*Claims) HighestRole ¶
HighestRole returns the highest Role found in the claims' Roles list. Returns RoleUser if the list is empty or contains no recognised role names.
type Gate ¶
Gate is one composable authentication check used by EvalComposite. It returns the (possibly claim-augmented) request and nil on success, or the request and a non-nil error on failure. A Gate MUST stash verified claims (via ContextWithClaims on the returned request) ONLY when it succeeds — never on the failure path — so that a failed AND-group leaves no claim residue. A Gate's verifier MUST verify the token signature and expiry BEFORE stashing claims via ContextWithClaims; stashing claims for an unverified token is a security defect because downstream RBAC (RequireRole, authzFromClaims) trusts these claims without re-checking the signature.
func GateAPIKey ¶
GateAPIKey runs the API-key verifier.
func GateCookie ¶
GateCookie lifts a session JWT from the named cookie into the Authorization header (on a clone, so a failed OR-group cannot leak the header) and validates it with the shared JWT verifier.
func GateDeny ¶
func GateDeny() Gate
GateDeny always fails closed; emitted for an unrecognized DNF leaf.
func GateMTLS ¶
func GateMTLS(hook func(*http.Request, MTLSPolicy) error, policy MTLSPolicy) Gate
GateMTLS verifies client-cert presence/EKU via VerifyMTLS, then runs the optional issuer/CAC-PIV policy hook. A nil hook still requires a valid cert (fail-closed). cac/piv tokens map to GateMTLS; their issuer policy is enforced by the wired hook plus the route's MTLSPolicy.
type JWKSVerifyOptions ¶
type JWKSVerifyOptions struct {
// Issuer, when non-empty, is matched against the token's `iss` claim.
Issuer string
// Audience, when non-empty, is matched against the token's `aud`.
Audience string
// RequireExpiry / RequireIssuer / RequireAudience demand presence of
// the corresponding claim (independent of the string fields' value
// matching). OIDC profiles should set all three.
//
// A-05: RequireIssuer/RequireAudience are presence-only and would accept
// ANY value unless the matching Issuer/Audience field is also set. Setting
// RequireIssuer with an empty Issuer (or RequireAudience with an empty
// Audience) is a misconfiguration that VerifyJWTWithJWKS rejects with
// ErrVerifierMisconfigured rather than silently authorizing arbitrary
// issuers/audiences. RequireExpiry has no value field and is exempt.
RequireExpiry bool
RequireIssuer bool
RequireAudience bool
// AllowedAlgs, when non-empty, overrides AllowedAsymmetricAlgs.
// `none` and HMAC family algs are rejected regardless of override.
AllowedAlgs []jwa.SignatureAlgorithm
}
JWKSVerifyOptions configures VerifyJWTWithJWKS.
type JWTVerifyOptions ¶
type JWTVerifyOptions struct {
// Issuer is the expected "iss" claim value. Empty disables issuer-value
// comparison (see RequireIssuer for presence enforcement).
Issuer string
// Audience is the expected "aud" claim value. Empty disables
// audience-value comparison (see RequireAudience for presence enforcement).
Audience string
// RequireExpiry rejects any token that omits the "exp" claim.
// RFC 7519 §4.1.4 marks exp OPTIONAL, but OIDC resource servers
// MUST reject non-expiring access tokens (a never-expiring
// bearer token is an authorization-server compromise primitive).
RequireExpiry bool
// RequireIssuer rejects any token that omits the "iss" claim.
// Has no effect when Issuer == "" (no expected value to compare
// against). When both Issuer != "" and RequireIssuer == true,
// the claim must be present AND match.
RequireIssuer bool
// RequireAudience rejects any token that omits the "aud" claim.
// Has no effect when Audience == "". When both Audience != "" and
// RequireAudience == true, the claim must be present AND match.
RequireAudience bool
}
JWTVerifyOptions are optional constraints for issuer/audience claim checks.
The boolean Require* fields demand presence of the corresponding claim: a missing claim returns ErrAuthFailed regardless of the string fields' content. Use the Require* form for OIDC resource-server profiles where RFC 6749 / OpenID Connect Core mandate the claim. Without Require*, a missing claim is silently accepted (legacy backwards-compatible behavior — see GENERATOR_BUGS.md "OIDC capability gap" A-S2).
type MTLSPolicy ¶
type MTLSPolicy struct {
// Required, when true, demands a peer certificate be presented and
// verified. When false, requests that omit a client cert are allowed
// through; this matches the tls.VerifyClientCertIfGiven listener mode.
Required bool
// EKUValidation, when true, demands the leaf certificate carry the
// x509.ExtKeyUsageClientAuth extended key usage (or ExtKeyUsageAny).
EKUValidation bool
// SupportedIssuers is plumbed through for caller-side issuer-label
// policy (PIV/CAC/custom). VerifyMTLS does NOT enforce this slice;
// the mapping from label to certificate Subject/Issuer is deployment-
// specific and must be supplied via the opts.AuthMTLS hook.
SupportedIssuers []string
// Runtime carries the boot-constructed CRL/OCSP revocation checkers and
// CAC/PIV certificate-policy verifier for this route (L-51). The zero
// value (all nil fields) enforces nothing beyond the fields above,
// matching pre-L-51 behavior exactly.
Runtime MTLSRuntime
}
MTLSPolicy is the per-route mTLS verification policy emitted by the apic code generator and consumed by VerifyMTLS at request time. The shape mirrors the MTLSContract block in the apic config.
type MTLSRuntime ¶ added in v0.17.0
type MTLSRuntime struct {
// CRL, when non-nil, is consulted after EKU validation. A non-nil
// error (including ErrCertRevoked from a hard-fail checker) rejects
// the request. A checker constructed with AllowSoftFail:true instead
// returns nil on fetch failure, which is exactly the "warn and
// continue" behavior L-51 requires for allow_soft_fail:true.
CRL RevocationChecker
// OCSP mirrors CRL for OCSP-based revocation checks. When both CRL and
// OCSP are configured, CRL is checked first.
OCSP RevocationChecker
// CertPolicy, when non-nil, is invoked AFTER CRL/OCSP checks pass. It
// enforces cac_piv policy (require_person / required_policy_oids /
// reject_unknown_classification) and resolves principal_mapping. Its
// returned principal/classification are informational for callers that
// want them (e.g. via opts.AuthMTLS); VerifyMTLS itself only inspects
// the error.
CertPolicy CertPolicyVerifier
}
MTLSRuntime bundles the boot-constructed, per-route revocation checkers and CAC/PIV policy verifier that VerifyMTLS enforces IN ADDITION to Required/EKUValidation/SupportedIssuers (L-51). The zero value performs no additional checks, so embedding it in MTLSPolicy is fully backward compatible with the pre-L-51 policy shape.
CRLChecker/OCSPChecker hold response caches and make network calls, so the generated server constructs each route's MTLSRuntime exactly ONCE at boot (server_lib.go.tmpl) and shares the same instance across every request for that route -- never reconstruct one per request.
type OwnerExtractor ¶
OwnerExtractor pulls the resource-owner identifier out of an inbound request — typically a path parameter such as {userId}. It returns the owner id and true when one is present, or false when the request carries no owner-scoped resource (in which case RequireOwnership lets the request through to be handled by other authz layers). The extractor MUST NOT trust any value from the request body for the owner id; it should read the routed path/segment so it cannot be spoofed independently of the route.
type RequestAuthPolicy ¶
type RequestAuthPolicy struct {
// AllowAuthorizationHeader accepts a credential presented in the normal
// HTTP "Authorization" header (Bearer or Signature).
AllowAuthorizationHeader bool
// AllowSubprotocolBearer accepts a bearer token carried in the WebSocket
// Sec-WebSocket-Protocol subprotocol list (the browser WS API cannot set
// arbitrary headers, so the token rides the subprotocol negotiation).
AllowSubprotocolBearer bool
// AllowSubprotocolSignature accepts an HTTP-signature credential carried
// in the WebSocket Sec-WebSocket-Protocol subprotocol list.
AllowSubprotocolSignature bool
// AllowQueryBearer accepts a bearer token supplied as a query-string
// parameter. Off by default in the hardened policy: query strings leak
// into access logs, proxies, and Referer headers.
AllowQueryBearer bool
// AllowQuerySignature accepts an HTTP-signature credential supplied as a
// query-string parameter (same logging-exposure caveat as AllowQueryBearer).
AllowQuerySignature bool
}
RequestAuthPolicy controls which alternate auth transports are accepted when a request cannot set normal Authorization headers directly.
func DefaultRequestAuthPolicy ¶
func DefaultRequestAuthPolicy() RequestAuthPolicy
DefaultRequestAuthPolicy is the hardened default used by generated servers.
type RevocationChecker ¶ added in v0.17.0
type RevocationChecker interface {
Check(ctx context.Context, leaf, issuer *x509.Certificate) error
}
RevocationChecker checks whether a leaf certificate has been revoked by its issuer (CRL or OCSP). Concrete implementations live in pkg/securex/mtlsx (CRLChecker / OCSPChecker); MTLSPolicy.Runtime references only this interface so that this package never imports mtlsx (mtlsx already imports securex -- importing back would cycle). AllowSoftFail behavior (warn-and-continue vs hard-fail) is baked into the concrete checker at construction time, not re-decided here: L-51 requires allow_soft_fail:false to hard-fail and the checker itself is the single source of truth for that decision.
type Role ¶
type Role int
Role represents an RBAC privilege level. Higher values mean greater privilege.
func RoleFromString ¶
RoleFromString parses a case-insensitive role name into a Role value against the active role hierarchy (see SetRoleHierarchy). Returns ErrInvalidConfig for unknown role names.
type WebhookAlg ¶
type WebhookAlg string
WebhookAlg names the HMAC family the verifier accepts. The generator emits an allowlist literal per route; the verifier rejects every alg outside the allowlist, defending against downgrade attempts where a sender stamps `alg=md5` and we silently honour it.
const ( WebhookAlgSHA256 WebhookAlg = "sha256" WebhookAlgSHA512 WebhookAlg = "sha512" )
type WebhookPolicy ¶
type WebhookPolicy struct {
// Name is the operator-facing webhook name (e.g. "stripe", "github").
// Passed to APIOptions.AuthWebhook to fetch the secret and recorded
// in audit events.
Name string
// Algs is the HMAC family allowlist. Empty (nil/zero-length) means
// "sha256 only" so a config that forgets to set this still defaults
// to a safe value. The verifier compares case-insensitively against
// the value carried in the X-Signature-Alg header (or the default
// sha256 when absent).
Algs []WebhookAlg
// Window bounds the |now - timestamp| acceptable skew. A zero value
// is treated as 5*time.Minute by VerifyWebhook (Task 2). The
// 5-minute default matches the industry de-facto window for
// HMAC-signed webhooks (Stripe, GitHub, PagerDuty).
Window time.Duration
// MaxBodyBytes caps the body the verifier will hash. Zero resolves
// to defaultHMACBodyLimit (1 MiB) at codegen.
MaxBodyBytes int64
// SignatureHeader is the header the signature lives in. Defaults
// to "X-Signature" when empty. Generator emits the configured
// override (e.g. "X-Hub-Signature-256") verbatim.
SignatureHeader string
// TimestampHeader is the unix-seconds header. Defaults to
// "X-Timestamp" when empty.
TimestampHeader string
// IDHeader is the per-event id header used for replay protection.
// Defaults to "X-Webhook-Id" when empty. The (id, timestamp) tuple
// goes into the package-shared replay map so an attacker cannot
// re-deliver an intercepted POST.
IDHeader string
}
WebhookPolicy is the verification contract for a single webhook receiver route. The generator emits a literal of this struct directly into the per-route handler. Secret resolution is external: APIOptions.AuthWebhook(name) returns the HMAC key at request time, keyed on the WebhookPolicy.Name field below.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package cacpiv extends mtlsx.Principal with DOD CAC and federal PIV identity extraction per FIPS 201-3 and the X.509 Certificate Policy for the U.S. Federal PKI Common Policy Framework.
|
Package cacpiv extends mtlsx.Principal with DOD CAC and federal PIV identity extraction per FIPS 201-3 and the X.509 Certificate Policy for the U.S. Federal PKI Common Policy Framework. |
|
Package csrfx implements stateless, HMAC-signed, session-bound CSRF tokens using the signed double-submit pattern (OWASP).
|
Package csrfx implements stateless, HMAC-signed, session-bound CSRF tokens using the signed double-submit pattern (OWASP). |
|
Package fipsx exposes a tiny façade over crypto/fips140 so the rest of apic can gate behavior on FIPS 140-3 mode without importing crypto/fips140 directly (keeping the import boundary tight makes FIPS-disabled callers easy to audit).
|
Package fipsx exposes a tiny façade over crypto/fips140 so the rest of apic can gate behavior on FIPS 140-3 mode without importing crypto/fips140 directly (keeping the import boundary tight makes FIPS-disabled callers easy to audit). |
|
Package harness provides RBAC/ABAC test helpers for downstream services that consume the generated API.
|
Package harness provides RBAC/ABAC test helpers for downstream services that consume the generated API. |
|
cmd/mint
command
Command mint prints a standard RFC 7519 HS256 JWT to stdout, signed with the supplied shared secret, so non-Go test stacks (pytest/PyJWT, curl, etc.) can obtain a harness-minted token without linking any Go code.
|
Command mint prints a standard RFC 7519 HS256 JWT to stdout, signed with the supplied shared secret, so non-Go test stacks (pytest/PyJWT, curl, etc.) can obtain a harness-minted token without linking any Go code. |
|
pkg/securex/hashx/algorithm.go Package hashx implements salted, self-describing, FIPS-140-3-aware one-way hashing and constant-time verification for passwords and other authentication material.
|
pkg/securex/hashx/algorithm.go Package hashx implements salted, self-describing, FIPS-140-3-aware one-way hashing and constant-time verification for passwords and other authentication material. |
|
Package mtlsx implements the per-route mTLS verification pipeline used by apic-generated handlers: trust store loading, issuer-label enforcement, CRL/OCSP revocation checks, and principal extraction.
|
Package mtlsx implements the per-route mTLS verification pipeline used by apic-generated handlers: trust store loading, issuer-label enforcement, CRL/OCSP revocation checks, and principal extraction. |
|
Package sessionx implements FedRAMP AC-7 account lockout, AC-11 inactivity timeout, and AC-12 session termination tracking.
|
Package sessionx implements FedRAMP AC-7 account lockout, AC-11 inactivity timeout, and AC-12 session termination tracking. |
|
Package signerx is the apic abstraction over hardware- and KMS-backed crypto.Signer providers (PKCS#11 HSMs, AWS KMS, Azure Key Vault, etc.).
|
Package signerx is the apic abstraction over hardware- and KMS-backed crypto.Signer providers (PKCS#11 HSMs, AWS KMS, Azure Key Vault, etc.). |
|
Package webauthnx wraps github.com/go-webauthn/webauthn with the apic-specific identity binding, FedRAMP-compatible attestation policy (AAGUID allow-list), and ceremony orchestration.
|
Package webauthnx wraps github.com/go-webauthn/webauthn with the apic-specific identity binding, FedRAMP-compatible attestation policy (AAGUID allow-list), and ceremony orchestration. |