Documentation
¶
Overview ¶
Package jwt issues and verifies compact JSON Web Tokens, deliberately limited to HS256, standard library only.
It follows the JWS compact serialization of RFC 7515 and the registered claims of RFC 7519, but supports exactly one algorithm (HMAC-SHA256) with strict defaults. There is no algorithm negotiation, no "none", and no asymmetric keys: a smaller surface is a safer surface.
Signing ¶
claims := jwt.Claims{
Subject: "user-123",
Issuer: "api",
ExpiresAt: time.Now().Add(time.Hour).Unix(),
IssuedAt: time.Now().Unix(),
}
token, err := jwt.Sign(claims, key)
The header is always {"alg":"HS256","typ":"JWT"}. Custom claims go in Claims.Extra; a registered claim name there is rejected (ErrReservedClaim), so registered claims come only from the typed fields. The key must be at least 32 bytes (ErrWeakKey), the HMAC-SHA256 output size required by RFC 7518.
Verifying ¶
claims, err := jwt.Verify(token, key,
jwt.WithIssuer("api"),
jwt.WithLeeway(30*time.Second),
)
Verify requires alg=HS256 and a present exp, verifies the HMAC signature (compared with hmac.Equal) before interpreting the payload, and then checks exp/nbf/iat plus any configured issuer and audience. It rejects a token that declares a crit header, a segment that is not strict base64url, and a registered claim of the wrong JSON type. For key rotation, pass additional keys with WithKey: tokens are signed with the primary key and verified against any configured key. WithMaxBytes bounds the work spent on untrusted input.
Not supported ¶
RS/ES/PS algorithms, "none", JWE encryption and JWKS are out of scope by design. Parsing without signature verification is not exposed.
See DOC.md (English) and DOC.UK.md (Ukrainian) for the full reference.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNoKey is returned when signing or verifying without a key. ErrNoKey = errors.New("jwt: no signing key") // ErrWeakKey is returned when the HS256 key is shorter than 32 bytes. RFC // 7518 requires a key at least as long as the hash output (256 bits) for // HMAC-SHA256, so shorter keys are rejected instead of silently weakening // every token. ErrWeakKey = errors.New("jwt: key too short, HS256 needs at least 32 bytes") // ErrMalformed is returned when the token is not a well-formed // header.payload.signature triple, a segment is not strict base64url, or a // registered claim has the wrong JSON type. ErrMalformed = errors.New("jwt: malformed token") // ErrUnsupportedCritical is returned when the header carries a crit // parameter: this verifier implements no extensions, so it must reject any // token that declares one as mandatory (RFC 7515 section 4.1.11). ErrUnsupportedCritical = errors.New("jwt: unsupported crit header parameter") // ErrReservedClaim is returned by Sign when Claims.Extra contains a // registered claim name (iss, sub, aud, exp, nbf, iat, jti). Set those // through the typed fields so the source of each registered claim is // unambiguous. ErrReservedClaim = errors.New("jwt: Extra must not contain registered claim names") // ErrTooLarge is returned by Verify when the token exceeds the configured // maximum size (see WithMaxBytes). ErrTooLarge = errors.New("jwt: token exceeds maximum size") // ErrAlgMismatch is returned when the token header does not use HS256 // (this includes "none" and any asymmetric algorithm). ErrAlgMismatch = errors.New("jwt: unexpected algorithm, want HS256") // ErrSignature is returned when the signature does not verify against any // configured key. ErrSignature = errors.New("jwt: signature mismatch") // ErrMissingExpiry is returned when the token has no exp claim, which is // required. ErrMissingExpiry = errors.New("jwt: missing exp claim") // ErrExpired is returned when the token is past its exp (plus leeway). ErrExpired = errors.New("jwt: token expired") // ErrNotYetValid is returned when the token is before its nbf (minus leeway). ErrNotYetValid = errors.New("jwt: token not yet valid") // ErrIssuedInFuture is returned when the token's iat is in the future // (minus leeway). ErrIssuedInFuture = errors.New("jwt: token issued in the future") // ErrIssuer is returned when the iss claim does not match the expected one. ErrIssuer = errors.New("jwt: issuer mismatch") // ErrAudience is returned when the aud claim does not include the expected // audience. ErrAudience = errors.New("jwt: audience mismatch") )
Functions ¶
func Sign ¶
Sign creates a signed HS256 JWT for the claims. The key must be at least 32 bytes. Claims.Extra must not carry registered claim names; set those through the typed fields.
Example ¶
package main
import (
"fmt"
"time"
"github.com/goloop/jwt"
)
func main() {
key := []byte("0123456789abcdef0123456789abcdef")
token, err := jwt.Sign(jwt.Claims{
Subject: "user-123",
Issuer: "api",
Audience: jwt.Audience{"web"},
ExpiresAt: time.Now().Add(time.Hour).Unix(),
IssuedAt: time.Now().Unix(),
Extra: map[string]any{"role": "admin"},
}, key)
if err != nil {
panic(err)
}
claims, err := jwt.Verify(token, key,
jwt.WithIssuer("api"),
jwt.WithAudience("web"),
jwt.WithLeeway(30*time.Second),
)
if err != nil {
panic(err)
}
fmt.Println(claims.Subject, claims.Extra["role"])
}
Output: user-123 admin
Types ¶
type Audience ¶
type Audience []string
Audience is the aud claim. Per RFC 7519 it may be a single string or an array of strings; it marshals to a string when it holds one value.
func (Audience) MarshalJSON ¶
MarshalJSON encodes a single-element audience as a string, otherwise as an array.
func (*Audience) UnmarshalJSON ¶
UnmarshalJSON accepts either a string or an array of strings.
type Claims ¶
type Claims struct {
Issuer string
Subject string
Audience Audience
ExpiresAt int64
NotBefore int64
IssuedAt int64
ID string
Extra map[string]any
}
Claims holds the RFC 7519 registered claims plus any custom claims in Extra. Times are Unix seconds (the RFC NumericDate representation).
func Verify ¶
Verify parses and validates an HS256 JWT and returns its claims. It requires alg=HS256 and a present exp; it verifies the HMAC signature (compared with hmac.Equal) before interpreting the payload, then checks exp/nbf/iat plus any configured issuer and audience. A token that declares a crit header is rejected, since this verifier implements no extensions.
func (Claims) MarshalJSON ¶
MarshalJSON merges the registered claims and Extra into one JSON object. Extra carries only custom claims; a registered claim name in Extra is an error (ErrReservedClaim), because the typed fields are the single source of truth for those claims.
func (*Claims) UnmarshalJSON ¶
UnmarshalJSON extracts the registered claims and keeps the rest in Extra. A registered claim with the wrong JSON type is rejected (ErrMalformed) rather than silently coerced, so a malformed exp or aud cannot slip through verification as if it were absent.
type Option ¶
type Option func(*options)
Option configures Verify.
func WithAudience ¶
WithAudience requires the aud claim to include audience.
func WithClock ¶
WithClock overrides the time source (for testing). A nil function is ignored, leaving the default of time.Now.
func WithIssuer ¶
WithIssuer requires the iss claim to equal issuer.
func WithKey ¶
WithKey adds another key to try during verification, for key rotation. Tokens are always signed with the primary key; verification accepts any configured key. A key shorter than 32 bytes is ignored, matching the signing minimum.
func WithLeeway ¶
WithLeeway allows a clock-skew tolerance when checking exp, nbf and iat.
func WithMaxBytes ¶
WithMaxBytes rejects a token longer than n bytes before any parsing, to bound the work spent on untrusted input. The default (0) imposes no limit; set a limit when verifying tokens from the open internet.