1*e7b1675dSTing-Kang Chang// Copyright 2021 Google LLC 2*e7b1675dSTing-Kang Chang// 3*e7b1675dSTing-Kang Chang// Licensed under the Apache License, Version 2.0 (the "License"); 4*e7b1675dSTing-Kang Chang// you may not use this file except in compliance with the License. 5*e7b1675dSTing-Kang Chang// You may obtain a copy of the License at 6*e7b1675dSTing-Kang Chang// 7*e7b1675dSTing-Kang Chang// http://www.apache.org/licenses/LICENSE-2.0 8*e7b1675dSTing-Kang Chang// 9*e7b1675dSTing-Kang Chang// Unless required by applicable law or agreed to in writing, software 10*e7b1675dSTing-Kang Chang// distributed under the License is distributed on an "AS IS" BASIS, 11*e7b1675dSTing-Kang Chang// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*e7b1675dSTing-Kang Chang// See the License for the specific language governing permissions and 13*e7b1675dSTing-Kang Chang// limitations under the License. 14*e7b1675dSTing-Kang Chang// 15*e7b1675dSTing-Kang Chang//////////////////////////////////////////////////////////////////////////////// 16*e7b1675dSTing-Kang Chang 17*e7b1675dSTing-Kang Changpackage jwt_test 18*e7b1675dSTing-Kang Chang 19*e7b1675dSTing-Kang Changimport ( 20*e7b1675dSTing-Kang Chang "testing" 21*e7b1675dSTing-Kang Chang "time" 22*e7b1675dSTing-Kang Chang 23*e7b1675dSTing-Kang Chang "github.com/google/go-cmp/cmp" 24*e7b1675dSTing-Kang Chang "github.com/google/go-cmp/cmp/cmpopts" 25*e7b1675dSTing-Kang Chang "github.com/google/tink/go/jwt" 26*e7b1675dSTing-Kang Chang) 27*e7b1675dSTing-Kang Chang 28*e7b1675dSTing-Kang Changconst ( 29*e7b1675dSTing-Kang Chang invalidUTF8 = "\xF4\x7F\xBF\xBF" 30*e7b1675dSTing-Kang Chang validExpiration = 1640043004 31*e7b1675dSTing-Kang Chang) 32*e7b1675dSTing-Kang Chang 33*e7b1675dSTing-Kang Changtype testCase struct { 34*e7b1675dSTing-Kang Chang tag string 35*e7b1675dSTing-Kang Chang opts *jwt.RawJWTOptions 36*e7b1675dSTing-Kang Chang json string 37*e7b1675dSTing-Kang Chang token *jwt.RawJWT 38*e7b1675dSTing-Kang Chang} 39*e7b1675dSTing-Kang Chang 40*e7b1675dSTing-Kang Changfunc refString(a string) *string { 41*e7b1675dSTing-Kang Chang return &a 42*e7b1675dSTing-Kang Chang} 43*e7b1675dSTing-Kang Chang 44*e7b1675dSTing-Kang Changfunc refTime(ts int64) *time.Time { 45*e7b1675dSTing-Kang Chang t := time.Unix(ts, 0) 46*e7b1675dSTing-Kang Chang return &t 47*e7b1675dSTing-Kang Chang} 48*e7b1675dSTing-Kang Chang 49*e7b1675dSTing-Kang Changfunc TestCreatingRawJWTWithAllClaims(t *testing.T) { 50*e7b1675dSTing-Kang Chang json := `{ 51*e7b1675dSTing-Kang Chang "sub": "tink-test-subject", 52*e7b1675dSTing-Kang Chang "iss": "tink-test-issuer", 53*e7b1675dSTing-Kang Chang "jti": "tink-jwt-id", 54*e7b1675dSTing-Kang Chang "aud": ["aud-1", "aud-2"], 55*e7b1675dSTing-Kang Chang "exp": 457888, 56*e7b1675dSTing-Kang Chang "nbf": 450888, 57*e7b1675dSTing-Kang Chang "iat": 400888, 58*e7b1675dSTing-Kang Chang "cc-num": 1.67, 59*e7b1675dSTing-Kang Chang "cc-bool": true, 60*e7b1675dSTing-Kang Chang "cc-null": null, 61*e7b1675dSTing-Kang Chang "cc-array": [1,2,3], 62*e7b1675dSTing-Kang Chang "cc-string": "cc-val", 63*e7b1675dSTing-Kang Chang "cc-object": {"nested-cc-num": 5.5} 64*e7b1675dSTing-Kang Chang }` 65*e7b1675dSTing-Kang Chang 66*e7b1675dSTing-Kang Chang opts := &jwt.RawJWTOptions{ 67*e7b1675dSTing-Kang Chang TypeHeader: refString("typeHeader"), 68*e7b1675dSTing-Kang Chang Subject: refString("tink-test-subject"), 69*e7b1675dSTing-Kang Chang Issuer: refString("tink-test-issuer"), 70*e7b1675dSTing-Kang Chang JWTID: refString("tink-jwt-id"), 71*e7b1675dSTing-Kang Chang Audiences: []string{"aud-1", "aud-2"}, 72*e7b1675dSTing-Kang Chang ExpiresAt: refTime(457888), 73*e7b1675dSTing-Kang Chang NotBefore: refTime(450888), 74*e7b1675dSTing-Kang Chang IssuedAt: refTime(400888), 75*e7b1675dSTing-Kang Chang CustomClaims: map[string]interface{}{ 76*e7b1675dSTing-Kang Chang "cc-num": 1.67, 77*e7b1675dSTing-Kang Chang "cc-bool": true, 78*e7b1675dSTing-Kang Chang "cc-null": nil, 79*e7b1675dSTing-Kang Chang "cc-string": "cc-val", 80*e7b1675dSTing-Kang Chang "cc-array": []interface{}{1.0, 2.0, 3.0}, 81*e7b1675dSTing-Kang Chang "cc-object": map[string]interface{}{"nested-cc-num": 5.5}, 82*e7b1675dSTing-Kang Chang }, 83*e7b1675dSTing-Kang Chang } 84*e7b1675dSTing-Kang Chang fromJSON, err := jwt.NewRawJWTFromJSON(refString("typeHeader"), []byte(json)) 85*e7b1675dSTing-Kang Chang if err != nil { 86*e7b1675dSTing-Kang Chang t.Fatalf("jwt.NewRawJWTFromJSON(%q): %v", json, err) 87*e7b1675dSTing-Kang Chang } 88*e7b1675dSTing-Kang Chang fromOpts, err := jwt.NewRawJWT(opts) 89*e7b1675dSTing-Kang Chang if err != nil { 90*e7b1675dSTing-Kang Chang t.Fatalf("jwt.NewRawJWT(%v): %v", opts, err) 91*e7b1675dSTing-Kang Chang } 92*e7b1675dSTing-Kang Chang for _, tc := range []testCase{ 93*e7b1675dSTing-Kang Chang { 94*e7b1675dSTing-Kang Chang tag: "jwt.NewRawJWTFromJSON", 95*e7b1675dSTing-Kang Chang token: fromJSON, 96*e7b1675dSTing-Kang Chang }, 97*e7b1675dSTing-Kang Chang { 98*e7b1675dSTing-Kang Chang tag: "NewRawJWT", 99*e7b1675dSTing-Kang Chang token: fromOpts, 100*e7b1675dSTing-Kang Chang }, 101*e7b1675dSTing-Kang Chang } { 102*e7b1675dSTing-Kang Chang if !tc.token.HasTypeHeader() { 103*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasTypeHeader() = false, want true") 104*e7b1675dSTing-Kang Chang } 105*e7b1675dSTing-Kang Chang if !tc.token.HasAudiences() { 106*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasAudiences() = false, want true") 107*e7b1675dSTing-Kang Chang } 108*e7b1675dSTing-Kang Chang if !tc.token.HasSubject() { 109*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasSubject() = false, want true") 110*e7b1675dSTing-Kang Chang } 111*e7b1675dSTing-Kang Chang if !tc.token.HasIssuer() { 112*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasIssuer() = false, want true") 113*e7b1675dSTing-Kang Chang } 114*e7b1675dSTing-Kang Chang if !tc.token.HasJWTID() { 115*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasJWTID() = false, want true") 116*e7b1675dSTing-Kang Chang } 117*e7b1675dSTing-Kang Chang if !tc.token.HasExpiration() { 118*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasExpiration() = false, want true") 119*e7b1675dSTing-Kang Chang } 120*e7b1675dSTing-Kang Chang if !tc.token.HasNotBefore() { 121*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasNotBefore() = false, want true") 122*e7b1675dSTing-Kang Chang } 123*e7b1675dSTing-Kang Chang if !tc.token.HasIssuedAt() { 124*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasIssuedAt() = false, want true") 125*e7b1675dSTing-Kang Chang } 126*e7b1675dSTing-Kang Chang 127*e7b1675dSTing-Kang Chang typeHeader, err := tc.token.TypeHeader() 128*e7b1675dSTing-Kang Chang if err != nil { 129*e7b1675dSTing-Kang Chang t.Errorf("tc.token.TypeHeader() err = %v, want nil", err) 130*e7b1675dSTing-Kang Chang } 131*e7b1675dSTing-Kang Chang if !cmp.Equal(typeHeader, *opts.TypeHeader) { 132*e7b1675dSTing-Kang Chang t.Errorf("tc.token.TypeHeader() = %q, want %q", typeHeader, *opts.TypeHeader) 133*e7b1675dSTing-Kang Chang } 134*e7b1675dSTing-Kang Chang audiences, err := tc.token.Audiences() 135*e7b1675dSTing-Kang Chang if err != nil { 136*e7b1675dSTing-Kang Chang t.Errorf("tc.token.Audiences() err = %v, want nil", err) 137*e7b1675dSTing-Kang Chang } 138*e7b1675dSTing-Kang Chang if !cmp.Equal(audiences, opts.Audiences) { 139*e7b1675dSTing-Kang Chang t.Errorf("tc.token.Audiences() = %q, want %q", audiences, opts.Audiences) 140*e7b1675dSTing-Kang Chang } 141*e7b1675dSTing-Kang Chang subject, err := tc.token.Subject() 142*e7b1675dSTing-Kang Chang if err != nil { 143*e7b1675dSTing-Kang Chang t.Errorf("tc.token.Subject() err = %v, want nil", err) 144*e7b1675dSTing-Kang Chang } 145*e7b1675dSTing-Kang Chang if !cmp.Equal(subject, *opts.Subject) { 146*e7b1675dSTing-Kang Chang t.Errorf("tc.token.Subject() = %q, want %q", subject, *opts.Subject) 147*e7b1675dSTing-Kang Chang } 148*e7b1675dSTing-Kang Chang issuer, err := tc.token.Issuer() 149*e7b1675dSTing-Kang Chang if err != nil { 150*e7b1675dSTing-Kang Chang t.Errorf("tc.token.Issuer() err = %v, want nil", err) 151*e7b1675dSTing-Kang Chang } 152*e7b1675dSTing-Kang Chang if !cmp.Equal(issuer, *opts.Issuer) { 153*e7b1675dSTing-Kang Chang t.Errorf("tc.token.Issuer() = %q, want %q", issuer, *opts.Issuer) 154*e7b1675dSTing-Kang Chang } 155*e7b1675dSTing-Kang Chang jwtID, err := tc.token.JWTID() 156*e7b1675dSTing-Kang Chang if err != nil { 157*e7b1675dSTing-Kang Chang t.Errorf("tc.token.JWTID() err = %v, want nil", err) 158*e7b1675dSTing-Kang Chang } 159*e7b1675dSTing-Kang Chang if !cmp.Equal(jwtID, *opts.JWTID) { 160*e7b1675dSTing-Kang Chang t.Errorf("tc.token.JWTID() = %q, want %q", jwtID, *opts.JWTID) 161*e7b1675dSTing-Kang Chang } 162*e7b1675dSTing-Kang Chang expiresAt, err := tc.token.ExpiresAt() 163*e7b1675dSTing-Kang Chang if err != nil { 164*e7b1675dSTing-Kang Chang t.Errorf("tc.token.ExpiresAt() err = %v, want nil", err) 165*e7b1675dSTing-Kang Chang } 166*e7b1675dSTing-Kang Chang if !cmp.Equal(expiresAt, *opts.ExpiresAt) { 167*e7b1675dSTing-Kang Chang t.Errorf("tc.token.ExpiresAt() = %q, want %q", expiresAt, *opts.ExpiresAt) 168*e7b1675dSTing-Kang Chang } 169*e7b1675dSTing-Kang Chang issuedAt, err := tc.token.IssuedAt() 170*e7b1675dSTing-Kang Chang if err != nil { 171*e7b1675dSTing-Kang Chang t.Errorf("tc.token.IssuedAt() err = %v, want nil", err) 172*e7b1675dSTing-Kang Chang } 173*e7b1675dSTing-Kang Chang if !cmp.Equal(issuedAt, *opts.IssuedAt) { 174*e7b1675dSTing-Kang Chang t.Errorf("tc.token.IssuedAt() = %q, want %q", issuedAt, *opts.IssuedAt) 175*e7b1675dSTing-Kang Chang } 176*e7b1675dSTing-Kang Chang notBefore, err := tc.token.NotBefore() 177*e7b1675dSTing-Kang Chang if err != nil { 178*e7b1675dSTing-Kang Chang t.Errorf("tc.token.NotBefore() err = %v, want nil", err) 179*e7b1675dSTing-Kang Chang } 180*e7b1675dSTing-Kang Chang if !cmp.Equal(notBefore, *opts.NotBefore) { 181*e7b1675dSTing-Kang Chang t.Errorf("tc.token.NotBefore() = %q, want %q", notBefore, *opts.NotBefore) 182*e7b1675dSTing-Kang Chang } 183*e7b1675dSTing-Kang Chang wantCustomClaims := []string{"cc-num", "cc-bool", "cc-null", "cc-string", "cc-array", "cc-object"} 184*e7b1675dSTing-Kang Chang if !cmp.Equal(tc.token.CustomClaimNames(), wantCustomClaims, cmpopts.SortSlices(func(a, b string) bool { return a < b })) { 185*e7b1675dSTing-Kang Chang t.Errorf("tc.token.CustomClaimNames() = %q, want %q", tc.token.CustomClaimNames(), wantCustomClaims) 186*e7b1675dSTing-Kang Chang } 187*e7b1675dSTing-Kang Chang if !tc.token.HasNumberClaim("cc-num") { 188*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasNumberClaim('cc-num') = false, want true") 189*e7b1675dSTing-Kang Chang } 190*e7b1675dSTing-Kang Chang if !tc.token.HasBooleanClaim("cc-bool") { 191*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasBooleanClaim('cc-bool') = false, want true") 192*e7b1675dSTing-Kang Chang } 193*e7b1675dSTing-Kang Chang if !tc.token.HasNullClaim("cc-null") { 194*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasNullClaim('cc-null') = false, want true") 195*e7b1675dSTing-Kang Chang } 196*e7b1675dSTing-Kang Chang if !tc.token.HasStringClaim("cc-string") { 197*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasStringClaim('cc-string') = false, want true") 198*e7b1675dSTing-Kang Chang } 199*e7b1675dSTing-Kang Chang if !tc.token.HasArrayClaim("cc-array") { 200*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasArrayClaim('cc-array') = false, want true") 201*e7b1675dSTing-Kang Chang } 202*e7b1675dSTing-Kang Chang if !tc.token.HasObjectClaim("cc-object") { 203*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasObjectClaim('cc-object') = false, want true") 204*e7b1675dSTing-Kang Chang } 205*e7b1675dSTing-Kang Chang 206*e7b1675dSTing-Kang Chang number, err := tc.token.NumberClaim("cc-num") 207*e7b1675dSTing-Kang Chang if err != nil { 208*e7b1675dSTing-Kang Chang t.Errorf("tc.token.NumberClaim('cc-num') err = %v, want nil", err) 209*e7b1675dSTing-Kang Chang } 210*e7b1675dSTing-Kang Chang if !cmp.Equal(number, opts.CustomClaims["cc-num"]) { 211*e7b1675dSTing-Kang Chang t.Errorf("tc.token.NumberClaim('cc-num') = %f, want %f", number, opts.CustomClaims["cc-num"]) 212*e7b1675dSTing-Kang Chang } 213*e7b1675dSTing-Kang Chang boolean, err := tc.token.BooleanClaim("cc-bool") 214*e7b1675dSTing-Kang Chang if err != nil { 215*e7b1675dSTing-Kang Chang t.Errorf("tc.token.BooleanClaim('cc-bool') err = %v, want nil", err) 216*e7b1675dSTing-Kang Chang } 217*e7b1675dSTing-Kang Chang if !cmp.Equal(boolean, opts.CustomClaims["cc-bool"]) { 218*e7b1675dSTing-Kang Chang t.Errorf("tc.token.BooleanClaim('cc-bool') = %v, want %v", boolean, opts.CustomClaims["cc-bool"]) 219*e7b1675dSTing-Kang Chang } 220*e7b1675dSTing-Kang Chang str, err := tc.token.StringClaim("cc-string") 221*e7b1675dSTing-Kang Chang if err != nil { 222*e7b1675dSTing-Kang Chang t.Errorf("tc.token.StringClaim('cc-string') err = %v, want nil", err) 223*e7b1675dSTing-Kang Chang } 224*e7b1675dSTing-Kang Chang if !cmp.Equal(str, opts.CustomClaims["cc-string"]) { 225*e7b1675dSTing-Kang Chang t.Errorf("tc.token.StringClaim('cc-string') = %q, want %q", str, opts.CustomClaims["cc-string"]) 226*e7b1675dSTing-Kang Chang } 227*e7b1675dSTing-Kang Chang array, err := tc.token.ArrayClaim("cc-array") 228*e7b1675dSTing-Kang Chang if err != nil { 229*e7b1675dSTing-Kang Chang t.Errorf("tc.token.ArrayClaim('cc-array') err = %v, want nil", err) 230*e7b1675dSTing-Kang Chang } 231*e7b1675dSTing-Kang Chang if !cmp.Equal(array, opts.CustomClaims["cc-array"]) { 232*e7b1675dSTing-Kang Chang t.Errorf("tc.token.ArrayClaim('cc-array') = %q, want %q", array, opts.CustomClaims["cc-array"]) 233*e7b1675dSTing-Kang Chang } 234*e7b1675dSTing-Kang Chang object, err := tc.token.ObjectClaim("cc-object") 235*e7b1675dSTing-Kang Chang if err != nil { 236*e7b1675dSTing-Kang Chang t.Errorf("tc.token.ObjectClaim('cc-object') err = %v, want nil", err) 237*e7b1675dSTing-Kang Chang } 238*e7b1675dSTing-Kang Chang if !cmp.Equal(object, opts.CustomClaims["cc-object"]) { 239*e7b1675dSTing-Kang Chang t.Errorf("tc.token.ObjectClaim('cc-object') = %q, want %q", object, opts.CustomClaims["cc-object"]) 240*e7b1675dSTing-Kang Chang } 241*e7b1675dSTing-Kang Chang } 242*e7b1675dSTing-Kang Chang} 243*e7b1675dSTing-Kang Chang 244*e7b1675dSTing-Kang Changfunc TestGeneratingRawJWTWithoutClaims(t *testing.T) { 245*e7b1675dSTing-Kang Chang jsonToken, err := jwt.NewRawJWTFromJSON(nil, []byte("{}")) 246*e7b1675dSTing-Kang Chang if err != nil { 247*e7b1675dSTing-Kang Chang t.Fatalf("jwt.NewRawJWTFromJSON({}): %v", err) 248*e7b1675dSTing-Kang Chang } 249*e7b1675dSTing-Kang Chang optsToken, err := jwt.NewRawJWT(&jwt.RawJWTOptions{WithoutExpiration: true}) 250*e7b1675dSTing-Kang Chang if err != nil { 251*e7b1675dSTing-Kang Chang t.Fatalf("NewRawJWT with no claims: %v", err) 252*e7b1675dSTing-Kang Chang } 253*e7b1675dSTing-Kang Chang for _, tc := range []testCase{ 254*e7b1675dSTing-Kang Chang { 255*e7b1675dSTing-Kang Chang tag: "jwt.NewRawJWTFromJSON", 256*e7b1675dSTing-Kang Chang token: jsonToken, 257*e7b1675dSTing-Kang Chang }, 258*e7b1675dSTing-Kang Chang { 259*e7b1675dSTing-Kang Chang tag: "NewRawJWT", 260*e7b1675dSTing-Kang Chang token: optsToken, 261*e7b1675dSTing-Kang Chang }, 262*e7b1675dSTing-Kang Chang } { 263*e7b1675dSTing-Kang Chang if tc.token.HasTypeHeader() { 264*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasTypeHeader() = true, want false") 265*e7b1675dSTing-Kang Chang } 266*e7b1675dSTing-Kang Chang if tc.token.HasAudiences() { 267*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasAudiences() = true, want false") 268*e7b1675dSTing-Kang Chang } 269*e7b1675dSTing-Kang Chang if tc.token.HasSubject() { 270*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasSubject() = true, want false") 271*e7b1675dSTing-Kang Chang } 272*e7b1675dSTing-Kang Chang if tc.token.HasIssuer() { 273*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasIssuer() = true, want false") 274*e7b1675dSTing-Kang Chang } 275*e7b1675dSTing-Kang Chang if tc.token.HasJWTID() { 276*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasJWTID() = true, want false") 277*e7b1675dSTing-Kang Chang } 278*e7b1675dSTing-Kang Chang if tc.token.HasExpiration() { 279*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasExpiration() = true, want false") 280*e7b1675dSTing-Kang Chang } 281*e7b1675dSTing-Kang Chang if tc.token.HasNotBefore() { 282*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasNotBefore() = true, want false") 283*e7b1675dSTing-Kang Chang } 284*e7b1675dSTing-Kang Chang if tc.token.HasIssuedAt() { 285*e7b1675dSTing-Kang Chang t.Errorf("tc.token.HasIssuedAt() = true, want false") 286*e7b1675dSTing-Kang Chang } 287*e7b1675dSTing-Kang Chang if _, err := tc.token.Audiences(); err == nil { 288*e7b1675dSTing-Kang Chang t.Errorf("tc.token.Audiences() err = nil, want error") 289*e7b1675dSTing-Kang Chang } 290*e7b1675dSTing-Kang Chang if _, err := tc.token.Subject(); err == nil { 291*e7b1675dSTing-Kang Chang t.Errorf("tc.token.Subject() err = nil, want error") 292*e7b1675dSTing-Kang Chang } 293*e7b1675dSTing-Kang Chang if _, err := tc.token.Issuer(); err == nil { 294*e7b1675dSTing-Kang Chang t.Errorf("tc.token.Issuer() err = nil, want error") 295*e7b1675dSTing-Kang Chang } 296*e7b1675dSTing-Kang Chang if _, err := tc.token.JWTID(); err == nil { 297*e7b1675dSTing-Kang Chang t.Errorf("tc.token.JWTID() err = nil, want error") 298*e7b1675dSTing-Kang Chang } 299*e7b1675dSTing-Kang Chang if _, err := tc.token.ExpiresAt(); err == nil { 300*e7b1675dSTing-Kang Chang t.Errorf("tc.token.ExpiresAt() err = nil, want error") 301*e7b1675dSTing-Kang Chang } 302*e7b1675dSTing-Kang Chang if _, err := tc.token.IssuedAt(); err == nil { 303*e7b1675dSTing-Kang Chang t.Errorf("tc.token.IssuedAt() err = nil, want error") 304*e7b1675dSTing-Kang Chang } 305*e7b1675dSTing-Kang Chang if _, err := tc.token.NotBefore(); err == nil { 306*e7b1675dSTing-Kang Chang t.Errorf("tc.token.NotBefore() err = nil, want error") 307*e7b1675dSTing-Kang Chang } 308*e7b1675dSTing-Kang Chang if !cmp.Equal(tc.token.CustomClaimNames(), []string{}) { 309*e7b1675dSTing-Kang Chang t.Errorf("tc.token.CustomClaimNames() = %q want %q", tc.token.CustomClaimNames(), []string{}) 310*e7b1675dSTing-Kang Chang } 311*e7b1675dSTing-Kang Chang } 312*e7b1675dSTing-Kang Chang} 313*e7b1675dSTing-Kang Chang 314*e7b1675dSTing-Kang Changfunc TestNewRawJWTLargeValidTimestamps(t *testing.T) { 315*e7b1675dSTing-Kang Chang opts := &jwt.RawJWTOptions{ 316*e7b1675dSTing-Kang Chang TypeHeader: refString("typeHeader"), 317*e7b1675dSTing-Kang Chang ExpiresAt: refTime(253402300799), 318*e7b1675dSTing-Kang Chang NotBefore: refTime(253402300700), 319*e7b1675dSTing-Kang Chang IssuedAt: refTime(253402300000), 320*e7b1675dSTing-Kang Chang } 321*e7b1675dSTing-Kang Chang token, err := jwt.NewRawJWT(opts) 322*e7b1675dSTing-Kang Chang if err != nil { 323*e7b1675dSTing-Kang Chang t.Fatalf("generating RawJWT with valid timestamps (%q, %q, %q): %v", opts.ExpiresAt, opts.NotBefore, opts.IssuedAt, err) 324*e7b1675dSTing-Kang Chang } 325*e7b1675dSTing-Kang Chang expiresAt, err := token.ExpiresAt() 326*e7b1675dSTing-Kang Chang if err != nil { 327*e7b1675dSTing-Kang Chang t.Errorf("tc.token.ExpiresAt() err = %v, want nil", err) 328*e7b1675dSTing-Kang Chang } 329*e7b1675dSTing-Kang Chang if !cmp.Equal(expiresAt, *opts.ExpiresAt) { 330*e7b1675dSTing-Kang Chang t.Errorf("tc.token.ExpiresAt() = %q want %q", expiresAt, *opts.ExpiresAt) 331*e7b1675dSTing-Kang Chang } 332*e7b1675dSTing-Kang Chang 333*e7b1675dSTing-Kang Chang notBefore, err := token.NotBefore() 334*e7b1675dSTing-Kang Chang if err != nil { 335*e7b1675dSTing-Kang Chang t.Errorf("tc.token.NotBefore() err = %v, want nil", err) 336*e7b1675dSTing-Kang Chang } 337*e7b1675dSTing-Kang Chang if !cmp.Equal(notBefore, *opts.NotBefore) { 338*e7b1675dSTing-Kang Chang t.Errorf("tc.token.NotBefore() = %q want %q", notBefore, *opts.NotBefore) 339*e7b1675dSTing-Kang Chang } 340*e7b1675dSTing-Kang Chang 341*e7b1675dSTing-Kang Chang issuedAt, err := token.IssuedAt() 342*e7b1675dSTing-Kang Chang if err != nil { 343*e7b1675dSTing-Kang Chang t.Errorf("tc.token.IssuedAt() err = %v, want nil", err) 344*e7b1675dSTing-Kang Chang } 345*e7b1675dSTing-Kang Chang if !cmp.Equal(issuedAt, *opts.IssuedAt) { 346*e7b1675dSTing-Kang Chang t.Errorf("tc.token.IssuedAt() = %q want %q", issuedAt, *opts.IssuedAt) 347*e7b1675dSTing-Kang Chang } 348*e7b1675dSTing-Kang Chang} 349*e7b1675dSTing-Kang Chang 350*e7b1675dSTing-Kang Changfunc TestNewRawJWTSingleStringAudience(t *testing.T) { 351*e7b1675dSTing-Kang Chang opts := &jwt.RawJWTOptions{ 352*e7b1675dSTing-Kang Chang WithoutExpiration: true, 353*e7b1675dSTing-Kang Chang Audience: refString("tink-aud"), 354*e7b1675dSTing-Kang Chang } 355*e7b1675dSTing-Kang Chang rawJWT, err := jwt.NewRawJWT(opts) 356*e7b1675dSTing-Kang Chang if err != nil { 357*e7b1675dSTing-Kang Chang t.Fatalf("generating RawJWT with a single audience: %v", err) 358*e7b1675dSTing-Kang Chang } 359*e7b1675dSTing-Kang Chang aud, err := rawJWT.Audiences() 360*e7b1675dSTing-Kang Chang if err != nil { 361*e7b1675dSTing-Kang Chang t.Errorf("getting audience from token: %v", err) 362*e7b1675dSTing-Kang Chang } 363*e7b1675dSTing-Kang Chang want := []string{*opts.Audience} 364*e7b1675dSTing-Kang Chang if !cmp.Equal(aud, want) { 365*e7b1675dSTing-Kang Chang t.Errorf("rawJWT.Audiences() = %q, want %q", aud, want) 366*e7b1675dSTing-Kang Chang } 367*e7b1675dSTing-Kang Chang} 368*e7b1675dSTing-Kang Chang 369*e7b1675dSTing-Kang Changfunc TestSingleStringAudienceFromJSON(t *testing.T) { 370*e7b1675dSTing-Kang Chang rawJWT, err := jwt.NewRawJWTFromJSON(nil, []byte(`{"aud": "tink-aud"}`)) 371*e7b1675dSTing-Kang Chang if err != nil { 372*e7b1675dSTing-Kang Chang t.Fatalf("parsing valid RawJWT: %v", err) 373*e7b1675dSTing-Kang Chang } 374*e7b1675dSTing-Kang Chang aud, err := rawJWT.Audiences() 375*e7b1675dSTing-Kang Chang if err != nil { 376*e7b1675dSTing-Kang Chang t.Errorf("getting audience from token: %v", err) 377*e7b1675dSTing-Kang Chang } 378*e7b1675dSTing-Kang Chang want := []string{"tink-aud"} 379*e7b1675dSTing-Kang Chang if !cmp.Equal(aud, want) { 380*e7b1675dSTing-Kang Chang t.Errorf("rawJWT.Audiences() = %q, want %q", aud, want) 381*e7b1675dSTing-Kang Chang } 382*e7b1675dSTing-Kang Chang} 383*e7b1675dSTing-Kang Chang 384*e7b1675dSTing-Kang Changfunc TestNewRawJWTValidationFailures(t *testing.T) { 385*e7b1675dSTing-Kang Chang testCases := []testCase{ 386*e7b1675dSTing-Kang Chang { 387*e7b1675dSTing-Kang Chang tag: "empty jwt.RawJWTOptions options fails", 388*e7b1675dSTing-Kang Chang }, 389*e7b1675dSTing-Kang Chang { 390*e7b1675dSTing-Kang Chang tag: "no ExpiresAt specified and WithoutExpiration = false fails", 391*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 392*e7b1675dSTing-Kang Chang Audiences: []string{"tink-foo"}, 393*e7b1675dSTing-Kang Chang }, 394*e7b1675dSTing-Kang Chang }, 395*e7b1675dSTing-Kang Chang { 396*e7b1675dSTing-Kang Chang tag: "ExpiresAt and WithoutExpiration = true fails", 397*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 398*e7b1675dSTing-Kang Chang Audiences: []string{"tink-foo"}, 399*e7b1675dSTing-Kang Chang ExpiresAt: refTime(validExpiration), 400*e7b1675dSTing-Kang Chang WithoutExpiration: true, 401*e7b1675dSTing-Kang Chang }, 402*e7b1675dSTing-Kang Chang }, 403*e7b1675dSTing-Kang Chang { 404*e7b1675dSTing-Kang Chang tag: "specifying Audenience and Audiences fails", 405*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 406*e7b1675dSTing-Kang Chang Audiences: []string{"tink-foo"}, 407*e7b1675dSTing-Kang Chang Audience: refString("tink-bar"), 408*e7b1675dSTing-Kang Chang WithoutExpiration: true, 409*e7b1675dSTing-Kang Chang }, 410*e7b1675dSTing-Kang Chang }, 411*e7b1675dSTing-Kang Chang { 412*e7b1675dSTing-Kang Chang tag: "empty audiences array fails", 413*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 414*e7b1675dSTing-Kang Chang ExpiresAt: refTime(validExpiration), 415*e7b1675dSTing-Kang Chang Audiences: []string{}, 416*e7b1675dSTing-Kang Chang }, 417*e7b1675dSTing-Kang Chang }, 418*e7b1675dSTing-Kang Chang { 419*e7b1675dSTing-Kang Chang tag: "audiences with invalid UTF-8 string fails", 420*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 421*e7b1675dSTing-Kang Chang WithoutExpiration: true, 422*e7b1675dSTing-Kang Chang Audiences: []string{"valid", invalidUTF8}, 423*e7b1675dSTing-Kang Chang }, 424*e7b1675dSTing-Kang Chang }, 425*e7b1675dSTing-Kang Chang { 426*e7b1675dSTing-Kang Chang tag: "custom claims containing registered subject claims fails", 427*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 428*e7b1675dSTing-Kang Chang Audiences: []string{"tink-foo"}, 429*e7b1675dSTing-Kang Chang ExpiresAt: refTime(validExpiration), 430*e7b1675dSTing-Kang Chang CustomClaims: map[string]interface{}{ 431*e7b1675dSTing-Kang Chang "sub": "overwrite", 432*e7b1675dSTing-Kang Chang }, 433*e7b1675dSTing-Kang Chang }, 434*e7b1675dSTing-Kang Chang }, 435*e7b1675dSTing-Kang Chang { 436*e7b1675dSTing-Kang Chang tag: "custom claims containing registered issuer claims fails", 437*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 438*e7b1675dSTing-Kang Chang Audiences: []string{"tink-foo"}, 439*e7b1675dSTing-Kang Chang ExpiresAt: refTime(validExpiration), 440*e7b1675dSTing-Kang Chang CustomClaims: map[string]interface{}{ 441*e7b1675dSTing-Kang Chang "iss": "overwrite", 442*e7b1675dSTing-Kang Chang }, 443*e7b1675dSTing-Kang Chang }, 444*e7b1675dSTing-Kang Chang }, 445*e7b1675dSTing-Kang Chang { 446*e7b1675dSTing-Kang Chang tag: "custom claims containing registered jwt id claims fails", 447*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 448*e7b1675dSTing-Kang Chang Audiences: []string{"tink-foo"}, 449*e7b1675dSTing-Kang Chang ExpiresAt: refTime(validExpiration), 450*e7b1675dSTing-Kang Chang CustomClaims: map[string]interface{}{ 451*e7b1675dSTing-Kang Chang "jti": "overwrite", 452*e7b1675dSTing-Kang Chang }, 453*e7b1675dSTing-Kang Chang }, 454*e7b1675dSTing-Kang Chang }, 455*e7b1675dSTing-Kang Chang { 456*e7b1675dSTing-Kang Chang tag: "custom claims containing registered expiration claims fails", 457*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 458*e7b1675dSTing-Kang Chang Audiences: []string{"tink-foo"}, 459*e7b1675dSTing-Kang Chang ExpiresAt: refTime(validExpiration), 460*e7b1675dSTing-Kang Chang CustomClaims: map[string]interface{}{ 461*e7b1675dSTing-Kang Chang "exp": "overwrite", 462*e7b1675dSTing-Kang Chang }, 463*e7b1675dSTing-Kang Chang }, 464*e7b1675dSTing-Kang Chang }, 465*e7b1675dSTing-Kang Chang { 466*e7b1675dSTing-Kang Chang tag: "custom claims containing registered audience claims fails", 467*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 468*e7b1675dSTing-Kang Chang Audiences: []string{"tink-foo"}, 469*e7b1675dSTing-Kang Chang WithoutExpiration: true, 470*e7b1675dSTing-Kang Chang CustomClaims: map[string]interface{}{ 471*e7b1675dSTing-Kang Chang "aud": []interface{}{"overwrite"}, 472*e7b1675dSTing-Kang Chang }, 473*e7b1675dSTing-Kang Chang }, 474*e7b1675dSTing-Kang Chang }, 475*e7b1675dSTing-Kang Chang { 476*e7b1675dSTing-Kang Chang tag: "custom claims with non standard JSON types fails", 477*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 478*e7b1675dSTing-Kang Chang Audiences: []string{"tink-foo"}, 479*e7b1675dSTing-Kang Chang ExpiresAt: refTime(validExpiration), 480*e7b1675dSTing-Kang Chang CustomClaims: map[string]interface{}{ 481*e7b1675dSTing-Kang Chang "complex": time.Time{}, 482*e7b1675dSTing-Kang Chang }, 483*e7b1675dSTing-Kang Chang }, 484*e7b1675dSTing-Kang Chang }, 485*e7b1675dSTing-Kang Chang { 486*e7b1675dSTing-Kang Chang tag: "non UTF-8 string on isser claim fails", 487*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 488*e7b1675dSTing-Kang Chang Audiences: []string{"tink-foo"}, 489*e7b1675dSTing-Kang Chang ExpiresAt: refTime(validExpiration), 490*e7b1675dSTing-Kang Chang Issuer: refString(invalidUTF8), 491*e7b1675dSTing-Kang Chang }, 492*e7b1675dSTing-Kang Chang }, 493*e7b1675dSTing-Kang Chang { 494*e7b1675dSTing-Kang Chang tag: "non UTF-8 string on subject claim fails", 495*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 496*e7b1675dSTing-Kang Chang Audiences: []string{"tink-foo"}, 497*e7b1675dSTing-Kang Chang WithoutExpiration: true, 498*e7b1675dSTing-Kang Chang Subject: refString(invalidUTF8), 499*e7b1675dSTing-Kang Chang }, 500*e7b1675dSTing-Kang Chang }, 501*e7b1675dSTing-Kang Chang { 502*e7b1675dSTing-Kang Chang tag: "non UTF-8 string on JWT ID claim fails", 503*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 504*e7b1675dSTing-Kang Chang Audiences: []string{"tink-foo"}, 505*e7b1675dSTing-Kang Chang WithoutExpiration: true, 506*e7b1675dSTing-Kang Chang JWTID: refString(invalidUTF8), 507*e7b1675dSTing-Kang Chang }, 508*e7b1675dSTing-Kang Chang }, 509*e7b1675dSTing-Kang Chang { 510*e7b1675dSTing-Kang Chang tag: "non UTF-8 string on custom claim fails", 511*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 512*e7b1675dSTing-Kang Chang Audiences: []string{"tink-foo"}, 513*e7b1675dSTing-Kang Chang Issuer: refString("ise-testing"), 514*e7b1675dSTing-Kang Chang ExpiresAt: refTime(validExpiration), 515*e7b1675dSTing-Kang Chang CustomClaims: map[string]interface{}{ 516*e7b1675dSTing-Kang Chang "esoteric": invalidUTF8, 517*e7b1675dSTing-Kang Chang }, 518*e7b1675dSTing-Kang Chang }, 519*e7b1675dSTing-Kang Chang }, 520*e7b1675dSTing-Kang Chang { 521*e7b1675dSTing-Kang Chang tag: "issued at timestamp greater than valid JWT max time fails", 522*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 523*e7b1675dSTing-Kang Chang Audiences: []string{"tink-foo"}, 524*e7b1675dSTing-Kang Chang ExpiresAt: refTime(validExpiration), 525*e7b1675dSTing-Kang Chang IssuedAt: refTime(253402300800), 526*e7b1675dSTing-Kang Chang }, 527*e7b1675dSTing-Kang Chang }, 528*e7b1675dSTing-Kang Chang { 529*e7b1675dSTing-Kang Chang tag: "expires at timestamp greater than valid JWT max time fails", 530*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 531*e7b1675dSTing-Kang Chang Audiences: []string{"tink-foo"}, 532*e7b1675dSTing-Kang Chang ExpiresAt: refTime(253402300800), 533*e7b1675dSTing-Kang Chang }, 534*e7b1675dSTing-Kang Chang }, 535*e7b1675dSTing-Kang Chang { 536*e7b1675dSTing-Kang Chang tag: "not before timestamp smaller than valid JWT min time fails", 537*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 538*e7b1675dSTing-Kang Chang Audiences: []string{"tink-foo"}, 539*e7b1675dSTing-Kang Chang ExpiresAt: refTime(validExpiration), 540*e7b1675dSTing-Kang Chang NotBefore: refTime(-5), 541*e7b1675dSTing-Kang Chang }, 542*e7b1675dSTing-Kang Chang }, 543*e7b1675dSTing-Kang Chang } 544*e7b1675dSTing-Kang Chang for _, tc := range testCases { 545*e7b1675dSTing-Kang Chang t.Run(tc.tag, func(t *testing.T) { 546*e7b1675dSTing-Kang Chang _, err := jwt.NewRawJWT(tc.opts) 547*e7b1675dSTing-Kang Chang if err == nil { 548*e7b1675dSTing-Kang Chang t.Errorf("expected error instead got nil") 549*e7b1675dSTing-Kang Chang } 550*e7b1675dSTing-Kang Chang }) 551*e7b1675dSTing-Kang Chang } 552*e7b1675dSTing-Kang Chang} 553*e7b1675dSTing-Kang Chang 554*e7b1675dSTing-Kang Changfunc TestJSONPayload(t *testing.T) { 555*e7b1675dSTing-Kang Chang for _, tc := range []testCase{ 556*e7b1675dSTing-Kang Chang { 557*e7b1675dSTing-Kang Chang tag: "subject", 558*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 559*e7b1675dSTing-Kang Chang WithoutExpiration: true, 560*e7b1675dSTing-Kang Chang Subject: refString("tink-subject"), 561*e7b1675dSTing-Kang Chang }, 562*e7b1675dSTing-Kang Chang json: `{"sub":"tink-subject"}`, 563*e7b1675dSTing-Kang Chang }, 564*e7b1675dSTing-Kang Chang { 565*e7b1675dSTing-Kang Chang tag: "audience list", 566*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 567*e7b1675dSTing-Kang Chang WithoutExpiration: true, 568*e7b1675dSTing-Kang Chang Audiences: []string{"one"}, 569*e7b1675dSTing-Kang Chang }, 570*e7b1675dSTing-Kang Chang json: `{"aud":["one"]}`, 571*e7b1675dSTing-Kang Chang }, 572*e7b1675dSTing-Kang Chang { 573*e7b1675dSTing-Kang Chang tag: "audience string", 574*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 575*e7b1675dSTing-Kang Chang WithoutExpiration: true, 576*e7b1675dSTing-Kang Chang Audience: refString("one"), 577*e7b1675dSTing-Kang Chang }, 578*e7b1675dSTing-Kang Chang json: `{"aud":"one"}`, 579*e7b1675dSTing-Kang Chang }, 580*e7b1675dSTing-Kang Chang { 581*e7b1675dSTing-Kang Chang tag: "issuer", 582*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 583*e7b1675dSTing-Kang Chang WithoutExpiration: true, 584*e7b1675dSTing-Kang Chang Issuer: refString("tink-test"), 585*e7b1675dSTing-Kang Chang }, 586*e7b1675dSTing-Kang Chang json: `{"iss":"tink-test"}`, 587*e7b1675dSTing-Kang Chang }, 588*e7b1675dSTing-Kang Chang { 589*e7b1675dSTing-Kang Chang tag: "jwt id", 590*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 591*e7b1675dSTing-Kang Chang WithoutExpiration: true, 592*e7b1675dSTing-Kang Chang JWTID: refString("tink-id"), 593*e7b1675dSTing-Kang Chang }, 594*e7b1675dSTing-Kang Chang json: `{"jti":"tink-id"}`, 595*e7b1675dSTing-Kang Chang }, 596*e7b1675dSTing-Kang Chang { 597*e7b1675dSTing-Kang Chang tag: "issued at", 598*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 599*e7b1675dSTing-Kang Chang WithoutExpiration: true, 600*e7b1675dSTing-Kang Chang IssuedAt: refTime(78324), 601*e7b1675dSTing-Kang Chang }, 602*e7b1675dSTing-Kang Chang json: `{"iat":78324}`, 603*e7b1675dSTing-Kang Chang }, 604*e7b1675dSTing-Kang Chang { 605*e7b1675dSTing-Kang Chang tag: "not before", 606*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 607*e7b1675dSTing-Kang Chang WithoutExpiration: true, 608*e7b1675dSTing-Kang Chang NotBefore: refTime(78324), 609*e7b1675dSTing-Kang Chang }, 610*e7b1675dSTing-Kang Chang json: `{"nbf":78324}`, 611*e7b1675dSTing-Kang Chang }, 612*e7b1675dSTing-Kang Chang { 613*e7b1675dSTing-Kang Chang tag: "expiration", 614*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 615*e7b1675dSTing-Kang Chang ExpiresAt: refTime(78324), 616*e7b1675dSTing-Kang Chang }, 617*e7b1675dSTing-Kang Chang json: `{"exp":78324}`, 618*e7b1675dSTing-Kang Chang }, 619*e7b1675dSTing-Kang Chang { 620*e7b1675dSTing-Kang Chang tag: "integer", 621*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 622*e7b1675dSTing-Kang Chang WithoutExpiration: true, 623*e7b1675dSTing-Kang Chang CustomClaims: map[string]interface{}{ 624*e7b1675dSTing-Kang Chang "num": 1, 625*e7b1675dSTing-Kang Chang }, 626*e7b1675dSTing-Kang Chang }, 627*e7b1675dSTing-Kang Chang json: `{"num":1}`, 628*e7b1675dSTing-Kang Chang }, 629*e7b1675dSTing-Kang Chang { 630*e7b1675dSTing-Kang Chang tag: "custom-claim", 631*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 632*e7b1675dSTing-Kang Chang WithoutExpiration: true, 633*e7b1675dSTing-Kang Chang CustomClaims: map[string]interface{}{ 634*e7b1675dSTing-Kang Chang "cust": []interface{}{map[string]interface{}{"key": "val"}}, 635*e7b1675dSTing-Kang Chang }, 636*e7b1675dSTing-Kang Chang }, 637*e7b1675dSTing-Kang Chang json: `{"cust":[{"key":"val"}]}`, 638*e7b1675dSTing-Kang Chang }, 639*e7b1675dSTing-Kang Chang { 640*e7b1675dSTing-Kang Chang tag: "no claims", 641*e7b1675dSTing-Kang Chang opts: &jwt.RawJWTOptions{ 642*e7b1675dSTing-Kang Chang WithoutExpiration: true, 643*e7b1675dSTing-Kang Chang }, 644*e7b1675dSTing-Kang Chang json: `{}`, 645*e7b1675dSTing-Kang Chang }, 646*e7b1675dSTing-Kang Chang } { 647*e7b1675dSTing-Kang Chang t.Run(tc.tag, func(t *testing.T) { 648*e7b1675dSTing-Kang Chang token, err := jwt.NewRawJWT(tc.opts) 649*e7b1675dSTing-Kang Chang if err != nil { 650*e7b1675dSTing-Kang Chang t.Errorf("generating valid RawJWT: %v", err) 651*e7b1675dSTing-Kang Chang } 652*e7b1675dSTing-Kang Chang j, err := token.JSONPayload() 653*e7b1675dSTing-Kang Chang if err != nil { 654*e7b1675dSTing-Kang Chang t.Errorf("calling JSONPayload() on rawJWT: %v", err) 655*e7b1675dSTing-Kang Chang } 656*e7b1675dSTing-Kang Chang if !cmp.Equal(string(j), tc.json) { 657*e7b1675dSTing-Kang Chang t.Fatalf("JSONPayload output got %v, expected %v", string(j), tc.json) 658*e7b1675dSTing-Kang Chang } 659*e7b1675dSTing-Kang Chang }) 660*e7b1675dSTing-Kang Chang } 661*e7b1675dSTing-Kang Chang} 662*e7b1675dSTing-Kang Chang 663*e7b1675dSTing-Kang Changfunc TestFromJSONValidationFailures(t *testing.T) { 664*e7b1675dSTing-Kang Chang testCases := []testCase{ 665*e7b1675dSTing-Kang Chang { 666*e7b1675dSTing-Kang Chang tag: "json with empty audience", 667*e7b1675dSTing-Kang Chang json: `{"sub": "tink", "aud": []}`, 668*e7b1675dSTing-Kang Chang }, 669*e7b1675dSTing-Kang Chang { 670*e7b1675dSTing-Kang Chang tag: "json with audience of wrong type", 671*e7b1675dSTing-Kang Chang json: `{"aud": 5}`, 672*e7b1675dSTing-Kang Chang }, 673*e7b1675dSTing-Kang Chang { 674*e7b1675dSTing-Kang Chang tag: "json with audiences of wrong type", 675*e7b1675dSTing-Kang Chang json: `{"aud": ["one", null]}`, 676*e7b1675dSTing-Kang Chang }, 677*e7b1675dSTing-Kang Chang { 678*e7b1675dSTing-Kang Chang tag: "json with registered claim with wrong type", 679*e7b1675dSTing-Kang Chang json: `{"sub": 1}`, 680*e7b1675dSTing-Kang Chang }, 681*e7b1675dSTing-Kang Chang { 682*e7b1675dSTing-Kang Chang tag: "json with non UTF-8 string on subject claim fails", 683*e7b1675dSTing-Kang Chang json: `{"sub": "\xF4\x7F\xBF\xBF"}`, 684*e7b1675dSTing-Kang Chang }, 685*e7b1675dSTing-Kang Chang { 686*e7b1675dSTing-Kang Chang tag: "json with non UTF-8 string on issuer claim fails", 687*e7b1675dSTing-Kang Chang json: `{"iss": "\xF4\x7F\xBF\xBF"}`, 688*e7b1675dSTing-Kang Chang }, 689*e7b1675dSTing-Kang Chang { 690*e7b1675dSTing-Kang Chang tag: "json with non UTF-8 string on jwt id claim fails", 691*e7b1675dSTing-Kang Chang json: `{"jti": "\xF4\x7F\xBF\xBF"}`, 692*e7b1675dSTing-Kang Chang }, 693*e7b1675dSTing-Kang Chang { 694*e7b1675dSTing-Kang Chang tag: "json with `not before` timestamp claim greater than valid JWT max time fails", 695*e7b1675dSTing-Kang Chang json: `{"nbf": 253402301799}`, 696*e7b1675dSTing-Kang Chang }, 697*e7b1675dSTing-Kang Chang { 698*e7b1675dSTing-Kang Chang tag: "json with `issued at` timestamp claim greater than valid JWT max time fails", 699*e7b1675dSTing-Kang Chang json: `{"iat": 253402301799}`, 700*e7b1675dSTing-Kang Chang }, 701*e7b1675dSTing-Kang Chang { 702*e7b1675dSTing-Kang Chang tag: "json with `expiration` timestamp claim greater than valid JWT max time fails", 703*e7b1675dSTing-Kang Chang json: `{"exp": 253402301799}`, 704*e7b1675dSTing-Kang Chang }, 705*e7b1675dSTing-Kang Chang { 706*e7b1675dSTing-Kang Chang tag: "json with `not before` timestamp claim smaller than valid JWT min time fails", 707*e7b1675dSTing-Kang Chang json: `{"nbf": -4}`, 708*e7b1675dSTing-Kang Chang }, 709*e7b1675dSTing-Kang Chang { 710*e7b1675dSTing-Kang Chang tag: "json with `issued at` timestamp claim smaller than valid JWT min time fails", 711*e7b1675dSTing-Kang Chang json: `{"iat": -4}`, 712*e7b1675dSTing-Kang Chang }, 713*e7b1675dSTing-Kang Chang { 714*e7b1675dSTing-Kang Chang tag: "json with `expiration` timestamp claim smaller than valid JWT min time fails", 715*e7b1675dSTing-Kang Chang json: `{"exp": -4}`, 716*e7b1675dSTing-Kang Chang }, 717*e7b1675dSTing-Kang Chang { 718*e7b1675dSTing-Kang Chang tag: "json with `not before` claim of non numeric type fails", 719*e7b1675dSTing-Kang Chang json: `{"nbf": "invalid"}`, 720*e7b1675dSTing-Kang Chang }, 721*e7b1675dSTing-Kang Chang { 722*e7b1675dSTing-Kang Chang tag: "json with `issued at` claim of non numeric type fails", 723*e7b1675dSTing-Kang Chang json: `{"iat": "invalid"}`, 724*e7b1675dSTing-Kang Chang }, 725*e7b1675dSTing-Kang Chang { 726*e7b1675dSTing-Kang Chang tag: "json with `expiration` claim of non numeric type fails", 727*e7b1675dSTing-Kang Chang json: `{"exp": "invalid"}`, 728*e7b1675dSTing-Kang Chang }, 729*e7b1675dSTing-Kang Chang } 730*e7b1675dSTing-Kang Chang 731*e7b1675dSTing-Kang Chang for _, tc := range testCases { 732*e7b1675dSTing-Kang Chang t.Run(tc.tag, func(t *testing.T) { 733*e7b1675dSTing-Kang Chang if _, err := jwt.NewRawJWTFromJSON(nil, []byte(tc.json)); err == nil { 734*e7b1675dSTing-Kang Chang t.Errorf("expected error instead got nil") 735*e7b1675dSTing-Kang Chang } 736*e7b1675dSTing-Kang Chang }) 737*e7b1675dSTing-Kang Chang } 738*e7b1675dSTing-Kang Chang} 739*e7b1675dSTing-Kang Chang 740*e7b1675dSTing-Kang Changfunc TestHasCustomClaimsOfKind(t *testing.T) { 741*e7b1675dSTing-Kang Chang opts := &jwt.RawJWTOptions{ 742*e7b1675dSTing-Kang Chang TypeHeader: refString("typeHeader"), 743*e7b1675dSTing-Kang Chang WithoutExpiration: true, 744*e7b1675dSTing-Kang Chang CustomClaims: map[string]interface{}{ 745*e7b1675dSTing-Kang Chang "cc-num": 1.67, 746*e7b1675dSTing-Kang Chang "cc-bool": false, 747*e7b1675dSTing-Kang Chang "cc-nil": nil, 748*e7b1675dSTing-Kang Chang "cc-list": []interface{}{1.0, 2.0, 3.0}, 749*e7b1675dSTing-Kang Chang "cc-string": "cc-val", 750*e7b1675dSTing-Kang Chang "cc-object": map[string]interface{}{ 751*e7b1675dSTing-Kang Chang "nested-cc-num": 5.5, 752*e7b1675dSTing-Kang Chang }, 753*e7b1675dSTing-Kang Chang }, 754*e7b1675dSTing-Kang Chang } 755*e7b1675dSTing-Kang Chang token, err := jwt.NewRawJWT(opts) 756*e7b1675dSTing-Kang Chang if err != nil { 757*e7b1675dSTing-Kang Chang t.Fatalf("generating valid RawJWT: %v", err) 758*e7b1675dSTing-Kang Chang } 759*e7b1675dSTing-Kang Chang if token.HasBooleanClaim("cc-num") { 760*e7b1675dSTing-Kang Chang t.Errorf("custom number claim 'cc-num' should return false when queried for another type") 761*e7b1675dSTing-Kang Chang } 762*e7b1675dSTing-Kang Chang if token.HasNullClaim("cc-bool") { 763*e7b1675dSTing-Kang Chang t.Errorf("custom boolean claim 'cc-bool' should return false when queried for another type") 764*e7b1675dSTing-Kang Chang } 765*e7b1675dSTing-Kang Chang if token.HasNumberClaim("cc-bool") { 766*e7b1675dSTing-Kang Chang t.Errorf("custom boolean claim 'cc-bool' should return false when queried for another type") 767*e7b1675dSTing-Kang Chang } 768*e7b1675dSTing-Kang Chang if token.HasStringClaim("cc-bool") { 769*e7b1675dSTing-Kang Chang t.Errorf("custom boolean claim 'cc-bool' should return false when queried for another type") 770*e7b1675dSTing-Kang Chang } 771*e7b1675dSTing-Kang Chang if token.HasArrayClaim("cc-bool") { 772*e7b1675dSTing-Kang Chang t.Errorf("custom boolean claim 'cc-bool' should return false when queried for another type") 773*e7b1675dSTing-Kang Chang } 774*e7b1675dSTing-Kang Chang if token.HasObjectClaim("cc-bool") { 775*e7b1675dSTing-Kang Chang t.Errorf("custom boolean claim 'cc-bool' should return false when queried for another type") 776*e7b1675dSTing-Kang Chang } 777*e7b1675dSTing-Kang Chang} 778*e7b1675dSTing-Kang Chang 779*e7b1675dSTing-Kang Changfunc TestGettingRegisteredClaimsThroughCustomFails(t *testing.T) { 780*e7b1675dSTing-Kang Chang opts := &jwt.RawJWTOptions{ 781*e7b1675dSTing-Kang Chang TypeHeader: refString("typeHeader"), 782*e7b1675dSTing-Kang Chang Subject: refString("tink-test-subject"), 783*e7b1675dSTing-Kang Chang Issuer: refString("tink-test-issuer"), 784*e7b1675dSTing-Kang Chang JWTID: refString("tink-jwt-id-1"), 785*e7b1675dSTing-Kang Chang Audiences: []string{"aud-1", "aud-2"}, 786*e7b1675dSTing-Kang Chang ExpiresAt: refTime(validExpiration), 787*e7b1675dSTing-Kang Chang IssuedAt: refTime(validExpiration - 100), 788*e7b1675dSTing-Kang Chang NotBefore: refTime(validExpiration - 50), 789*e7b1675dSTing-Kang Chang } 790*e7b1675dSTing-Kang Chang token, err := jwt.NewRawJWT(opts) 791*e7b1675dSTing-Kang Chang if err != nil { 792*e7b1675dSTing-Kang Chang t.Fatalf("generating valid RawJWT: %v", err) 793*e7b1675dSTing-Kang Chang } 794*e7b1675dSTing-Kang Chang if !cmp.Equal(token.CustomClaimNames(), []string{}) { 795*e7b1675dSTing-Kang Chang t.Errorf("tc.token.CustomClaimNames() = %q want %q", token.CustomClaimNames(), []string{}) 796*e7b1675dSTing-Kang Chang } 797*e7b1675dSTing-Kang Chang for _, c := range []string{"sub", "iss", "aud", "nbf", "exp", "iat", "jti"} { 798*e7b1675dSTing-Kang Chang if token.HasNullClaim(c) { 799*e7b1675dSTing-Kang Chang t.Errorf("registered '%q' claim should return false when calling HasNullClaim", c) 800*e7b1675dSTing-Kang Chang } 801*e7b1675dSTing-Kang Chang if token.HasBooleanClaim(c) { 802*e7b1675dSTing-Kang Chang t.Errorf("registered '%q' claim should return false when calling HasBooleanClaim", c) 803*e7b1675dSTing-Kang Chang } 804*e7b1675dSTing-Kang Chang if _, err := token.BooleanClaim(c); err == nil { 805*e7b1675dSTing-Kang Chang t.Errorf("expected error when calling token.BoolClaim(%q) instead got nil", c) 806*e7b1675dSTing-Kang Chang } 807*e7b1675dSTing-Kang Chang if token.HasNumberClaim(c) { 808*e7b1675dSTing-Kang Chang t.Errorf("registered '%q' claim should return false when calling HasNumberClaim", c) 809*e7b1675dSTing-Kang Chang } 810*e7b1675dSTing-Kang Chang if _, err := token.NumberClaim(c); err == nil { 811*e7b1675dSTing-Kang Chang t.Errorf("expected error when calling token.NumberClaim(%q) instead got nil", c) 812*e7b1675dSTing-Kang Chang } 813*e7b1675dSTing-Kang Chang if token.HasStringClaim(c) { 814*e7b1675dSTing-Kang Chang t.Errorf("registered '%q' claim should return false when calling HasStringClaim", c) 815*e7b1675dSTing-Kang Chang } 816*e7b1675dSTing-Kang Chang if _, err := token.StringClaim(c); err == nil { 817*e7b1675dSTing-Kang Chang t.Errorf("expected error when calling token.StringClaim(%q) instead got nil", c) 818*e7b1675dSTing-Kang Chang } 819*e7b1675dSTing-Kang Chang if token.HasArrayClaim(c) { 820*e7b1675dSTing-Kang Chang t.Errorf("registered '%q' claim should return false when calling HasArrayClaim", c) 821*e7b1675dSTing-Kang Chang } 822*e7b1675dSTing-Kang Chang if _, err := token.ArrayClaim(c); err == nil { 823*e7b1675dSTing-Kang Chang t.Errorf("expected error when calling token.ListClaim(%q) instead got nil", c) 824*e7b1675dSTing-Kang Chang } 825*e7b1675dSTing-Kang Chang if token.HasObjectClaim(c) { 826*e7b1675dSTing-Kang Chang t.Errorf("registered '%q' claim should return false when calling HasObjectClaim", c) 827*e7b1675dSTing-Kang Chang } 828*e7b1675dSTing-Kang Chang if _, err := token.ObjectClaim(c); err == nil { 829*e7b1675dSTing-Kang Chang t.Errorf("expected error when calling token.JSONClaim(%q) instead got nil", c) 830*e7b1675dSTing-Kang Chang } 831*e7b1675dSTing-Kang Chang } 832*e7b1675dSTing-Kang Chang} 833