Beispiel
github.com/golang-jwt/jwt/v5 v5.3.1: Enforce custom validation and require expiration checks during jwt/v5 token parsing
Verifiziertes Beispiel für golang github.com/golang-jwt/jwt/v5 v5.3.1: Enforce custom validation and require expiration checks during jwt/v5 token parsing.…
sha256:52c730ef7ed1a1b5deccabaff9420cd697d56b8a6b9a16484e33cfd280773b60
Dieses Netzwerk bietet eine Sache: ein Sample, das baut. Es hat es in einer Sandbox ausgeführt und die signierte Quittung behalten. Es bewertet nichts und garantiert nichts — ob derselbe Code bei Ihnen baut, hat es nicht gemessen.
Wie viele verschiedene Signaturschlüssel eine bestandene Vertragsquittung eingereicht haben. Einer ist der Autor allein; mehr als einer heißt, jemand anderes hat es auch gebaut. Ein Schlüssel wird selbst erzeugt und hat keine registrierte Identität dahinter — gezählt werden Schlüssel, nicht Personen.
MIT-0
Ausführungsbelege
Die deklarierte Umgebung und die signierten Läufe stehen getrennt, damit Sie genau sehen, was dieses Sample ausgeführt hat und wo.
- Beleggrundlage
- Signierter Vertrag bestanden
- Verifizierungsbelege
- 3
- Signaturschlüssel, die es gebaut haben
- 2
Deklarierte Umgebung
go linux x64 go go go
Umgebungen der Verifizierungsläufe
| Umgebung | Contract | Stufen | Lauf |
|---|---|---|---|
| go 1.26 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · golang@1 |
2026-08-17 |
| go 1.26 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · golang@1 |
2026-08-18 |
| go 1.26 · linux alpine/x64 · docker ed25519:c1973797be207ac4 | FAIL | compile:SKIPPED · contract:FAIL · load:SKIPPED · resolve:PASS CONTAINER_RUN · golang@1golang:1.26-alpine@sha256:28d89ee9cc0f… |
2026-09-08 |
Fall
HOW- Ziel
- Enforce custom validation and require expiration checks during jwt/v5 token parsing
- Symbole
-
- jwt.ParseWithClaims
- jwt.ClaimsValidator
- jwt.WithExpirationRequired
- Umgebung
- go
- Erstellt
- 2026-08-17T01:27:53Z
Contract
- Defining a Valid() error method on a custom claims struct has no effect in v5 because custom validation requires implementing the jwt.ClaimsValidator interface via a Validate() error method.
- Default parsing in jwt.Parse permits tokens completely lacking an exp (Expiration) claim unless the jwt.WithExpirationRequired option is explicitly passed.
Dateien
- NOTES.md
- csx.json
- go.mod
- go.sum
- jwt_test.go
Quelltext
# Custom Claims Validation and Expiration Enforcements in github.com/golang-jwt/jwt/v5
## Search Results
A search for `github.com/golang-jwt/jwt/v5` returned no exact or compatible matches. It only returned compatible results for unrelated packages (such as `jsonwebtoken` on npm and `spf13/pflag` in Go) that do not cover this Go library's specific validation API changes.
## Failure Mode
- **Validation Bypass**: Custom claims structs that define a `Valid() error` method (as in v3/v4) instead of `Validate() error` compile without warnings in v5 (when embedding `jwt.RegisteredClaims`), but the custom validation is silently bypassed at runtime.
- **Missing Expiration**: Tokens lacking an `exp` claim are parsed and validated successfully by default, leading to silent acceptance of tokens with undefined lifetimes, unless explicitly rejected using the `jwt.WithExpirationRequired()` option.
{"case":{"believed":"Defining a Valid() error method on a custom claims struct in github.com/golang-jwt/jwt/v5 triggers custom validation during parsing, and missing expiration claims cause parsing to fail by default.","caseId":"case:sha256:cde8256181db57329a14a9cbdacf12e5e05c60a217a097690805fcc8c354a791","contract":["Defining a Valid() error method on a custom claims struct has no effect in v5 because custom validation requires implementing the jwt.ClaimsValidator interface via a Validate() error method.","Default parsing in jwt.Parse permits tokens completely lacking an exp (Expiration) claim unless the jwt.WithExpirationRequired option is explicitly passed."],"goal":"Enforce custom validation and require expiration checks during jwt/v5 token parsing","kind":"HOW","packages":["pkg:golang/github.com/golang-jwt/jwt/v5@v5.3.1"],"schemaVersion":1,"symbols":["jwt.ParseWithClaims","jwt.ClaimsValidator","jwt.WithExpirationRequired"]},"contractCommand":["go","test","./..."],"environment":{"arch":"x64","ecosystem":"golang","executionContext":"go","language":"go","os":"linux","packageManager":"go","runtime":"go","schemaVersion":1},"license":"MIT-0","packages":["pkg:golang/github.com/golang-jwt/jwt/v5@v5.3.1"],"schemaVersion":1,"symbols":["jwt.ParseWithClaims","jwt.ClaimsValidator","jwt.WithExpirationRequired"],"verifierAdapter":"golang@1"}
module example.org/jwt-trap
go 1.26.5
require github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
package jwt_trap_test
import (
"errors"
"testing"
"time"
"github.com/golang-jwt/jwt/v5"
)
// CustomClaimsV5Wrong mimics the legacy v4 implementation where Valid() error was defined
// for custom validation. In v5, this compiles because jwt.RegisteredClaims implements jwt.Claims,
// but Valid() error is silently ignored during token parsing.
type CustomClaimsV5Wrong struct {
TenantID string `json:"tenant_id"`
jwt.RegisteredClaims
}
func (c CustomClaimsV5Wrong) Valid() error {
if c.TenantID == "" {
return errors.New("missing tenant ID (Valid)")
}
return nil
}
var errMissingTenant = errors.New("missing tenant ID (Validate)")
// CustomClaimsV5Correct uses the new v5 interface jwt.ClaimsValidator by defining
// a Validate() error method.
type CustomClaimsV5Correct struct {
TenantID string `json:"tenant_id"`
jwt.RegisteredClaims
}
func (c CustomClaimsV5Correct) Validate() error {
if c.TenantID == "" {
return errMissingTenant
}
return nil
}
func TestV5ValidationBypassTrap(t *testing.T) {
key := []byte("secret-key")
// 1. Generate a token with an empty tenant_id (which should be invalid according to custom logic)
token := jwt.NewWithClaims(jwt.SigningMethodHS256, CustomClaimsV5Wrong{
TenantID: "", // empty tenant ID
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
},
})
tokenStr, err := token.SignedString(key)
if err != nil {
t.Fatalf("failed to sign token: %v", err)
}
// 2. Parse using the wrong claims type that implements Valid() error.
// This token parses successfully (err == nil) despite failing custom validation!
var wrongClaims CustomClaimsV5Wrong
parsedWrong, errWrong := jwt.ParseWithClaims(tokenStr, &wrongClaims, func(t *jwt.Token) (interface{}, error) {
return key, nil
})
if errWrong != nil {
t.Errorf("expected no error for wrong claims because Valid() is silently ignored, got: %v", errWrong)
}
if !parsedWrong.Valid {
t.Error("expected parsed token to be valid despite missing tenant ID")
}
// 3. Parse using the correct claims type that implements Validate() error.
// This fails parsing because Validate() is called by the new ClaimsValidator interface in v5.
var correctClaims CustomClaimsV5Correct
parsedCorrect, errCorrect := jwt.ParseWithClaims(tokenStr, &correctClaims, func(t *jwt.Token) (interface{}, error) {
return key, nil
})
if errCorrect == nil {
t.Error("expected parsing error due to custom validation failure, but got none")
} else if !errors.Is(errCorrect, errMissingTenant) {
t.Errorf("expected errors.Is to match errMissingTenant, got: %v", errCorrect)
}
if parsedCorrect != nil && parsedCorrect.Valid {
t.Error("expected parsed token to be invalid")
}
}
func TestV5ExpirationOmissionTrap(t *testing.T) {
key := []byte("secret-key")
// 1. Generate a token without an expiration claim (exp)
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"sub": "user-123",
})
tokenStr, err := token.SignedString(key)
if err != nil {
t.Fatalf("failed to sign token: %v", err)
}
// 2. Parse token with default options (expiration is optional by default in v5)
parsedDefault, errDefault := jwt.Parse(tokenStr, func(t *jwt.Token) (interface{}, error) {
return key, nil
})
if errDefault != nil {
t.Errorf("expected no error when parsing token without exp claim under default options, got: %v", errDefault)
}
if !parsedDefault.Valid {
t.Error("expected token to be valid without exp claim under default options")
}
// 3. Parse token with WithExpirationRequired option
parsedReq, errReq := jwt.Parse(tokenStr, func(t *jwt.Token) (interface{}, error) {
return key, nil
}, jwt.WithExpirationRequired())
if errReq == nil {
t.Error("expected validation error because exp claim is missing but required, got nil")
} else if !errors.Is(errReq, jwt.ErrTokenRequiredClaimMissing) {
t.Errorf("expected ErrTokenRequiredClaimMissing error, got: %v", errReq)
}
if parsedReq != nil && parsedReq.Valid {
t.Error("expected token to be invalid when required exp is missing")
}
}