Beispiel
github.com/golang-jwt/jwt/v5 v5.3.1: Validate single and multiple token audiences using WithAudience, WithAllAudiences, and ClaimStrings in jwt/v5
Verifiziertes Beispiel für golang github.com/golang-jwt/jwt/v5 v5.3.1: Validate single and multiple token audiences using WithAudience, WithAllAudiences, and…
sha256:a5bbd16d6ea67b8a7eebbb367ba078aef91a7eb164022f08bec050e926dd4e89
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
- Validate single and multiple token audiences using WithAudience, WithAllAudiences, and ClaimStrings in jwt/v5
- Symbole
-
- jwt.WithAudience
- jwt.WithAllAudiences
- jwt.ClaimStrings
- jwt.ParseWithClaims
- Umgebung
- go
- Erstellt
- 2026-08-17T20:27:29Z
Contract
- jwt.WithAudience matches when any specified audience is present in the token aud claim, whereas jwt.WithAllAudiences requires all specified audiences to be present.
- assert jwt.ClaimStrings deserializes aud claims formatted as either a single JSON string or a JSON array of strings
- assert audience mismatch produces jwt.ErrTokenInvalidAudience while an omitted aud claim produces jwt.ErrTokenRequiredClaimMissing when audience validation is active
Dateien
- NOTES.md
- audience_test.go
- csx.json
- go.mod
- go.sum
Quelltext
# Audience Claim Validation and Serialization in github.com/golang-jwt/jwt/v5
## Prior Search Results
A search via the network cache returned existing samples for `github.com/golang-jwt/jwt/v5`:
- `sha256:52c730ef7ed1a1b5deccabaff9420cd697d56b8a6b9a16484e33cfd280773b60` and `sha256:5cc6ca09e311b47fd32d10456d99a84756f1419206b988682cadf876c050712e` (custom claims validation and expiration requirement).
- `sha256:d819a477e3c71430c7e39b93904d658552f6fd0a02fc4532dcc8876754758157` (RSA invalid key type error wrapping).
To avoid duplication, this sample investigates audience (`aud`) claim handling across single and multiple audience tokens, examining `jwt.WithAudience`, `jwt.WithAllAudiences`, and `jwt.ClaimStrings`.
## Exact Release Pinned
- `github.com/golang-jwt/jwt/v5` at `v5.3.1`.
## Observed Behaviour
1. **Audience Serialization Flexibility (`jwt.ClaimStrings`)**:
Under RFC 7519, the `aud` claim can be represented either as a single JSON string or an array of strings. `jwt.ClaimStrings` deserializes both formats into a string slice without error.
2. **Any-Match vs All-Match Audience Requirements**:
- `jwt.WithAudience(aud ...string)` validates that the token contains *at least one* of the specified audiences. For tokens carrying multiple audiences, any match satisfies the parser.
- `jwt.WithAllAudiences(aud ...string)` validates that the token contains *all* specified audiences. If any specified audience is missing from the token's audience list, validation fails.
3. **Distinct Error Reporting**:
- When a token contains an `aud` claim that fails matching requirements, parsing fails with `jwt.ErrTokenInvalidAudience` (which also wraps `jwt.ErrTokenInvalidClaims`).
- When an expected audience is validated against a token that omits the `aud` claim entirely, `jwt.ParseWithClaims` fails with `jwt.ErrTokenRequiredClaimMissing` rather than `jwt.ErrTokenInvalidAudience`.
package audience_test
import (
"encoding/json"
"errors"
"testing"
"time"
"github.com/golang-jwt/jwt/v5"
)
var signingKey = []byte("sample-hmac-secret-key-32-bytes-long")
func TestClaimStringsUnmarshal(t *testing.T) {
// RFC 7519 allows the "aud" claim to be formatted as either a single string or an array of strings.
// jwt.ClaimStrings supports unmarshaling from both representations.
singleJSON := []byte(`{"aud":"https://api.example.com"}`)
var singleClaims jwt.RegisteredClaims
if err := json.Unmarshal(singleJSON, &singleClaims); err != nil {
t.Fatalf("unexpected unmarshal error for single audience string: %v", err)
}
if len(singleClaims.Audience) != 1 || singleClaims.Audience[0] != "https://api.example.com" {
t.Fatalf("expected [https://api.example.com], got %v", singleClaims.Audience)
}
arrayJSON := []byte(`{"aud":["https://api.example.com","https://auth.example.com"]}`)
var arrayClaims jwt.RegisteredClaims
if err := json.Unmarshal(arrayJSON, &arrayClaims); err != nil {
t.Fatalf("unexpected unmarshal error for audience array: %v", err)
}
if len(arrayClaims.Audience) != 2 || arrayClaims.Audience[0] != "https://api.example.com" || arrayClaims.Audience[1] != "https://auth.example.com" {
t.Fatalf("expected [https://api.example.com, https://auth.example.com], got %v", arrayClaims.Audience)
}
}
func TestWithAudienceValidation(t *testing.T) {
// Token with a single audience
singleAudToken := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
Audience: jwt.ClaimStrings{"https://api.example.com"},
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
})
singleSigned, err := singleAudToken.SignedString(signingKey)
if err != nil {
t.Fatalf("failed to sign single audience token: %v", err)
}
// Token with multiple audiences
multiAudToken := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
Audience: jwt.ClaimStrings{"https://api.example.com", "https://auth.example.com"},
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
})
multiSigned, err := multiAudToken.SignedString(signingKey)
if err != nil {
t.Fatalf("failed to sign multi audience token: %v", err)
}
// 1. WithAudience matches single audience token
tok1, err := jwt.ParseWithClaims(singleSigned, &jwt.RegisteredClaims{}, func(tok *jwt.Token) (interface{}, error) {
return signingKey, nil
}, jwt.WithAudience("https://api.example.com"))
if err != nil || !tok1.Valid {
t.Fatalf("expected valid token when audience matches single aud, got err: %v", err)
}
// 2. WithAudience matches any one of multiple audiences in multi-audience token
tok2, err := jwt.ParseWithClaims(multiSigned, &jwt.RegisteredClaims{}, func(tok *jwt.Token) (interface{}, error) {
return signingKey, nil
}, jwt.WithAudience("https://auth.example.com"))
if err != nil || !tok2.Valid {
t.Fatalf("expected valid token when audience matches one of multiple aud entries, got err: %v", err)
}
// 3. WithAudience fails when audience is not present in token
tok3, err := jwt.ParseWithClaims(multiSigned, &jwt.RegisteredClaims{}, func(tok *jwt.Token) (interface{}, error) {
return signingKey, nil
}, jwt.WithAudience("https://billing.example.com"))
if err == nil {
t.Fatal("expected error when audience is not in token aud list, got nil")
}
if tok3 != nil && tok3.Valid {
t.Fatal("expected token to be marked invalid on audience mismatch")
}
if !errors.Is(err, jwt.ErrTokenInvalidAudience) {
t.Fatalf("expected ErrTokenInvalidAudience, got: %v", err)
}
if !errors.Is(err, jwt.ErrTokenInvalidClaims) {
t.Fatalf("expected ErrTokenInvalidClaims, got: %v", err)
}
}
func TestWithAllAudiencesValidation(t *testing.T) {
multiAudToken := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
Audience: jwt.ClaimStrings{"https://api.example.com", "https://auth.example.com"},
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
})
multiSigned, err := multiAudToken.SignedString(signingKey)
if err != nil {
t.Fatalf("failed to sign multi audience token: %v", err)
}
// 1. WithAllAudiences succeeds when all specified audiences exist in token
tokPass, err := jwt.ParseWithClaims(multiSigned, &jwt.RegisteredClaims{}, func(tok *jwt.Token) (interface{}, error) {
return signingKey, nil
}, jwt.WithAllAudiences("https://api.example.com", "https://auth.example.com"))
if err != nil || !tokPass.Valid {
t.Fatalf("expected valid token when all required audiences are present, got err: %v", err)
}
// 2. WithAllAudiences fails if at least one required audience is missing from token
tokFail, err := jwt.ParseWithClaims(multiSigned, &jwt.RegisteredClaims{}, func(tok *jwt.Token) (interface{}, error) {
return signingKey, nil
}, jwt.WithAllAudiences("https://api.example.com", "https://billing.example.com"))
if err == nil {
t.Fatal("expected error when not all required audiences are present, got nil")
}
if tokFail != nil && tokFail.Valid {
t.Fatal("expected token to be marked invalid when missing one required audience")
}
if !errors.Is(err, jwt.ErrTokenInvalidAudience) {
t.Fatalf("expected ErrTokenInvalidAudience, got: %v", err)
}
}
func TestMissingAudienceClaimRequiresAudience(t *testing.T) {
// When a token does not include an "aud" claim at all, validating with WithAudience fails with ErrTokenRequiredClaimMissing
tokenNoAud := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
})
noAudSigned, err := tokenNoAud.SignedString(signingKey)
if err != nil {
t.Fatalf("failed to sign token without audience: %v", err)
}
_, errParse := jwt.ParseWithClaims(noAudSigned, &jwt.RegisteredClaims{}, func(tok *jwt.Token) (interface{}, error) {
return signingKey, nil
}, jwt.WithAudience("https://api.example.com"))
if errParse == nil {
t.Fatal("expected error when validating audience on token without aud claim, got nil")
}
if !errors.Is(errParse, jwt.ErrTokenRequiredClaimMissing) {
t.Fatalf("expected ErrTokenRequiredClaimMissing, got: %v", errParse)
}
if errors.Is(errParse, jwt.ErrTokenInvalidAudience) {
t.Fatalf("expected ErrTokenInvalidAudience to be false for omitted aud claim, got true")
}
}
{"case":{"caseId":"case:sha256:23ebf25af3f85b76643ead8db95fc820242e7122dc16c8efabc132f4e2aa27d6","contract":["jwt.WithAudience matches when any specified audience is present in the token aud claim, whereas jwt.WithAllAudiences requires all specified audiences to be present.","assert jwt.ClaimStrings deserializes aud claims formatted as either a single JSON string or a JSON array of strings","assert audience mismatch produces jwt.ErrTokenInvalidAudience while an omitted aud claim produces jwt.ErrTokenRequiredClaimMissing when audience validation is active"],"goal":"Validate single and multiple token audiences using WithAudience, WithAllAudiences, and ClaimStrings in jwt/v5","kind":"HOW","packages":["pkg:golang/github.com/golang-jwt/jwt/v5@v5.3.1"],"schemaVersion":1,"symbols":["jwt.WithAudience","jwt.WithAllAudiences","jwt.ClaimStrings","jwt.ParseWithClaims"]},"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.WithAudience","jwt.WithAllAudiences","jwt.ClaimStrings","jwt.ParseWithClaims"],"verifierAdapter":"golang@1"}
module example.com/jwt-sample
go 1.26.5
require github.com/golang-jwt/jwt/v5 v5.3.1
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=