...
1 package jwksx
2
3 import (
4 "encoding/json"
5 "net/http"
6 "sync"
7
8 "github.com/pkg/errors"
9 "gopkg.in/square/go-jose.v2"
10 )
11
12
13 type Fetcher struct {
14 sync.RWMutex
15 remote string
16 c *http.Client
17 keys map[string]jose.JSONWebKey
18 }
19
20
21 func NewFetcher(remote string) *Fetcher {
22 return &Fetcher{
23 remote: remote,
24 c: http.DefaultClient,
25 keys: make(map[string]jose.JSONWebKey),
26 }
27 }
28
29
30 func (f *Fetcher) GetKey(kid string) (*jose.JSONWebKey, error) {
31 f.RLock()
32 if k, ok := f.keys[kid]; ok {
33 f.RUnlock()
34 return &k, nil
35 }
36 f.RUnlock()
37
38 res, err := f.c.Get(f.remote)
39 if err != nil {
40 return nil, errors.WithStack(err)
41 }
42 defer res.Body.Close()
43 if res.StatusCode != http.StatusOK {
44 return nil, errors.Errorf("expected status code 200 but got %d when requesting %s", res.StatusCode, f.remote)
45 }
46
47 var set jose.JSONWebKeySet
48 if err := json.NewDecoder(res.Body).Decode(&set); err != nil {
49 return nil, errors.WithStack(err)
50 }
51
52 for _, k := range set.Keys {
53 f.Lock()
54 f.keys[k.KeyID] = k
55 f.Unlock()
56 }
57
58 f.RLock()
59 defer f.RUnlock()
60 if k, ok := f.keys[kid]; ok {
61 return &k, nil
62 }
63
64 return nil, errors.Errorf("unable to find JSON Web Key with ID: %s", kid)
65 }
66
View as plain text