Documentation
¶
Overview ¶
Package jwt implements JSON Web Tokens (RFC 7519) and the JSON Web Signature (RFC 7515) compact serialization from the Go standard library only — no third-party JOSE dependency. It provides signing and verification for HMAC (HS256/384/512), RSA-PKCS1v15 (RS256/384/512), ECDSA (ES256/384/512, fixed- width R||S per RFC 7518) and EdDSA (Ed25519), with an explicit algorithm allowlist on the verify path to make algorithm-confusion attacks impossible.
Security posture (see plans/phase-4-jwt.md):
- A Verifier is bound to a fixed (algorithm allowlist, key material) pair. A token whose header "alg" is not in the allowlist is rejected before any cryptographic operation runs, so algorithm-confusion (e.g. presenting an HS256 token to an RSA verifier and using the public key bytes as the MAC secret) is impossible by construction.
- The string "none" is never a valid Algorithm and is rejected both at verifier construction and on decode.
- ECDSA signatures are JOSE-native fixed-width R||S; DER-encoded signatures are rejected.
- The raw token bytes are never placed in an error string, log line, or Authentication.Details(); errors describe only the failure class.
Index ¶
- Variables
- func Decode(raw string, v Verifier, validator *Validator) (MapClaims, RegisteredClaims, error)
- func ES256Verify(pub *ecdsa.PublicKey) *ecdsaVerifyingKey
- func ES384Verify(pub *ecdsa.PublicKey) *ecdsaVerifyingKey
- func ES512Verify(pub *ecdsa.PublicKey) *ecdsaVerifyingKey
- func EdDSASign(priv ed25519.PrivateKey) *ed25519SigningKey
- func EdDSAVerify(pub ed25519.PublicKey) *ed25519VerifyingKey
- func Encode(s Signer, claims MapClaims) (string, error)
- func EncodeRegistered(s Signer, rc RegisteredClaims, extra MapClaims) (string, error)
- func NewES256(priv *ecdsa.PrivateKey) *ecdsaSigningKey
- func NewES384(priv *ecdsa.PrivateKey) *ecdsaSigningKey
- func NewES512(priv *ecdsa.PrivateKey) *ecdsaSigningKey
- func NewHS256(secret []byte) *hmacKey
- func NewHS384(secret []byte) *hmacKey
- func NewHS512(secret []byte) *hmacKey
- func NewRS256(priv *rsa.PrivateKey) *rsaSigningKey
- func NewRS384(priv *rsa.PrivateKey) *rsaSigningKey
- func NewRS512(priv *rsa.PrivateKey) *rsaSigningKey
- func RS256Verify(pub *rsa.PublicKey) *rsaVerifyingKey
- func RS384Verify(pub *rsa.PublicKey) *rsaVerifyingKey
- func RS512Verify(pub *rsa.PublicKey) *rsaVerifyingKey
- type Algorithm
- type Audience
- type Clock
- type Header
- type JWK
- type JWKSClient
- type JWKSOption
- type JWKSet
- type KeySelector
- type MapClaims
- type NumericDate
- type RegisteredClaims
- type Signer
- type SigningKey
- type ValidationOption
- func WithClock(c Clock) ValidationOption
- func WithLeeway(skew time.Duration) ValidationOption
- func WithRequireExpiry(required bool) ValidationOption
- func WithRequiredAudience(aud string) ValidationOption
- func WithRequiredIssuer(iss string) ValidationOption
- func WithValidateIAT(required bool) ValidationOption
- type Validator
- type Verifier
- type VerifyingKey
Constants ¶
This section is empty.
Variables ¶
var ( // ErrMalformedToken indicates the compact serialization is structurally // invalid (e.g. an empty segment). ErrMalformedToken = errors.New("jwt: malformed compact serialization") // ErrNotJWS indicates the serialization does not have exactly three // dot-separated segments (a JWE 5-segment token is out of scope). ErrNotJWS = errors.New("jwt: not a JWS compact serialization (expected 3 segments)") // ErrUnsupportedAlg indicates the algorithm is "none", empty, or otherwise // not supported. ErrUnsupportedAlg = errors.New("jwt: unsupported or disallowed algorithm") // ErrUnsupportedCrit indicates an unrecognized critical header parameter. ErrUnsupportedCrit = errors.New("jwt: unrecognized critical header parameter") // ErrAlgMismatch indicates the token's alg is not in the verifier allowlist. ErrAlgMismatch = errors.New("jwt: token alg not in verifier allowlist") // ErrKeyTypeMismatch indicates the supplied key type does not match the // algorithm (or, for ECDSA, the curve does not match). ErrKeyTypeMismatch = errors.New("jwt: key type does not match algorithm") // ErrSignatureInvalid indicates the signature did not verify. ErrSignatureInvalid = errors.New("jwt: signature invalid") // ErrTokenExpired indicates the token is past its exp (accounting for skew). ErrTokenExpired = errors.New("jwt: token expired") // ErrTokenNotYetValid indicates the token's nbf is in the future. ErrTokenNotYetValid = errors.New("jwt: token not yet valid (nbf)") // ErrIssuedInFuture indicates the token's iat is in the future beyond skew. ErrIssuedInFuture = errors.New("jwt: token issued in the future (iat)") // ErrClaimMissing indicates a required claim is absent. ErrClaimMissing = errors.New("jwt: required claim missing") // ErrIssuerMismatch indicates the iss claim does not match the required value. ErrIssuerMismatch = errors.New("jwt: issuer mismatch") // ErrAudienceMismatch indicates the aud claim does not contain the required value. ErrAudienceMismatch = errors.New("jwt: audience mismatch") // ErrKeyNotFound indicates no configured key matches the token's kid. ErrKeyNotFound = errors.New("jwt: no key matches kid") )
Sentinel errors. They are wrap-friendly (callers match with errors.Is) and never embed token contents.
var ErrJWKSFetch = errors.New("jwt: jwks fetch failed")
ErrJWKSFetch indicates the JWKS endpoint returned a non-2xx status or could not be reached.
var ErrJWKSTooLarge = errors.New("jwt: jwks response too large")
ErrJWKSTooLarge indicates the JWKS response exceeded the configured size cap.
Functions ¶
func Decode ¶
Decode parses and verifies the signature of raw against v, then (if validator is non-nil) validates the registered claims. It returns the claims only on full success; it NEVER returns claims from an unverified or invalid token.
func ES256Verify ¶
ES256Verify builds a VerifyingKey for ES256 from a P-256 public key.
func ES384Verify ¶
ES384Verify builds a VerifyingKey for ES384 (P-384).
func ES512Verify ¶
ES512Verify builds a VerifyingKey for ES512 (P-521).
func EdDSASign ¶
func EdDSASign(priv ed25519.PrivateKey) *ed25519SigningKey
EdDSASign builds an Ed25519 SigningKey. The key is copied defensively.
func EdDSAVerify ¶
EdDSAVerify builds an Ed25519 VerifyingKey. The key is copied defensively and must be ed25519.PublicKeySize bytes.
func Encode ¶
Encode marshals the header (with the signer's algorithm and optional kid) and the claims, signs the signing-input, and returns the compact JWS.
func EncodeRegistered ¶
func EncodeRegistered(s Signer, rc RegisteredClaims, extra MapClaims) (string, error)
EncodeRegistered encodes the registered claims merged with any extra custom claims (extra wins on key collision is avoided: registered claims take precedence for the standard names).
func NewES256 ¶
func NewES256(priv *ecdsa.PrivateKey) *ecdsaSigningKey
NewES256 builds an ECDSA-P-256-SHA-256 SigningKey. The private key MUST be on P-256; a mismatch yields ErrKeyTypeMismatch at sign time.
func NewES384 ¶
func NewES384(priv *ecdsa.PrivateKey) *ecdsaSigningKey
NewES384 builds an ECDSA-P-384-SHA-384 SigningKey (requires P-384).
func NewES512 ¶
func NewES512(priv *ecdsa.PrivateKey) *ecdsaSigningKey
NewES512 builds an ECDSA-P-521-SHA-512 SigningKey (requires P-521; note P-521, not P-512).
func NewHS256 ¶
func NewHS256(secret []byte) *hmacKey
NewHS256 builds an HMAC-SHA-256 SigningKey/VerifyingKey from a secret. The secret should be at least 32 bytes for HS256 (RFC 7518 §3.2 recommends a key at least as long as the hash output); a shorter key is accepted but weak.
func NewHS384 ¶
func NewHS384(secret []byte) *hmacKey
NewHS384 builds an HMAC-SHA-384 carrier (recommended secret >= 48 bytes).
func NewHS512 ¶
func NewHS512(secret []byte) *hmacKey
NewHS512 builds an HMAC-SHA-512 carrier (recommended secret >= 64 bytes).
func NewRS256 ¶
func NewRS256(priv *rsa.PrivateKey) *rsaSigningKey
NewRS256 builds an RSASSA-PKCS1-v1_5-SHA-256 SigningKey from an RSA private key.
func NewRS384 ¶
func NewRS384(priv *rsa.PrivateKey) *rsaSigningKey
NewRS384 builds an RSASSA-PKCS1-v1_5-SHA-384 SigningKey.
func NewRS512 ¶
func NewRS512(priv *rsa.PrivateKey) *rsaSigningKey
NewRS512 builds an RSASSA-PKCS1-v1_5-SHA-512 SigningKey.
func RS256Verify ¶
RS256Verify builds a VerifyingKey for RS256 from an RSA public key.
func RS384Verify ¶
RS384Verify builds a VerifyingKey for RS384.
func RS512Verify ¶
RS512Verify builds a VerifyingKey for RS512.
Types ¶
type Algorithm ¶
type Algorithm string
Algorithm is the JOSE "alg" header value. "none" is intentionally absent from the constant set and is never a valid Algorithm.
type Audience ¶
type Audience []string
Audience is the RFC 7519 §4.1.3 "aud" claim. It marshals to a bare string when it holds a single value and to a JSON array otherwise, and unmarshals from either form.
func (Audience) MarshalJSON ¶
MarshalJSON emits a bare string for a single-element audience, a JSON array otherwise. An empty audience marshals to null (and is omitted when the field carries omitempty).
func (*Audience) UnmarshalJSON ¶
UnmarshalJSON accepts either a JSON string or a JSON array of strings.
type Header ¶
type Header struct {
Alg string `json:"alg"`
Typ string `json:"typ,omitempty"`
Kid string `json:"kid,omitempty"`
Cty string `json:"cty,omitempty"`
Crit []string `json:"crit,omitempty"`
}
Header is the JOSE protected header (RFC 7515 §4). The jku/x5u/jwk/x5c parameters are intentionally NOT fields: they are ignored and never trusted, which closes the classic JWKS-via-token SSRF and embedded-key trust holes.
type JWK ¶
type JWK struct {
Kty string `json:"kty"`
Kid string `json:"kid,omitempty"`
Alg string `json:"alg,omitempty"`
Use string `json:"use,omitempty"`
// RSA public params.
N string `json:"n,omitempty"`
E string `json:"e,omitempty"`
// EC public params.
Crv string `json:"crv,omitempty"`
X string `json:"x,omitempty"`
Y string `json:"y,omitempty"`
// oct (symmetric) secret.
K string `json:"k,omitempty"`
}
JWK is a single JSON Web Key (RFC 7517). Only the fields required to reconstruct a public verifying key (or an oct secret) are parsed.
func (JWK) VerifyingKey ¶
func (k JWK) VerifyingKey() (VerifyingKey, error)
VerifyingKey reconstructs the Go verifying key (or oct secret carrier) from a JWK, defaulting the algorithm from the key type and curve. The returned VerifyingKey's Algorithm() reports the alg the key can verify, which the JWKSClient cross-checks against the verifier allowlist.
For an EC key the public point is validated to be on the named curve (rejecting invalid-curve attack inputs). An unknown kty, a malformed base64url parameter, or an out-of-range RSA exponent is rejected.
type JWKSClient ¶
type JWKSClient struct {
// contains filtered or unexported fields
}
JWKSClient fetches a JWK Set from an operator-configured URL and caches it, refreshing on TTL expiry and on a kid miss (throttled by a minimum refresh interval). It implements KeySelector so it plugs directly into NewVerifier.
The fetch URL is operator-configured and is NEVER derived from a token claim or header (no jku/x5u following), which closes the classic JWKS SSRF. The response body is read through an io.LimitReader with a hard cap. The cache is per-instance and guarded by a sync.RWMutex.
func NewJWKSClient ¶
func NewJWKSClient(jwksURL string, opts ...JWKSOption) (*JWKSClient, error)
NewJWKSClient builds a JWKSClient for the given JWKS URL. It validates the URL scheme (https unless AllowInsecureURL(true) is set).
func (*JWKSClient) Refresh ¶
func (c *JWKSClient) Refresh(ctx context.Context) error
Refresh forces a fetch, bypassing the min-refresh throttle.
func (*JWKSClient) Select ¶
func (c *JWKSClient) Select(kid string, alg Algorithm) (VerifyingKey, error)
Select implements KeySelector. It returns the cached key for kid (an empty kid selects the sole key when exactly one is cached). On a stale cache or a kid miss it triggers at most one throttled refresh, then re-checks.
type JWKSOption ¶
type JWKSOption func(*jwksConfig)
JWKSOption configures a JWKSClient.
func AllowInsecureURL ¶
func AllowInsecureURL(allow bool) JWKSOption
AllowInsecureURL permits an http:// JWKS URL (default false: https only). It exists for localhost test fixtures and should not be used in production.
func WithCacheTTL ¶
func WithCacheTTL(d time.Duration) JWKSOption
WithCacheTTL sets how long a fetched key set is served from cache before a lazy refresh (default 10m).
func WithHTTPClient ¶
func WithHTTPClient(c *http.Client) JWKSOption
WithHTTPClient injects the HTTP client used for fetches. The default is a bounded client with a 5s timeout (never http.DefaultClient).
func WithMaxResponseBytes ¶
func WithMaxResponseBytes(n int64) JWKSOption
WithMaxResponseBytes caps the JWKS response body size (default 256 KiB).
func WithMinRefreshInterval ¶
func WithMinRefreshInterval(d time.Duration) JWKSOption
WithMinRefreshInterval sets the minimum interval between on-miss refreshes, throttling refreshes triggered by unknown kids (default 1m).
type JWKSet ¶
type JWKSet struct {
Keys []JWK `json:"keys"`
}
JWKSet is a JWK Set (RFC 7517 §5).
func ParseJWKSet ¶
ParseJWKSet parses a JWK Set from its JSON encoding.
type KeySelector ¶
type KeySelector interface {
Select(kid string, alg Algorithm) (VerifyingKey, error)
}
KeySelector resolves a VerifyingKey by kid (and alg). An empty kid selects the sole configured key when exactly one matches the algorithm.
type MapClaims ¶
MapClaims is the full claim set: registered claims plus arbitrary custom claims, preserving unknown fields. Typed accessors return a zero value and false on absence or type mismatch.
func (MapClaims) GetString ¶
GetString returns the named claim as a string, or ("", false) when absent or not a string.
func (MapClaims) GetStringSlice ¶
GetStringSlice returns the named claim as a string slice. A single string is tolerated and returned as a one-element slice (it is NOT space-split here; space-splitting of the scope claim is a mapper concern). A JSON array of strings is returned element-wise. Returns (nil, false) on absence or a non-string/array shape.
func (MapClaims) Registered ¶
func (m MapClaims) Registered() (RegisteredClaims, error)
Registered extracts the RFC 7519 registered claims from the map. Numeric dates tolerate integer or float JSON numbers (json decodes them as float64).
type NumericDate ¶
NumericDate wraps time.Time with the RFC 7519 §2 "seconds since epoch" JSON representation. It tolerates a fractional value on decode and emits an integer on encode.
func NewNumericDate ¶
func NewNumericDate(t time.Time) *NumericDate
NewNumericDate builds a *NumericDate from a time.Time (truncated to seconds on the wire).
func (NumericDate) MarshalJSON ¶
func (n NumericDate) MarshalJSON() ([]byte, error)
MarshalJSON emits the integer seconds since the Unix epoch.
func (*NumericDate) UnmarshalJSON ¶
func (n *NumericDate) UnmarshalJSON(b []byte) error
UnmarshalJSON parses a numeric (integer or float) seconds-since-epoch value.
type RegisteredClaims ¶
type RegisteredClaims struct {
Issuer string `json:"iss,omitempty"`
Subject string `json:"sub,omitempty"`
Audience Audience `json:"aud,omitempty"`
ExpiresAt *NumericDate `json:"exp,omitempty"`
NotBefore *NumericDate `json:"nbf,omitempty"`
IssuedAt *NumericDate `json:"iat,omitempty"`
ID string `json:"jti,omitempty"`
}
RegisteredClaims are the RFC 7519 §4.1 registered claims.
type Signer ¶
type Signer interface {
Algorithm() Algorithm
// Sign returns the raw signature bytes for the signing-input.
Sign(signingInput []byte) ([]byte, error)
// contains filtered or unexported methods
}
Signer signs a signing-input and reports its algorithm.
type SigningKey ¶
type SigningKey interface {
Algorithm() Algorithm
// contains filtered or unexported methods
}
SigningKey is the per-algorithm signing carrier produced by HS256/RS256/…/ EdDSASign. It reports its algorithm and signs a signing-input. It is the internal contract consumed by NewSigner/Encode.
type ValidationOption ¶
type ValidationOption func(*Validator)
ValidationOption configures a Validator.
func WithClock ¶
func WithClock(c Clock) ValidationOption
WithClock injects the time source used for exp/nbf/iat checks.
func WithLeeway ¶
func WithLeeway(skew time.Duration) ValidationOption
WithLeeway sets the permitted clock skew. It must be in [0, 5m]; a value outside that range panics as a programmer error (a huge skew silently defeats expiry validation, and a negative skew is meaningless).
func WithRequireExpiry ¶
func WithRequireExpiry(required bool) ValidationOption
WithRequireExpiry sets whether a token must carry an exp claim. The default is true (reject a token with no exp).
SECURITY: Setting this false accepts never-expiring tokens — a stolen token is then valid forever and cannot be aged out. Only do so for an atypical issuer that genuinely omits exp, and compensate with short-lived issuance or revocation elsewhere. Never set it false to "fix" a clock-skew or expired-token error.
func WithRequiredAudience ¶
func WithRequiredAudience(aud string) ValidationOption
WithRequiredAudience requires the aud claim to contain aud. An empty string clears the requirement.
func WithRequiredIssuer ¶
func WithRequiredIssuer(iss string) ValidationOption
WithRequiredIssuer requires the iss claim to equal iss exactly (no normalization, no prefix match). An empty string clears the requirement.
func WithValidateIAT ¶
func WithValidateIAT(required bool) ValidationOption
WithValidateIAT sets whether the iat claim, if present, is rejected when it is in the future beyond the skew. Presence remains optional. Default false.
type Validator ¶
type Validator struct {
// contains filtered or unexported fields
}
Validator checks the registered claims of a verified token with a configurable clock skew (leeway) and required-claim policy. It is read-only after construction and safe for concurrent use.
func NewValidator ¶
func NewValidator(opts ...ValidationOption) *Validator
NewValidator builds a Validator. Defaults: system clock, 60s leeway, expiry required, issuer/audience unchecked, iat not validated.
func (*Validator) Validate ¶
func (v *Validator) Validate(c RegisteredClaims) error
Validate checks exp/nbf/iat (with skew), the required issuer/audience, and the require-expiry policy. It returns a wrapped sentinel error on failure.
type Verifier ¶
type Verifier interface {
// Verify checks that h.Alg is in the allowlist and the signature is valid
// for the key selected by h.Kid. It returns ErrAlgMismatch /
// ErrUnsupportedAlg / ErrSignatureInvalid / ErrKeyNotFound.
Verify(h Header, signingInput, sig []byte) error
// Allowed reports the immutable algorithm allowlist.
Allowed() []Algorithm
}
Verifier verifies a signature for a fixed set of allowed algorithms and keys. Algorithm-confusion is impossible: an alg outside the allowlist, or "none", is rejected before any cryptographic operation runs.
func NewVerifier ¶
func NewVerifier(allow []Algorithm, sel KeySelector) (Verifier, error)
NewVerifier builds a Verifier from an explicit algorithm allowlist and a KeySelector. It rejects an empty allowlist and any "none"/unsupported alg in the allowlist at construction time.
func NewVerifierKey ¶
func NewVerifierKey(alg Algorithm, key VerifyingKey) (Verifier, error)
NewVerifierKey builds a Verifier with a single static key for one algorithm.
type VerifyingKey ¶
type VerifyingKey interface {
Algorithm() Algorithm
// contains filtered or unexported methods
}
VerifyingKey is the per-algorithm verification carrier produced by RS256Verify/…/EdDSAVerify and by JWK.VerifyingKey. It reports the algorithm it can verify and checks a signature over a signing-input. It is the internal contract consumed by NewVerifier and JWKSClient.