Documentation
¶
Index ¶
- Constants
- Variables
- func AllowedContentEncs() []jose.ContentEncryption
- func AllowedKeyAlgs() []jose.KeyAlgorithm
- func AllowedSigAlgs() []jose.SignatureAlgorithm
- func IsAllowedEnc(enc jose.ContentEncryption) bool
- func IsAllowedKeyAlg(alg jose.KeyAlgorithm) bool
- func IsAllowedSigAlg(alg jose.SignatureAlgorithm) bool
- func IsContentType(ct string) bool
- func IsError(err error) bool
- func IsInboundVerified(ctx context.Context) bool
- func Open(compact string, p *Policy, r KeyResolver) (plaintext []byte, claims *Claims, hdr OpenHeader, err error)
- func ResolvePolicy(r KeyResolver, p *Policy) error
- func Seal(payload []byte, p *Policy, r KeyResolver) (string, error)
- func WithClaims(ctx context.Context, c *Claims) context.Context
- func WithInboundVerified(ctx context.Context) context.Context
- func WithPolicy(ctx context.Context, p *Policy) context.Context
- type Claims
- type Direction
- type Error
- type Header
- type KeyResolver
- type KeyStoreLike
- type KeyStoreResolver
- type OpenHeader
- type Policy
- type PolicyRegistry
Constants ¶
const ( DefaultSigAlg = jose.RS256 DefaultKeyAlg = jose.RSA_OAEP_256 DefaultEnc = jose.A256GCM DefaultCty = "application/json" )
const ContentType = "application/jose"
ContentType is the IANA-registered media type for compact JOSE serializations. Used as the request and response Content-Type for JOSE-protected HTTP traffic.
const SentinelFieldName = "_"
SentinelFieldName is the conventional field name applications use for the jose-tagged sentinel field. Any blank-named field with a jose tag also matches; this constant documents the canonical choice.
const TagName = "jose"
Variables ¶
var ( ErrBodyRequired = errors.New("jose: request body required") ErrUnsupportedMedia = errors.New("jose: unsupported media type") ErrMalformed = errors.New("jose: malformed compact serialization") ErrAlgorithmDisallowed = errors.New("jose: algorithm not in allowlist") ErrNoneAlgRejected = errors.New("jose: alg=none rejected") ErrKidMissing = errors.New("jose: header missing kid") ErrCritUnsupported = errors.New("jose: unrecognized crit header value") ErrCtyRejected = errors.New("jose: inner cty does not match policy") ErrKidUnknown = errors.New("jose: kid not registered") ErrDecryptFailed = errors.New("jose: decryption failed") ErrInnerNotJWS = errors.New("jose: inner payload is not a JWS") ErrSignatureInvalid = errors.New("jose: signature verification failed") ErrPlaintextRejected = errors.New("jose: plaintext request rejected by policy") ErrOutboundFailed = errors.New("jose: outbound seal failed") ErrPolicyMismatch = errors.New("jose: policy mismatch") ErrPolicyAsymmetric = errors.New("jose: bidirectional policy required (request and response must both declare jose tags or neither)") ErrTagInvalid = errors.New("jose: invalid jose struct tag") ErrKeyResolution = errors.New("jose: key resolution failed") )
Functions ¶
func AllowedContentEncs ¶
func AllowedContentEncs() []jose.ContentEncryption
func AllowedKeyAlgs ¶
func AllowedKeyAlgs() []jose.KeyAlgorithm
func AllowedSigAlgs ¶
func AllowedSigAlgs() []jose.SignatureAlgorithm
AllowedSigAlgs returns a copy of the signature-algorithm allowlist for callers that need to pass it to go-jose primitives (e.g., jose.ParseSigned). Returning a copy prevents external mutation.
func IsAllowedEnc ¶
func IsAllowedEnc(enc jose.ContentEncryption) bool
func IsAllowedKeyAlg ¶
func IsAllowedKeyAlg(alg jose.KeyAlgorithm) bool
func IsAllowedSigAlg ¶
func IsAllowedSigAlg(alg jose.SignatureAlgorithm) bool
func IsContentType ¶
IsContentType reports whether ct (typically a Content-Type header value) names the JOSE compact-serialization media type. Matches application/jose with optional parameters (e.g., "application/jose; charset=utf-8") case-insensitively per RFC 7231 §3.1.1.1.
func IsError ¶
IsError reports whether err is (or wraps) a *jose.Error — useful for callers that need to distinguish JOSE crypto failures (signature invalid, decrypt failed, kid unknown, etc.) from network/transport errors. Equivalent to manually doing `var jerr *jose.Error; errors.As(err, &jerr)` but reads as a single intent at the call site.
func IsInboundVerified ¶
IsInboundVerified reports whether the context was marked verified by a successful inbound JOSE decode.
func Open ¶
func Open(compact string, p *Policy, r KeyResolver) (plaintext []byte, claims *Claims, hdr OpenHeader, err error)
Open performs the inbound transformation: decrypt the compact JWE with our private key, verify the inner JWS with the peer's public key, and parse standard JWT claims out of the verified payload.
Returns the verified plaintext payload, the extracted Claims, and the JWE+JWS Headers for diagnostic logging by the caller. On any failure, returns an *Error with the appropriate Code/Status (mostly 401 for crypto failures, 400 for malformed input).
The middleware MUST set inbound-verified state on the context only when this returns nil error — that's the gate for the encrypt-on-response security invariant.
func ResolvePolicy ¶
func ResolvePolicy(r KeyResolver, p *Policy) error
ResolvePolicy verifies that every kid named in the policy resolves to a key of the correct role via the resolver. Called once at registration time per route — failures must fail process startup (Fail Fast principle).
func Seal ¶
func Seal(payload []byte, p *Policy, r KeyResolver) (string, error)
Seal performs the outbound transformation: sign payload as a compact JWS with our private key, then encrypt that JWS as a compact JWE to the peer's public key. Returns the compact JWE string.
On any failure, returns an *Error with Code/Status mapped to JOSE_OUTBOUND_FAILED (500) — outbound failures are framework/operator errors (missing keys, bad config), never peer-induced. The Cause field carries the underlying detail for logging.
func WithClaims ¶
WithClaims attaches the verified claim set extracted from the inbound JWS payload. Applications retrieve it via ClaimsFromContext to enforce iat/exp/jti policies.
func WithInboundVerified ¶
WithInboundVerified marks the context as having passed JOSE inbound decryption + signature verification. The presence of this marker (not its value) gates outbound encryption per the security invariant: a response is JOSE-encrypted iff this is set AND the route has an outbound policy.
func WithPolicy ¶
WithPolicy attaches the active outbound Policy for this request so the response formatter can find it after the handler returns. Set by the inbound middleware branch (or by the route-registration scaffolding for outbound-only routes).
Types ¶
type Claims ¶
type Claims struct {
Issuer string
Subject string
Audience []string
IssuedAt time.Time
ExpiresAt time.Time
NotBefore time.Time
JTI string
// Raw is the full decoded claim map for fields not promoted above (vendor extensions,
// custom claims, etc.). Apps cast values themselves.
Raw map[string]any
}
Claims is the verified claim set extracted from a successfully decrypted-and-verified inbound JOSE payload. The middleware sets it on the request context so application handlers can enforce iat/exp/jti policies (per the v1 decision: framework verifies the signature, applications enforce timing).
All fields are zero-valued if absent from the JWS payload; nothing here is required at the framework layer — apps that don't care about a particular claim simply ignore it.
func ClaimsFromContext ¶
ClaimsFromContext returns the verified Claims attached by the inbound middleware, or nil if no JOSE verification ran for this request.
type Direction ¶
type Direction int
Direction indicates which side of the request/response pipeline a Policy applies to.
type Error ¶
type Error struct {
Sentinel error
Code string
Status int
Message string
Kid string
Alg string
Enc string
Cause error
}
Error is the structured value returned by every jose package operation that fails. It carries diagnostic fields (Kid, Alg, Enc) for logging and an HTTP-mapped Code/Status for response shaping. Use errors.Is(err, ErrDecryptFailed) for sentinel comparisons.
The Cause field MUST NOT be exposed to peers — it can leak information about which key was tried or which library detected the failure. Server middleware logs Cause; only Code and the constant-time generic Message reach the wire.
type Header ¶
Header (jose-package level) is the diagnostic header shape exposed to callers, distinct from the internal cryptoadapter.Header to insulate consumers from library churn.
type KeyResolver ¶
type KeyResolver interface {
PrivateKey(kid string) (*rsa.PrivateKey, error)
PublicKey(kid string) (*rsa.PublicKey, error)
}
KeyResolver abstracts key lookup so the jose package does not depend directly on app.KeyStore. This keeps the door open for future JWKS-URL backed resolvers without breaking callers, and makes testing trivial (NewTestResolver in jose/testing/).
Public/private semantics map to JOSE roles:
- PrivateKey: used to decrypt inbound JWE and sign outbound JWS.
- PublicKey: used to verify inbound JWS and encrypt outbound JWE.
Implementations MUST return the registered ErrKidUnknown sentinel (wrapped in *Error) when a kid is not configured, so callers can distinguish unknown-key from other failures via errors.Is.
type KeyStoreLike ¶
type KeyStoreLike interface {
PrivateKey(name string) (*rsa.PrivateKey, error)
PublicKey(name string) (*rsa.PublicKey, error)
}
KeyStoreLike is the minimal subset of app.KeyStore that the resolver consumes. Defining it locally lets jose/ wrap any compatible store without importing app/, which would create an app → server → jose → app cycle once the server module wires the resolver via app/module_registry.go.
type KeyStoreResolver ¶
type KeyStoreResolver struct {
// contains filtered or unexported fields
}
KeyStoreResolver adapts a KeyStoreLike (typically an app.KeyStore) to KeyResolver. It is the default resolver wired into the server when a keystore module is registered.
func NewKeyStoreResolver ¶
func NewKeyStoreResolver(ks KeyStoreLike) *KeyStoreResolver
func (*KeyStoreResolver) PrivateKey ¶
func (r *KeyStoreResolver) PrivateKey(kid string) (*rsa.PrivateKey, error)
type OpenHeader ¶
OpenHeader holds the diagnostic headers from both JOSE layers, surfaced to the caller so the middleware can log them. Never includes plaintext.
type Policy ¶
type Policy struct {
Direction Direction
// Inbound (DirectionInbound) — both required.
DecryptKid string // our private key kid (jose: decrypt=...)
VerifyKid string // peer public key kid (jose: verify=...)
// Outbound (DirectionOutbound) — both required.
SignKid string // our private key kid (jose: sign=...)
EncryptKid string // peer public key kid (jose: encrypt=...)
// Algorithms — defaults applied by the parser if tag omits them.
SigAlg jose.SignatureAlgorithm
KeyAlg jose.KeyAlgorithm
Enc jose.ContentEncryption
Cty string
}
Policy captures the JOSE configuration declared by a request or response struct's jose: tag. Inbound policies populate DecryptKid/VerifyKid; outbound populate SignKid/EncryptKid. SigAlg/KeyAlg/Enc/Cty fall back to package defaults when unset.
A Policy is constructed once at registration time by the scanner, validated against the KeyResolver, and cached in the registry — never re-parsed per request.
func ParseTag ¶
ParseTag parses a `jose:` struct tag value into a Policy with the given direction. Direction is supplied by the caller (the scanner knows whether the type is request or response from its position in HandlerFunc[T, R]). Returns an *Error wrapping ErrTagInvalid on any parse failure; the caller should treat this as a registration failure (panic at startup).
func PolicyFromContext ¶
PolicyFromContext returns the outbound Policy attached for this request, or nil.
func ScanType ¶
ScanType inspects a Go type for a jose: sentinel field. Returns (nil, nil) if the type has no jose tag (i.e., the route is not JOSE-protected). Returns (*Policy, nil) on a valid declaration. Returns (nil, *Error) on any parse or policy-validation failure.
The caller is responsible for resolving the Policy's kid references against a KeyResolver — ScanType is purely structural.
Pointer types are unwrapped: ScanType(reflect.TypeOf(*Foo)) and ScanType(reflect.TypeOf(Foo)) return the same result.
type PolicyRegistry ¶
type PolicyRegistry struct {
// contains filtered or unexported fields
}
PolicyRegistry caches scanned + resolved Policies keyed by (reflect.Type, Direction). Including direction is essential: the same struct type can legitimately be scanned for both DirectionInbound and DirectionOutbound (e.g., a shared envelope used as both a request and response on different routes), and the resulting policies have different required keys. A type-only key would return the wrong policy on the second scan.
Mirrors the pattern in database/internal/columns/registry.go:24,89 (sync.Map + LoadOrStore) which is proven thread-safe and lock-free on the hot path.
The cache stores nil to mean "this type was scanned and has no JOSE policy" — distinct from "this type has not been scanned yet" (cache miss). This avoids re-scanning untagged types on every request, which would otherwise dominate the request hot path for non-JOSE routes.
func NewPolicyRegistry ¶
func NewPolicyRegistry() *PolicyRegistry
func (*PolicyRegistry) LoadOrScan ¶
LoadOrScan returns the cached Policy for (t, dir), or scans t with the given direction and caches the result. If scan fails, returns the error without caching.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
cryptoadapter
Package cryptoadapter wraps go-jose/v4 with strict allowlist enforcement and a constant-time generic error surface.
|
Package cryptoadapter wraps go-jose/v4 with strict allowlist enforcement and a constant-time generic error surface. |
|
Package testing provides utilities for testing JOSE-protected handlers in go-bricks applications without requiring real counterparty credentials.
|
Package testing provides utilities for testing JOSE-protected handlers in go-bricks applications without requiring real counterparty credentials. |