// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package jwt_test import ( "testing" "time" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/google/tink/go/jwt" ) const ( invalidUTF8 = "\xF4\x7F\xBF\xBF" validExpiration = 1640043004 ) type testCase struct { tag string opts *jwt.RawJWTOptions json string token *jwt.RawJWT } func refString(a string) *string { return &a } func refTime(ts int64) *time.Time { t := time.Unix(ts, 0) return &t } func TestCreatingRawJWTWithAllClaims(t *testing.T) { json := `{ "sub": "tink-test-subject", "iss": "tink-test-issuer", "jti": "tink-jwt-id", "aud": ["aud-1", "aud-2"], "exp": 457888, "nbf": 450888, "iat": 400888, "cc-num": 1.67, "cc-bool": true, "cc-null": null, "cc-array": [1,2,3], "cc-string": "cc-val", "cc-object": {"nested-cc-num": 5.5} }` opts := &jwt.RawJWTOptions{ TypeHeader: refString("typeHeader"), Subject: refString("tink-test-subject"), Issuer: refString("tink-test-issuer"), JWTID: refString("tink-jwt-id"), Audiences: []string{"aud-1", "aud-2"}, ExpiresAt: refTime(457888), NotBefore: refTime(450888), IssuedAt: refTime(400888), CustomClaims: map[string]interface{}{ "cc-num": 1.67, "cc-bool": true, "cc-null": nil, "cc-string": "cc-val", "cc-array": []interface{}{1.0, 2.0, 3.0}, "cc-object": map[string]interface{}{"nested-cc-num": 5.5}, }, } fromJSON, err := jwt.NewRawJWTFromJSON(refString("typeHeader"), []byte(json)) if err != nil { t.Fatalf("jwt.NewRawJWTFromJSON(%q): %v", json, err) } fromOpts, err := jwt.NewRawJWT(opts) if err != nil { t.Fatalf("jwt.NewRawJWT(%v): %v", opts, err) } for _, tc := range []testCase{ { tag: "jwt.NewRawJWTFromJSON", token: fromJSON, }, { tag: "NewRawJWT", token: fromOpts, }, } { if !tc.token.HasTypeHeader() { t.Errorf("tc.token.HasTypeHeader() = false, want true") } if !tc.token.HasAudiences() { t.Errorf("tc.token.HasAudiences() = false, want true") } if !tc.token.HasSubject() { t.Errorf("tc.token.HasSubject() = false, want true") } if !tc.token.HasIssuer() { t.Errorf("tc.token.HasIssuer() = false, want true") } if !tc.token.HasJWTID() { t.Errorf("tc.token.HasJWTID() = false, want true") } if !tc.token.HasExpiration() { t.Errorf("tc.token.HasExpiration() = false, want true") } if !tc.token.HasNotBefore() { t.Errorf("tc.token.HasNotBefore() = false, want true") } if !tc.token.HasIssuedAt() { t.Errorf("tc.token.HasIssuedAt() = false, want true") } typeHeader, err := tc.token.TypeHeader() if err != nil { t.Errorf("tc.token.TypeHeader() err = %v, want nil", err) } if !cmp.Equal(typeHeader, *opts.TypeHeader) { t.Errorf("tc.token.TypeHeader() = %q, want %q", typeHeader, *opts.TypeHeader) } audiences, err := tc.token.Audiences() if err != nil { t.Errorf("tc.token.Audiences() err = %v, want nil", err) } if !cmp.Equal(audiences, opts.Audiences) { t.Errorf("tc.token.Audiences() = %q, want %q", audiences, opts.Audiences) } subject, err := tc.token.Subject() if err != nil { t.Errorf("tc.token.Subject() err = %v, want nil", err) } if !cmp.Equal(subject, *opts.Subject) { t.Errorf("tc.token.Subject() = %q, want %q", subject, *opts.Subject) } issuer, err := tc.token.Issuer() if err != nil { t.Errorf("tc.token.Issuer() err = %v, want nil", err) } if !cmp.Equal(issuer, *opts.Issuer) { t.Errorf("tc.token.Issuer() = %q, want %q", issuer, *opts.Issuer) } jwtID, err := tc.token.JWTID() if err != nil { t.Errorf("tc.token.JWTID() err = %v, want nil", err) } if !cmp.Equal(jwtID, *opts.JWTID) { t.Errorf("tc.token.JWTID() = %q, want %q", jwtID, *opts.JWTID) } expiresAt, err := tc.token.ExpiresAt() if err != nil { t.Errorf("tc.token.ExpiresAt() err = %v, want nil", err) } if !cmp.Equal(expiresAt, *opts.ExpiresAt) { t.Errorf("tc.token.ExpiresAt() = %q, want %q", expiresAt, *opts.ExpiresAt) } issuedAt, err := tc.token.IssuedAt() if err != nil { t.Errorf("tc.token.IssuedAt() err = %v, want nil", err) } if !cmp.Equal(issuedAt, *opts.IssuedAt) { t.Errorf("tc.token.IssuedAt() = %q, want %q", issuedAt, *opts.IssuedAt) } notBefore, err := tc.token.NotBefore() if err != nil { t.Errorf("tc.token.NotBefore() err = %v, want nil", err) } if !cmp.Equal(notBefore, *opts.NotBefore) { t.Errorf("tc.token.NotBefore() = %q, want %q", notBefore, *opts.NotBefore) } wantCustomClaims := []string{"cc-num", "cc-bool", "cc-null", "cc-string", "cc-array", "cc-object"} if !cmp.Equal(tc.token.CustomClaimNames(), wantCustomClaims, cmpopts.SortSlices(func(a, b string) bool { return a < b })) { t.Errorf("tc.token.CustomClaimNames() = %q, want %q", tc.token.CustomClaimNames(), wantCustomClaims) } if !tc.token.HasNumberClaim("cc-num") { t.Errorf("tc.token.HasNumberClaim('cc-num') = false, want true") } if !tc.token.HasBooleanClaim("cc-bool") { t.Errorf("tc.token.HasBooleanClaim('cc-bool') = false, want true") } if !tc.token.HasNullClaim("cc-null") { t.Errorf("tc.token.HasNullClaim('cc-null') = false, want true") } if !tc.token.HasStringClaim("cc-string") { t.Errorf("tc.token.HasStringClaim('cc-string') = false, want true") } if !tc.token.HasArrayClaim("cc-array") { t.Errorf("tc.token.HasArrayClaim('cc-array') = false, want true") } if !tc.token.HasObjectClaim("cc-object") { t.Errorf("tc.token.HasObjectClaim('cc-object') = false, want true") } number, err := tc.token.NumberClaim("cc-num") if err != nil { t.Errorf("tc.token.NumberClaim('cc-num') err = %v, want nil", err) } if !cmp.Equal(number, opts.CustomClaims["cc-num"]) { t.Errorf("tc.token.NumberClaim('cc-num') = %f, want %f", number, opts.CustomClaims["cc-num"]) } boolean, err := tc.token.BooleanClaim("cc-bool") if err != nil { t.Errorf("tc.token.BooleanClaim('cc-bool') err = %v, want nil", err) } if !cmp.Equal(boolean, opts.CustomClaims["cc-bool"]) { t.Errorf("tc.token.BooleanClaim('cc-bool') = %v, want %v", boolean, opts.CustomClaims["cc-bool"]) } str, err := tc.token.StringClaim("cc-string") if err != nil { t.Errorf("tc.token.StringClaim('cc-string') err = %v, want nil", err) } if !cmp.Equal(str, opts.CustomClaims["cc-string"]) { t.Errorf("tc.token.StringClaim('cc-string') = %q, want %q", str, opts.CustomClaims["cc-string"]) } array, err := tc.token.ArrayClaim("cc-array") if err != nil { t.Errorf("tc.token.ArrayClaim('cc-array') err = %v, want nil", err) } if !cmp.Equal(array, opts.CustomClaims["cc-array"]) { t.Errorf("tc.token.ArrayClaim('cc-array') = %q, want %q", array, opts.CustomClaims["cc-array"]) } object, err := tc.token.ObjectClaim("cc-object") if err != nil { t.Errorf("tc.token.ObjectClaim('cc-object') err = %v, want nil", err) } if !cmp.Equal(object, opts.CustomClaims["cc-object"]) { t.Errorf("tc.token.ObjectClaim('cc-object') = %q, want %q", object, opts.CustomClaims["cc-object"]) } } } func TestGeneratingRawJWTWithoutClaims(t *testing.T) { jsonToken, err := jwt.NewRawJWTFromJSON(nil, []byte("{}")) if err != nil { t.Fatalf("jwt.NewRawJWTFromJSON({}): %v", err) } optsToken, err := jwt.NewRawJWT(&jwt.RawJWTOptions{WithoutExpiration: true}) if err != nil { t.Fatalf("NewRawJWT with no claims: %v", err) } for _, tc := range []testCase{ { tag: "jwt.NewRawJWTFromJSON", token: jsonToken, }, { tag: "NewRawJWT", token: optsToken, }, } { if tc.token.HasTypeHeader() { t.Errorf("tc.token.HasTypeHeader() = true, want false") } if tc.token.HasAudiences() { t.Errorf("tc.token.HasAudiences() = true, want false") } if tc.token.HasSubject() { t.Errorf("tc.token.HasSubject() = true, want false") } if tc.token.HasIssuer() { t.Errorf("tc.token.HasIssuer() = true, want false") } if tc.token.HasJWTID() { t.Errorf("tc.token.HasJWTID() = true, want false") } if tc.token.HasExpiration() { t.Errorf("tc.token.HasExpiration() = true, want false") } if tc.token.HasNotBefore() { t.Errorf("tc.token.HasNotBefore() = true, want false") } if tc.token.HasIssuedAt() { t.Errorf("tc.token.HasIssuedAt() = true, want false") } if _, err := tc.token.Audiences(); err == nil { t.Errorf("tc.token.Audiences() err = nil, want error") } if _, err := tc.token.Subject(); err == nil { t.Errorf("tc.token.Subject() err = nil, want error") } if _, err := tc.token.Issuer(); err == nil { t.Errorf("tc.token.Issuer() err = nil, want error") } if _, err := tc.token.JWTID(); err == nil { t.Errorf("tc.token.JWTID() err = nil, want error") } if _, err := tc.token.ExpiresAt(); err == nil { t.Errorf("tc.token.ExpiresAt() err = nil, want error") } if _, err := tc.token.IssuedAt(); err == nil { t.Errorf("tc.token.IssuedAt() err = nil, want error") } if _, err := tc.token.NotBefore(); err == nil { t.Errorf("tc.token.NotBefore() err = nil, want error") } if !cmp.Equal(tc.token.CustomClaimNames(), []string{}) { t.Errorf("tc.token.CustomClaimNames() = %q want %q", tc.token.CustomClaimNames(), []string{}) } } } func TestNewRawJWTLargeValidTimestamps(t *testing.T) { opts := &jwt.RawJWTOptions{ TypeHeader: refString("typeHeader"), ExpiresAt: refTime(253402300799), NotBefore: refTime(253402300700), IssuedAt: refTime(253402300000), } token, err := jwt.NewRawJWT(opts) if err != nil { t.Fatalf("generating RawJWT with valid timestamps (%q, %q, %q): %v", opts.ExpiresAt, opts.NotBefore, opts.IssuedAt, err) } expiresAt, err := token.ExpiresAt() if err != nil { t.Errorf("tc.token.ExpiresAt() err = %v, want nil", err) } if !cmp.Equal(expiresAt, *opts.ExpiresAt) { t.Errorf("tc.token.ExpiresAt() = %q want %q", expiresAt, *opts.ExpiresAt) } notBefore, err := token.NotBefore() if err != nil { t.Errorf("tc.token.NotBefore() err = %v, want nil", err) } if !cmp.Equal(notBefore, *opts.NotBefore) { t.Errorf("tc.token.NotBefore() = %q want %q", notBefore, *opts.NotBefore) } issuedAt, err := token.IssuedAt() if err != nil { t.Errorf("tc.token.IssuedAt() err = %v, want nil", err) } if !cmp.Equal(issuedAt, *opts.IssuedAt) { t.Errorf("tc.token.IssuedAt() = %q want %q", issuedAt, *opts.IssuedAt) } } func TestNewRawJWTSingleStringAudience(t *testing.T) { opts := &jwt.RawJWTOptions{ WithoutExpiration: true, Audience: refString("tink-aud"), } rawJWT, err := jwt.NewRawJWT(opts) if err != nil { t.Fatalf("generating RawJWT with a single audience: %v", err) } aud, err := rawJWT.Audiences() if err != nil { t.Errorf("getting audience from token: %v", err) } want := []string{*opts.Audience} if !cmp.Equal(aud, want) { t.Errorf("rawJWT.Audiences() = %q, want %q", aud, want) } } func TestSingleStringAudienceFromJSON(t *testing.T) { rawJWT, err := jwt.NewRawJWTFromJSON(nil, []byte(`{"aud": "tink-aud"}`)) if err != nil { t.Fatalf("parsing valid RawJWT: %v", err) } aud, err := rawJWT.Audiences() if err != nil { t.Errorf("getting audience from token: %v", err) } want := []string{"tink-aud"} if !cmp.Equal(aud, want) { t.Errorf("rawJWT.Audiences() = %q, want %q", aud, want) } } func TestNewRawJWTValidationFailures(t *testing.T) { testCases := []testCase{ { tag: "empty jwt.RawJWTOptions options fails", }, { tag: "no ExpiresAt specified and WithoutExpiration = false fails", opts: &jwt.RawJWTOptions{ Audiences: []string{"tink-foo"}, }, }, { tag: "ExpiresAt and WithoutExpiration = true fails", opts: &jwt.RawJWTOptions{ Audiences: []string{"tink-foo"}, ExpiresAt: refTime(validExpiration), WithoutExpiration: true, }, }, { tag: "specifying Audenience and Audiences fails", opts: &jwt.RawJWTOptions{ Audiences: []string{"tink-foo"}, Audience: refString("tink-bar"), WithoutExpiration: true, }, }, { tag: "empty audiences array fails", opts: &jwt.RawJWTOptions{ ExpiresAt: refTime(validExpiration), Audiences: []string{}, }, }, { tag: "audiences with invalid UTF-8 string fails", opts: &jwt.RawJWTOptions{ WithoutExpiration: true, Audiences: []string{"valid", invalidUTF8}, }, }, { tag: "custom claims containing registered subject claims fails", opts: &jwt.RawJWTOptions{ Audiences: []string{"tink-foo"}, ExpiresAt: refTime(validExpiration), CustomClaims: map[string]interface{}{ "sub": "overwrite", }, }, }, { tag: "custom claims containing registered issuer claims fails", opts: &jwt.RawJWTOptions{ Audiences: []string{"tink-foo"}, ExpiresAt: refTime(validExpiration), CustomClaims: map[string]interface{}{ "iss": "overwrite", }, }, }, { tag: "custom claims containing registered jwt id claims fails", opts: &jwt.RawJWTOptions{ Audiences: []string{"tink-foo"}, ExpiresAt: refTime(validExpiration), CustomClaims: map[string]interface{}{ "jti": "overwrite", }, }, }, { tag: "custom claims containing registered expiration claims fails", opts: &jwt.RawJWTOptions{ Audiences: []string{"tink-foo"}, ExpiresAt: refTime(validExpiration), CustomClaims: map[string]interface{}{ "exp": "overwrite", }, }, }, { tag: "custom claims containing registered audience claims fails", opts: &jwt.RawJWTOptions{ Audiences: []string{"tink-foo"}, WithoutExpiration: true, CustomClaims: map[string]interface{}{ "aud": []interface{}{"overwrite"}, }, }, }, { tag: "custom claims with non standard JSON types fails", opts: &jwt.RawJWTOptions{ Audiences: []string{"tink-foo"}, ExpiresAt: refTime(validExpiration), CustomClaims: map[string]interface{}{ "complex": time.Time{}, }, }, }, { tag: "non UTF-8 string on isser claim fails", opts: &jwt.RawJWTOptions{ Audiences: []string{"tink-foo"}, ExpiresAt: refTime(validExpiration), Issuer: refString(invalidUTF8), }, }, { tag: "non UTF-8 string on subject claim fails", opts: &jwt.RawJWTOptions{ Audiences: []string{"tink-foo"}, WithoutExpiration: true, Subject: refString(invalidUTF8), }, }, { tag: "non UTF-8 string on JWT ID claim fails", opts: &jwt.RawJWTOptions{ Audiences: []string{"tink-foo"}, WithoutExpiration: true, JWTID: refString(invalidUTF8), }, }, { tag: "non UTF-8 string on custom claim fails", opts: &jwt.RawJWTOptions{ Audiences: []string{"tink-foo"}, Issuer: refString("ise-testing"), ExpiresAt: refTime(validExpiration), CustomClaims: map[string]interface{}{ "esoteric": invalidUTF8, }, }, }, { tag: "issued at timestamp greater than valid JWT max time fails", opts: &jwt.RawJWTOptions{ Audiences: []string{"tink-foo"}, ExpiresAt: refTime(validExpiration), IssuedAt: refTime(253402300800), }, }, { tag: "expires at timestamp greater than valid JWT max time fails", opts: &jwt.RawJWTOptions{ Audiences: []string{"tink-foo"}, ExpiresAt: refTime(253402300800), }, }, { tag: "not before timestamp smaller than valid JWT min time fails", opts: &jwt.RawJWTOptions{ Audiences: []string{"tink-foo"}, ExpiresAt: refTime(validExpiration), NotBefore: refTime(-5), }, }, } for _, tc := range testCases { t.Run(tc.tag, func(t *testing.T) { _, err := jwt.NewRawJWT(tc.opts) if err == nil { t.Errorf("expected error instead got nil") } }) } } func TestJSONPayload(t *testing.T) { for _, tc := range []testCase{ { tag: "subject", opts: &jwt.RawJWTOptions{ WithoutExpiration: true, Subject: refString("tink-subject"), }, json: `{"sub":"tink-subject"}`, }, { tag: "audience list", opts: &jwt.RawJWTOptions{ WithoutExpiration: true, Audiences: []string{"one"}, }, json: `{"aud":["one"]}`, }, { tag: "audience string", opts: &jwt.RawJWTOptions{ WithoutExpiration: true, Audience: refString("one"), }, json: `{"aud":"one"}`, }, { tag: "issuer", opts: &jwt.RawJWTOptions{ WithoutExpiration: true, Issuer: refString("tink-test"), }, json: `{"iss":"tink-test"}`, }, { tag: "jwt id", opts: &jwt.RawJWTOptions{ WithoutExpiration: true, JWTID: refString("tink-id"), }, json: `{"jti":"tink-id"}`, }, { tag: "issued at", opts: &jwt.RawJWTOptions{ WithoutExpiration: true, IssuedAt: refTime(78324), }, json: `{"iat":78324}`, }, { tag: "not before", opts: &jwt.RawJWTOptions{ WithoutExpiration: true, NotBefore: refTime(78324), }, json: `{"nbf":78324}`, }, { tag: "expiration", opts: &jwt.RawJWTOptions{ ExpiresAt: refTime(78324), }, json: `{"exp":78324}`, }, { tag: "integer", opts: &jwt.RawJWTOptions{ WithoutExpiration: true, CustomClaims: map[string]interface{}{ "num": 1, }, }, json: `{"num":1}`, }, { tag: "custom-claim", opts: &jwt.RawJWTOptions{ WithoutExpiration: true, CustomClaims: map[string]interface{}{ "cust": []interface{}{map[string]interface{}{"key": "val"}}, }, }, json: `{"cust":[{"key":"val"}]}`, }, { tag: "no claims", opts: &jwt.RawJWTOptions{ WithoutExpiration: true, }, json: `{}`, }, } { t.Run(tc.tag, func(t *testing.T) { token, err := jwt.NewRawJWT(tc.opts) if err != nil { t.Errorf("generating valid RawJWT: %v", err) } j, err := token.JSONPayload() if err != nil { t.Errorf("calling JSONPayload() on rawJWT: %v", err) } if !cmp.Equal(string(j), tc.json) { t.Fatalf("JSONPayload output got %v, expected %v", string(j), tc.json) } }) } } func TestFromJSONValidationFailures(t *testing.T) { testCases := []testCase{ { tag: "json with empty audience", json: `{"sub": "tink", "aud": []}`, }, { tag: "json with audience of wrong type", json: `{"aud": 5}`, }, { tag: "json with audiences of wrong type", json: `{"aud": ["one", null]}`, }, { tag: "json with registered claim with wrong type", json: `{"sub": 1}`, }, { tag: "json with non UTF-8 string on subject claim fails", json: `{"sub": "\xF4\x7F\xBF\xBF"}`, }, { tag: "json with non UTF-8 string on issuer claim fails", json: `{"iss": "\xF4\x7F\xBF\xBF"}`, }, { tag: "json with non UTF-8 string on jwt id claim fails", json: `{"jti": "\xF4\x7F\xBF\xBF"}`, }, { tag: "json with `not before` timestamp claim greater than valid JWT max time fails", json: `{"nbf": 253402301799}`, }, { tag: "json with `issued at` timestamp claim greater than valid JWT max time fails", json: `{"iat": 253402301799}`, }, { tag: "json with `expiration` timestamp claim greater than valid JWT max time fails", json: `{"exp": 253402301799}`, }, { tag: "json with `not before` timestamp claim smaller than valid JWT min time fails", json: `{"nbf": -4}`, }, { tag: "json with `issued at` timestamp claim smaller than valid JWT min time fails", json: `{"iat": -4}`, }, { tag: "json with `expiration` timestamp claim smaller than valid JWT min time fails", json: `{"exp": -4}`, }, { tag: "json with `not before` claim of non numeric type fails", json: `{"nbf": "invalid"}`, }, { tag: "json with `issued at` claim of non numeric type fails", json: `{"iat": "invalid"}`, }, { tag: "json with `expiration` claim of non numeric type fails", json: `{"exp": "invalid"}`, }, } for _, tc := range testCases { t.Run(tc.tag, func(t *testing.T) { if _, err := jwt.NewRawJWTFromJSON(nil, []byte(tc.json)); err == nil { t.Errorf("expected error instead got nil") } }) } } func TestHasCustomClaimsOfKind(t *testing.T) { opts := &jwt.RawJWTOptions{ TypeHeader: refString("typeHeader"), WithoutExpiration: true, CustomClaims: map[string]interface{}{ "cc-num": 1.67, "cc-bool": false, "cc-nil": nil, "cc-list": []interface{}{1.0, 2.0, 3.0}, "cc-string": "cc-val", "cc-object": map[string]interface{}{ "nested-cc-num": 5.5, }, }, } token, err := jwt.NewRawJWT(opts) if err != nil { t.Fatalf("generating valid RawJWT: %v", err) } if token.HasBooleanClaim("cc-num") { t.Errorf("custom number claim 'cc-num' should return false when queried for another type") } if token.HasNullClaim("cc-bool") { t.Errorf("custom boolean claim 'cc-bool' should return false when queried for another type") } if token.HasNumberClaim("cc-bool") { t.Errorf("custom boolean claim 'cc-bool' should return false when queried for another type") } if token.HasStringClaim("cc-bool") { t.Errorf("custom boolean claim 'cc-bool' should return false when queried for another type") } if token.HasArrayClaim("cc-bool") { t.Errorf("custom boolean claim 'cc-bool' should return false when queried for another type") } if token.HasObjectClaim("cc-bool") { t.Errorf("custom boolean claim 'cc-bool' should return false when queried for another type") } } func TestGettingRegisteredClaimsThroughCustomFails(t *testing.T) { opts := &jwt.RawJWTOptions{ TypeHeader: refString("typeHeader"), Subject: refString("tink-test-subject"), Issuer: refString("tink-test-issuer"), JWTID: refString("tink-jwt-id-1"), Audiences: []string{"aud-1", "aud-2"}, ExpiresAt: refTime(validExpiration), IssuedAt: refTime(validExpiration - 100), NotBefore: refTime(validExpiration - 50), } token, err := jwt.NewRawJWT(opts) if err != nil { t.Fatalf("generating valid RawJWT: %v", err) } if !cmp.Equal(token.CustomClaimNames(), []string{}) { t.Errorf("tc.token.CustomClaimNames() = %q want %q", token.CustomClaimNames(), []string{}) } for _, c := range []string{"sub", "iss", "aud", "nbf", "exp", "iat", "jti"} { if token.HasNullClaim(c) { t.Errorf("registered '%q' claim should return false when calling HasNullClaim", c) } if token.HasBooleanClaim(c) { t.Errorf("registered '%q' claim should return false when calling HasBooleanClaim", c) } if _, err := token.BooleanClaim(c); err == nil { t.Errorf("expected error when calling token.BoolClaim(%q) instead got nil", c) } if token.HasNumberClaim(c) { t.Errorf("registered '%q' claim should return false when calling HasNumberClaim", c) } if _, err := token.NumberClaim(c); err == nil { t.Errorf("expected error when calling token.NumberClaim(%q) instead got nil", c) } if token.HasStringClaim(c) { t.Errorf("registered '%q' claim should return false when calling HasStringClaim", c) } if _, err := token.StringClaim(c); err == nil { t.Errorf("expected error when calling token.StringClaim(%q) instead got nil", c) } if token.HasArrayClaim(c) { t.Errorf("registered '%q' claim should return false when calling HasArrayClaim", c) } if _, err := token.ArrayClaim(c); err == nil { t.Errorf("expected error when calling token.ListClaim(%q) instead got nil", c) } if token.HasObjectClaim(c) { t.Errorf("registered '%q' claim should return false when calling HasObjectClaim", c) } if _, err := token.ObjectClaim(c); err == nil { t.Errorf("expected error when calling token.JSONClaim(%q) instead got nil", c) } } }