1// Copyright 2022 Google LLC 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14// 15//////////////////////////////////////////////////////////////////////////////// 16 17package jwt 18 19import ( 20 "encoding/base64" 21 "fmt" 22 "testing" 23 "time" 24 25 "github.com/google/tink/go/mac/subtle" 26) 27 28func TestNewMACwithNilMACFails(t *testing.T) { 29 if _, err := newMACWithKID(nil, "", nil); err == nil { 30 t.Errorf("NewMACWithKID(nil, '', nil) err = nil, want error") 31 } 32} 33 34func createMACwithKID(customKID *string) (*macWithKID, error) { 35 // https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.1.1 36 key, err := base64.URLEncoding.WithPadding(base64.NoPadding).DecodeString("AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow") 37 if err != nil { 38 return nil, fmt.Errorf("failed parsing test key: %v", err) 39 } 40 mac, err := subtle.NewHMAC("SHA256", key, 32) 41 if err != nil { 42 return nil, err 43 } 44 return newMACWithKID(mac, "HS256", customKID) 45} 46 47func TestCreateAndValidateToken(t *testing.T) { 48 m, err := createMACwithKID(nil) 49 if err != nil { 50 t.Fatalf("creating JWTMACwithKID primitive: %v", err) 51 } 52 rawOpts := &RawJWTOptions{ 53 TypeHeader: refString("typeHeader"), 54 JWTID: refString("123"), 55 WithoutExpiration: true, 56 } 57 rawJWT, err := NewRawJWT(rawOpts) 58 if err != nil { 59 t.Errorf("NewRawJWT() err = %v, want nil", err) 60 } 61 compact, err := m.ComputeMACAndEncodeWithKID(rawJWT, nil) 62 if err != nil { 63 t.Errorf("m.ComputeMACAndEncodeWithKID err = %v, want nil", err) 64 } 65 validatorOps := &ValidatorOpts{ 66 ExpectedTypeHeader: refString("typeHeader"), 67 AllowMissingExpiration: true, 68 } 69 validator, err := NewValidator(validatorOps) 70 if err != nil { 71 t.Errorf("NewValidator err = %v, want nil", err) 72 } 73 verifiedJWT, err := m.VerifyMACAndDecodeWithKID(compact, validator, nil) 74 if err != nil { 75 t.Errorf("m.VerifyMACAndDecodeWithKID() err = %v, want nil", err) 76 } 77 typeHeader, err := verifiedJWT.TypeHeader() 78 if err != nil { 79 t.Errorf("verifiedJWT.TypeHeader() err = %v, want nil", err) 80 } 81 if typeHeader != "typeHeader" { 82 t.Errorf("verifiedJWT.TypeHeader() = %q, want 'typeHeader'", typeHeader) 83 } 84 jwtID, err := verifiedJWT.JWTID() 85 if err != nil { 86 t.Errorf("verifiedJWT.JWTID() err = %v, want nil", err) 87 } 88 if jwtID != "123" { 89 t.Errorf("verifiedJWT.JWTID() = %q, want '123'", jwtID) 90 } 91 92 validatorOps = &ValidatorOpts{ 93 ExpectedTypeHeader: refString("notTypeHeader"), 94 AllowMissingExpiration: true, 95 } 96 validator, err = NewValidator(validatorOps) 97 if err != nil { 98 t.Errorf("NewValidator err = %v, want nil", err) 99 } 100 if _, err := m.VerifyMACAndDecodeWithKID(compact, validator, nil); err == nil { 101 t.Errorf("m.VerifyMACAndDecodeWithKID() err = nil, want error") 102 } 103} 104 105func TestCreateAndValidateTokenWithKID(t *testing.T) { 106 m, err := createMACwithKID(nil) 107 if err != nil { 108 t.Fatalf("creating JWTMACwithKID primitive: %v", err) 109 } 110 rawOpts := &RawJWTOptions{ 111 TypeHeader: refString("typeHeader"), 112 JWTID: refString("123"), 113 WithoutExpiration: true, 114 } 115 rawJWT, err := NewRawJWT(rawOpts) 116 if err != nil { 117 t.Errorf("NewRawJWT() err = %v, want nil", err) 118 } 119 compact, err := m.ComputeMACAndEncodeWithKID(rawJWT, refString("kid-123")) 120 if err != nil { 121 t.Errorf("m.ComputeMACAndEncodeWithKID err = %v, want nil", err) 122 } 123 opts := &ValidatorOpts{ 124 ExpectedTypeHeader: refString("typeHeader"), 125 AllowMissingExpiration: true, 126 } 127 validator, err := NewValidator(opts) 128 if err != nil { 129 t.Fatalf("creating JWT validator, NewValidator: %v", err) 130 } 131 verifiedJWT, err := m.VerifyMACAndDecodeWithKID(compact, validator, refString("kid-123")) 132 if err != nil { 133 t.Errorf("m.VerifyMACAndDecodeWithKID(kid = kid-123) err = %v, want nil", err) 134 } 135 136 typeHeader, err := verifiedJWT.TypeHeader() 137 if err != nil { 138 t.Errorf("verifiedJWT.TypeHeader() err = %v, want nil", err) 139 } 140 if typeHeader != *rawOpts.TypeHeader { 141 t.Errorf("verifiedJWT.TypeHeader() = %q, want %q", typeHeader, *rawOpts.TypeHeader) 142 } 143 jwtID, err := verifiedJWT.JWTID() 144 if err != nil { 145 t.Errorf("verifiedJWT.JWTID() err = %v, want nil", err) 146 } 147 if jwtID != *rawOpts.JWTID { 148 t.Errorf("verifiedJWT.JWTID() = %q, want %q", jwtID, *rawOpts.JWTID) 149 } 150 151 if _, err := m.VerifyMACAndDecodeWithKID(compact, validator, nil); err != nil { 152 t.Errorf("m.VerifyMACAndDecodeWithKID(kid = nil) err = %v, want nil", err) 153 } 154 if _, err := m.VerifyMACAndDecodeWithKID(compact, validator, refString("other-kid")); err == nil { 155 t.Errorf("m.VerifyMACAndDecodeWithKID(kid = 'other-kid') err = nil, want error") 156 } 157} 158 159func TestValidateFixedToken(t *testing.T) { 160 // Key and Token are examples from: https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.1.1 161 compact := "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk" 162 m, err := createMACwithKID(nil) 163 if err != nil { 164 t.Fatalf("creating JWTMACwithKID primitive: %v", err) 165 } 166 opts := &ValidatorOpts{ 167 ExpectedTypeHeader: refString("JWT"), 168 ExpectedIssuer: refString("joe"), 169 FixedNow: time.Unix(12345, 0), 170 } 171 pastValidator, err := NewValidator(opts) 172 if err != nil { 173 t.Fatalf("creating JWTValidator: %v", err) 174 } 175 // verification succeeds because token was valid valid on January 1, 1970 UTC. 176 verifiedJWT, err := m.VerifyMACAndDecodeWithKID(compact, pastValidator, nil) 177 if err != nil { 178 t.Fatalf("m.VerifyMACAndDecodeWithKID() err = %v, want nil", err) 179 } 180 typeHeader, err := verifiedJWT.TypeHeader() 181 if err != nil { 182 t.Errorf("verifiedJWT.TypeHeader() err = %v, want nil", err) 183 } 184 if typeHeader != *opts.ExpectedTypeHeader { 185 t.Errorf("verifiedJWT.TypeHeader() = %q, want %q", typeHeader, *opts.ExpectedTypeHeader) 186 } 187 issuer, err := verifiedJWT.Issuer() 188 if err != nil { 189 t.Errorf("verifiedJWT.Issuer() err = %v, want nil", err) 190 } 191 if issuer != *opts.ExpectedIssuer { 192 t.Errorf("verifiedJWT.Issuer() = %q, want %q", issuer, *opts.ExpectedIssuer) 193 } 194 boolClaim, err := verifiedJWT.BooleanClaim("http://example.com/is_root") 195 if err != nil { 196 t.Errorf("verifiedJWT.BooleanClaim('http://example.com/is_root') err = %v, want nil", err) 197 } 198 if boolClaim != true { 199 t.Errorf("verifiedJWT.BooleanClaim('http://example.com/is_root') = %q, want true", issuer) 200 } 201 202 // expired token fails verification 203 opts.FixedNow = time.Now() 204 presentValidator, err := NewValidator(opts) 205 if err != nil { 206 t.Fatalf("creating JWTValidator: %v", err) 207 } 208 _, err = m.VerifyMACAndDecodeWithKID(compact, presentValidator, nil) 209 if err == nil { 210 t.Fatalf("m.VerifyMACAndDecodeWithKID() with expired token err = nil, want error") 211 } 212 if !IsExpirationErr(err) { 213 t.Fatalf("IsExpirationErr(err) for err = %q is false, want true", err) 214 } 215 216 // tampered token verification fails 217 tamperedCompact := "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXx" 218 if _, err := m.VerifyMACAndDecodeWithKID(tamperedCompact, pastValidator, nil); err == nil { 219 t.Fatalf("m.VerifyMACAndDecodeWithKID() with expired tampered token err = nil, want error") 220 } 221} 222 223func TestInvalidTokens(t *testing.T) { 224 m, err := createMACwithKID(nil) 225 if err != nil { 226 t.Fatalf("creating JWTMACwithKID primitive: %v", err) 227 } 228 validator, err := NewValidator(&ValidatorOpts{}) 229 if err != nil { 230 t.Fatalf("creating JWTValidator: %v", err) 231 } 232 for _, compact := range []string{ 233 "eyJhbGciOiJIUzI1NiJ9.e30.abc.", 234 "eyJhbGciOiJIUzI1NiJ9?.e30.abc", 235 "eyJhbGciOiJIUzI1NiJ9.e30?.abc", 236 "eyJhbGciOiJIUzI1NiJ9.e30.abc?", 237 "eyJhbGciOiJIUzI1NiJ9.e30", 238 "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOi&Jqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", 239 } { 240 if _, err := m.VerifyMACAndDecodeWithKID(compact, validator, nil); err == nil { 241 t.Errorf("m.VerifyMACAndDecodeWithKID(%q) err = nil, want error", compact) 242 } 243 } 244} 245 246func TestCustomKIDAndTinkPrefixKeyFail(t *testing.T) { 247 m, err := createMACwithKID(refString("custom-kid")) 248 if err != nil { 249 t.Fatalf("creating JWTMACwithKID primitive: %v", err) 250 } 251 rawJWT, err := NewRawJWT(&RawJWTOptions{WithoutExpiration: true}) 252 if err != nil { 253 t.Fatalf("NewRawJWT() err = %v, want nil", err) 254 } 255 if _, err := m.ComputeMACAndEncodeWithKID(rawJWT, refString("tink-kid")); err == nil { 256 t.Errorf("specifying kid when primitive contains kid to ComputeMACAndEncodeWithKID() err = nil, want error") 257 } 258} 259