1 package openid_test
2
3 import (
4 "context"
5 "fmt"
6 "strconv"
7 "testing"
8 "time"
9
10 "github.com/lestrrat-go/jwx/internal/json"
11 "github.com/lestrrat-go/jwx/internal/jwxtest"
12
13 "github.com/lestrrat-go/jwx/jwa"
14 "github.com/lestrrat-go/jwx/jwt"
15 "github.com/lestrrat-go/jwx/jwt/internal/types"
16 "github.com/lestrrat-go/jwx/jwt/openid"
17 "github.com/stretchr/testify/assert"
18 )
19
20 const aLongLongTimeAgo = 233431200
21 const aLongLongTimeAgoString = "233431200"
22 const (
23 tokenTime = 233431200
24 )
25
26 var expectedTokenTime = time.Unix(tokenTime, 0).UTC()
27
28 func testStockAddressClaim(t *testing.T, x *openid.AddressClaim) {
29 t.Helper()
30 if !assert.NotNil(t, x) {
31 return
32 }
33
34 tests := []struct {
35 Accessor func() string
36 KeyName string
37 Value string
38 }{
39 {
40 Accessor: x.Formatted,
41 KeyName: openid.AddressFormattedKey,
42 Value: "〒105-0011 東京都港区芝公園4丁目2−8",
43 },
44 {
45 Accessor: x.Country,
46 KeyName: openid.AddressCountryKey,
47 Value: "日本",
48 },
49 {
50 Accessor: x.Region,
51 KeyName: openid.AddressRegionKey,
52 Value: "東京都",
53 },
54 {
55 Accessor: x.Locality,
56 KeyName: openid.AddressLocalityKey,
57 Value: "港区",
58 },
59 {
60 Accessor: x.StreetAddress,
61 KeyName: openid.AddressStreetAddressKey,
62 Value: "芝公園4丁目2−8",
63 },
64 {
65 Accessor: x.PostalCode,
66 KeyName: openid.AddressPostalCodeKey,
67 Value: "105-0011",
68 },
69 }
70
71 for _, tc := range tests {
72 tc := tc
73 t.Run(tc.KeyName, func(t *testing.T) {
74 t.Run("Accessor", func(t *testing.T) {
75 if !assert.Equal(t, tc.Value, tc.Accessor(), "values should match") {
76 return
77 }
78 })
79 t.Run("Get", func(t *testing.T) {
80 v, ok := x.Get(tc.KeyName)
81 if !assert.True(t, ok, `x.Get should succeed`) {
82 return
83 }
84 if !assert.Equal(t, tc.Value, v, `values should match`) {
85 return
86 }
87 })
88 })
89 }
90 }
91
92 func TestAdressClaim(t *testing.T) {
93 const src = `{
94 "formatted": "〒105-0011 東京都港区芝公園4丁目2−8",
95 "street_address": "芝公園4丁目2−8",
96 "locality": "港区",
97 "region": "東京都",
98 "postal_code": "105-0011",
99 "country": "日本"
100 }`
101
102 var address openid.AddressClaim
103 if !assert.NoError(t, json.Unmarshal([]byte(src), &address), "json.Unmarshal should succeed") {
104 return
105 }
106
107 var roundtrip openid.AddressClaim
108 buf, err := json.Marshal(address)
109 if !assert.NoError(t, err, `json.Marshal(address) should succeed`) {
110 return
111 }
112
113 if !assert.NoError(t, json.Unmarshal(buf, &roundtrip), "json.Unmarshal should succeed") {
114 return
115 }
116
117 for _, x := range []*openid.AddressClaim{&address, &roundtrip} {
118 testStockAddressClaim(t, x)
119 }
120 }
121
122 func TestOpenIDClaims(t *testing.T) {
123 getVerify := func(token openid.Token, key string, expected interface{}) bool {
124 v, ok := token.Get(key)
125 if !assert.True(t, ok, `token.Get %#v should succeed`, key) {
126 return false
127 }
128 return assert.Equal(t, v, expected)
129 }
130
131 var base = []struct {
132 Value interface{}
133 Expected func(interface{}) interface{}
134 Check func(openid.Token)
135 Key string
136 }{
137 {
138 Key: openid.AudienceKey,
139 Value: []string{"developers", "secops", "tac"},
140 Check: func(token openid.Token) {
141 assert.Equal(t, token.Audience(), []string{"developers", "secops", "tac"})
142 },
143 },
144 {
145 Key: openid.ExpirationKey,
146 Value: tokenTime,
147 Expected: func(v interface{}) interface{} {
148 var n types.NumericDate
149 if err := n.Accept(v); err != nil {
150 panic(err)
151 }
152 return n.Get()
153 },
154 Check: func(token openid.Token) {
155 assert.Equal(t, token.Expiration(), expectedTokenTime)
156 },
157 },
158 {
159 Key: openid.IssuedAtKey,
160 Value: tokenTime,
161 Expected: func(v interface{}) interface{} {
162 var n types.NumericDate
163 if err := n.Accept(v); err != nil {
164 panic(err)
165 }
166 return n.Get()
167 },
168 Check: func(token openid.Token) {
169 assert.Equal(t, token.Expiration(), expectedTokenTime)
170 },
171 },
172 {
173 Key: openid.IssuerKey,
174 Value: "http://www.example.com",
175 Check: func(token openid.Token) {
176 assert.Equal(t, token.Issuer(), "http://www.example.com")
177 },
178 },
179 {
180 Key: openid.JwtIDKey,
181 Value: "e9bc097a-ce51-4036-9562-d2ade882db0d",
182 Check: func(token openid.Token) {
183 assert.Equal(t, token.JwtID(), "e9bc097a-ce51-4036-9562-d2ade882db0d")
184 },
185 },
186 {
187 Key: openid.NotBeforeKey,
188 Value: tokenTime,
189 Expected: func(v interface{}) interface{} {
190 var n types.NumericDate
191 if err := n.Accept(v); err != nil {
192 panic(err)
193 }
194 return n.Get()
195 },
196 Check: func(token openid.Token) {
197 assert.Equal(t, token.NotBefore(), expectedTokenTime)
198 },
199 },
200 {
201 Key: openid.SubjectKey,
202 Value: "unit test",
203 Check: func(token openid.Token) {
204 assert.Equal(t, token.Subject(), "unit test")
205 },
206 },
207 {
208 Value: "jwx",
209 Key: openid.NameKey,
210 Check: func(token openid.Token) {
211 assert.Equal(t, token.Name(), "jwx")
212 },
213 },
214 {
215 Value: "jay",
216 Key: openid.GivenNameKey,
217 Check: func(token openid.Token) {
218 assert.Equal(t, token.GivenName(), "jay")
219 },
220 },
221 {
222 Value: "weee",
223 Key: openid.MiddleNameKey,
224 Check: func(token openid.Token) {
225 assert.Equal(t, token.MiddleName(), "weee")
226 },
227 },
228 {
229 Value: "xi",
230 Key: openid.FamilyNameKey,
231 Check: func(token openid.Token) {
232 assert.Equal(t, token.FamilyName(), "xi")
233 },
234 },
235 {
236 Value: "jayweexi",
237 Key: openid.NicknameKey,
238 Check: func(token openid.Token) {
239 assert.Equal(t, token.Nickname(), "jayweexi")
240 },
241 },
242 {
243 Value: "jwx",
244 Key: openid.PreferredUsernameKey,
245 Check: func(token openid.Token) {
246 assert.Equal(t, token.PreferredUsername(), "jwx")
247 },
248 },
249 {
250 Value: "https://github.com/lestrrat-go/jwx",
251 Key: openid.ProfileKey,
252 Check: func(token openid.Token) {
253 assert.Equal(t, token.Profile(), "https://github.com/lestrrat-go/jwx")
254 },
255 },
256 {
257 Value: "https://avatars1.githubusercontent.com/u/36653903?s=400&v=4",
258 Key: openid.PictureKey,
259 Check: func(token openid.Token) {
260 assert.Equal(t, token.Picture(), "https://avatars1.githubusercontent.com/u/36653903?s=400&v=4")
261 },
262 },
263 {
264 Value: "https://github.com/lestrrat-go/jwx",
265 Key: openid.WebsiteKey,
266 Check: func(token openid.Token) {
267 assert.Equal(t, token.Website(), "https://github.com/lestrrat-go/jwx")
268 },
269 },
270 {
271 Value: "lestrrat+github@gmail.com",
272 Key: openid.EmailKey,
273 Check: func(token openid.Token) {
274 assert.Equal(t, token.Email(), "lestrrat+github@gmail.com")
275 },
276 },
277 {
278 Value: true,
279 Key: openid.EmailVerifiedKey,
280 Check: func(token openid.Token) {
281 assert.True(t, token.EmailVerified())
282 },
283 },
284 {
285 Value: "n/a",
286 Key: openid.GenderKey,
287 Check: func(token openid.Token) {
288 assert.Equal(t, token.Gender(), "n/a")
289 },
290 },
291 {
292 Value: "2015-11-04",
293 Key: openid.BirthdateKey,
294 Expected: func(v interface{}) interface{} {
295 var b openid.BirthdateClaim
296 if err := b.Accept(v); err != nil {
297 panic(err)
298 }
299 return &b
300 },
301 Check: func(token openid.Token) {
302 var b openid.BirthdateClaim
303 b.Accept("2015-11-04")
304 assert.Equal(t, token.Birthdate(), &b)
305 },
306 },
307 {
308 Value: "Asia/Tokyo",
309 Key: openid.ZoneinfoKey,
310 Check: func(token openid.Token) {
311 assert.Equal(t, token.Zoneinfo(), "Asia/Tokyo")
312 },
313 },
314 {
315 Value: "ja_JP",
316 Key: openid.LocaleKey,
317 Check: func(token openid.Token) {
318 assert.Equal(t, token.Locale(), "ja_JP")
319 },
320 },
321 {
322 Value: "819012345678",
323 Key: openid.PhoneNumberKey,
324 Check: func(token openid.Token) {
325 assert.Equal(t, token.PhoneNumber(), "819012345678")
326 },
327 },
328 {
329 Value: true,
330 Key: openid.PhoneNumberVerifiedKey,
331 Check: func(token openid.Token) {
332 assert.True(t, token.PhoneNumberVerified())
333 },
334 },
335 {
336 Value: map[string]interface{}{
337 "formatted": "〒105-0011 東京都港区芝公園4丁目2−8",
338 "street_address": "芝公園4丁目2−8",
339 "locality": "港区",
340 "region": "東京都",
341 "country": "日本",
342 "postal_code": "105-0011",
343 },
344 Key: openid.AddressKey,
345 Expected: func(v interface{}) interface{} {
346 address := openid.NewAddress()
347 m, ok := v.(map[string]interface{})
348 if !ok {
349 panic(fmt.Sprintf("expected map[string]interface{}, got %T", v))
350 }
351 for name, val := range m {
352 if !assert.NoError(t, address.Set(name, val), `address.Set should succeed`) {
353 return nil
354 }
355 }
356 return address
357 },
358 Check: func(token openid.Token) {
359 testStockAddressClaim(t, token.Address())
360 },
361 },
362 {
363 Value: aLongLongTimeAgoString,
364 Key: openid.UpdatedAtKey,
365 Expected: func(v interface{}) interface{} {
366 var n types.NumericDate
367 if err := n.Accept(v); err != nil {
368 panic(err)
369 }
370 return n.Get()
371 },
372 Check: func(token openid.Token) {
373 assert.Equal(t, time.Unix(aLongLongTimeAgo, 0).UTC(), token.UpdatedAt())
374 },
375 },
376 {
377 Value: `dummy`,
378 Key: `dummy`,
379 Check: func(token openid.Token) {
380 v, ok := token.Get(`dummy`)
381 if !assert.True(t, ok, `token.Get should return valid value`) {
382 return
383 }
384 if !assert.Equal(t, `dummy`, v, `values should match`) {
385 return
386 }
387 },
388 },
389 }
390
391 var data = map[string]interface{}{}
392 var expected = map[string]interface{}{}
393 for _, value := range base {
394 data[value.Key] = value.Value
395 if expf := value.Expected; expf != nil {
396 expected[value.Key] = expf(value.Value)
397 } else {
398 expected[value.Key] = value.Value
399 }
400 }
401
402 type openidTokTestCase struct {
403 Token openid.Token
404 Name string
405 }
406 var tokens []openidTokTestCase
407
408 {
409 token := openid.New()
410 for name, value := range data {
411 if !assert.NoError(t, token.Set(name, value), `token.Set should succeed`) {
412 return
413 }
414 }
415 tokens = append(tokens, openidTokTestCase{Name: `token constructed by calling Set()`, Token: token})
416 }
417
418 {
419 src, err := json.MarshalIndent(data, "", " ")
420 if !assert.NoError(t, err, `failed to marshal base map`) {
421 return
422 }
423
424 t.Logf("Using source JSON: %s", src)
425
426 token := openid.New()
427 if !assert.NoError(t, json.Unmarshal(src, &token), `json.Unmarshal should succeed`) {
428 return
429 }
430 tokens = append(tokens, openidTokTestCase{Name: `token constructed by Marshal(map)+Unmashal`, Token: token})
431
432
433 buf, err := json.Marshal(token)
434 if !assert.NoError(t, err, `json.Marshal should succeed`) {
435 return
436 }
437
438 token2 := openid.New()
439 if !assert.NoError(t, json.Unmarshal(buf, &token2), `json.Unmarshal should succeed`) {
440 return
441 }
442 tokens = append(tokens, openidTokTestCase{Name: `token constructed by Marshal(openid.Token)+Unmashal`, Token: token2})
443
444
445
446 var token3 openid.Token
447 {
448 alg := jwa.RS256
449 key, err := jwxtest.GenerateRsaKey()
450 if !assert.NoError(t, err, `rsa.GeneraKey should succeed`) {
451 return
452 }
453 signed, err := jwt.Sign(token, alg, key)
454 if !assert.NoError(t, err, `jwt.Sign should succeed`) {
455 return
456 }
457
458 tokenTmp, err := jwt.Parse(signed, jwt.WithToken(openid.New()), jwt.WithVerify(alg, &key.PublicKey))
459 if !assert.NoError(t, err, `parsing the token via jwt.Parse should succeed`) {
460 return
461 }
462
463
464 if _, ok := tokenTmp.(openid.Token); !assert.True(t, ok, `token should be a openid.Token (%T)`, tokenTmp) {
465 return
466 }
467 token3 = tokenTmp.(openid.Token)
468 }
469
470 tokens = append(tokens, openidTokTestCase{Name: `token constructed by jwt.Parse`, Token: token3})
471 }
472
473 for _, token := range tokens {
474 token := token
475 t.Run(token.Name, func(t *testing.T) {
476 for _, value := range base {
477 value := value
478 t.Run(value.Key, func(t *testing.T) {
479 value.Check(token.Token)
480 })
481 t.Run(value.Key+" via Get()", func(t *testing.T) {
482 expected := value.Value
483 if expf := value.Expected; expf != nil {
484 expected = expf(value.Value)
485 }
486 getVerify(token.Token, value.Key, expected)
487 })
488 }
489 })
490 }
491
492 t.Run("Iterator", func(t *testing.T) {
493 v := tokens[0].Token
494 t.Run("Iterate", func(t *testing.T) {
495 seen := make(map[string]interface{})
496 for iter := v.Iterate(context.TODO()); iter.Next(context.TODO()); {
497 pair := iter.Pair()
498 seen[pair.Key.(string)] = pair.Value
499
500 getV, ok := v.Get(pair.Key.(string))
501 if !assert.True(t, ok, `v.Get should succeed for key %#v`, pair.Key) {
502 return
503 }
504 if !assert.Equal(t, pair.Value, getV, `pair.Value should match value from v.Get()`) {
505 return
506 }
507 }
508 if !assert.Equal(t, expected, seen, `values should match`) {
509 return
510 }
511 })
512 t.Run("Walk", func(t *testing.T) {
513 seen := make(map[string]interface{})
514 v.Walk(context.TODO(), openid.VisitorFunc(func(key string, value interface{}) error {
515 seen[key] = value
516 return nil
517 }))
518 if !assert.Equal(t, expected, seen, `values should match`) {
519 return
520 }
521 })
522 t.Run("AsMap", func(t *testing.T) {
523 seen, err := v.AsMap(context.TODO())
524 if !assert.NoError(t, err, `v.AsMap should succeed`) {
525 return
526 }
527 if !assert.Equal(t, expected, seen, `values should match`) {
528 return
529 }
530 })
531 t.Run("Clone", func(t *testing.T) {
532 cloned, err := v.Clone()
533 if !assert.NoError(t, err, `v.Clone should succeed`) {
534 return
535 }
536
537 if !assert.True(t, jwt.Equal(v, cloned), `values should match`) {
538 return
539 }
540 })
541 })
542 }
543
544 func TestBirthdateClaim(t *testing.T) {
545 t.Parallel()
546 t.Run("regular date", func(t *testing.T) {
547 t.Parallel()
548 testcases := []struct {
549 Source string
550 Year int
551 Month int
552 Day int
553 Error bool
554 }{
555 {
556 Source: `"2015-11-04"`,
557 Year: 2015,
558 Month: 11,
559 Day: 4,
560 },
561 {
562 Source: `"0009-09-09"`,
563 Year: 9,
564 Month: 9,
565 Day: 9,
566 },
567 {
568 Source: `{}`,
569 Error: true,
570 },
571 {
572 Source: `"202X-01-01"`,
573 Error: true,
574 },
575 {
576 Source: `"0000-01-01"`,
577 Error: true,
578 },
579 {
580 Source: `"0001-00-01"`,
581 Error: true,
582 },
583 {
584 Source: `"0001-01-00"`,
585 Error: true,
586 },
587 }
588
589 for _, tc := range testcases {
590 tc := tc
591 t.Run(tc.Source, func(t *testing.T) {
592 var b openid.BirthdateClaim
593 if tc.Error {
594 assert.Error(t, json.Unmarshal([]byte(tc.Source), &b), `json.Unmarshal should fail`)
595 return
596 }
597
598 if !assert.NoError(t, json.Unmarshal([]byte(tc.Source), &b), `json.Unmarshal should succeed`) {
599 return
600 }
601
602 if !assert.Equal(t, b.Year(), tc.Year, "year should match") {
603 return
604 }
605 if !assert.Equal(t, b.Month(), tc.Month, "month should match") {
606 return
607 }
608 if !assert.Equal(t, b.Day(), tc.Day, "day should match") {
609 return
610 }
611 serialized, err := json.Marshal(b)
612 if !assert.NoError(t, err, `json.Marshal should succeed`) {
613 return
614 }
615 if !assert.Equal(t, string(serialized), tc.Source, `serialized format should be the same`) {
616 return
617 }
618 stringified := b.String()
619 expectedString, _ := strconv.Unquote(tc.Source)
620 if !assert.Equal(t, stringified, expectedString, `stringified format should be the same`) {
621 return
622 }
623 })
624 }
625 })
626 t.Run("empty date", func(t *testing.T) {
627 t.Parallel()
628 var b openid.BirthdateClaim
629 if !assert.Equal(t, b.Year(), 0, "year should match") {
630 return
631 }
632 if !assert.Equal(t, b.Month(), 0, "month should match") {
633 return
634 }
635 if !assert.Equal(t, b.Day(), 0, "day should match") {
636 return
637 }
638 })
639 t.Run("invalid accept", func(t *testing.T) {
640 t.Parallel()
641 var b openid.BirthdateClaim
642 if !assert.Error(t, b.Accept(nil)) {
643 return
644 }
645 })
646 }
647
648 func TestKeys(t *testing.T) {
649 at := assert.New(t)
650 at.Equal(`address`, openid.AddressKey)
651 at.Equal(`aud`, openid.AudienceKey)
652 at.Equal(`birthdate`, openid.BirthdateKey)
653 at.Equal(`email`, openid.EmailKey)
654 at.Equal(`email_verified`, openid.EmailVerifiedKey)
655 at.Equal(`exp`, openid.ExpirationKey)
656 at.Equal(`family_name`, openid.FamilyNameKey)
657 at.Equal(`gender`, openid.GenderKey)
658 at.Equal(`given_name`, openid.GivenNameKey)
659 at.Equal(`iat`, openid.IssuedAtKey)
660 at.Equal(`iss`, openid.IssuerKey)
661 at.Equal(`jti`, openid.JwtIDKey)
662 at.Equal(`locale`, openid.LocaleKey)
663 at.Equal(`middle_name`, openid.MiddleNameKey)
664 at.Equal(`name`, openid.NameKey)
665 at.Equal(`nickname`, openid.NicknameKey)
666 at.Equal(`nbf`, openid.NotBeforeKey)
667 at.Equal(`phone_number`, openid.PhoneNumberKey)
668 at.Equal(`phone_number_verified`, openid.PhoneNumberVerifiedKey)
669 at.Equal(`picture`, openid.PictureKey)
670 at.Equal(`preferred_username`, openid.PreferredUsernameKey)
671 at.Equal(`profile`, openid.ProfileKey)
672 at.Equal(`sub`, openid.SubjectKey)
673 at.Equal(`updated_at`, openid.UpdatedAtKey)
674 at.Equal(`website`, openid.WebsiteKey)
675 at.Equal(`zoneinfo`, openid.ZoneinfoKey)
676 }
677
View as plain text