1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
package main
import (
"github.com/pkg/errors"
"gopkg.in/square/go-jose.v2"
"log"
"net/url"
"time"
)
const (
REQUEST_BUFFER_SIZE = 10
KEY_MAP_INITIAL_SIZE = 5
DEFAULT_REFRESH_INTERVAL = 15 * time.Minute
MIN_REFRESH_INTERVAL = 1 * time.Minute
)
type KeyRequest struct {
KeyId string
Response chan *jose.JSONWebKey
}
type JWKSFetcher interface {
Run()
Fetch() error
GetKey(string) (*jose.JSONWebKey, error)
Done()
}
type jwksFetcher struct {
keyMap map[string]jose.JSONWebKey
httpClient CautiousHTTPClient
validator KeyValidator
fetchTimer *time.Timer
url *url.URL
requests chan *KeyRequest
done chan bool
}
func NewJWKSFetcher(h CautiousHTTPClient, url *url.URL, issuer string, root string) JWKSFetcher {
val := NewKeyValidator(HostFromURL(issuer))
val.LoadRootPEM(root)
return &jwksFetcher{
httpClient: h,
validator: val,
url: url,
fetchTimer: time.NewTimer(DEFAULT_REFRESH_INTERVAL),
requests: make(chan *KeyRequest, REQUEST_BUFFER_SIZE),
keyMap: make(map[string]jose.JSONWebKey, KEY_MAP_INITIAL_SIZE),
done: make(chan bool),
}
}
func (f *jwksFetcher) Fetch() error {
var jwks jose.JSONWebKeySet
timeout, err := f.httpClient.GetJSONExpires(f.url.String(), &jwks)
if err != nil {
return errors.WithStack(err)
}
for _, k := range jwks.Keys {
err = f.validator.Validate(k)
if err == nil {
f.keyMap[k.KeyID] = k
} else {
log.Printf("Rejecting key %q because %q", k.KeyID, err)
}
}
if timeout < MIN_REFRESH_INTERVAL {
timeout = MIN_REFRESH_INTERVAL
}
success := f.fetchTimer.Reset(timeout)
if !success {
f.fetchTimer = time.NewTimer(timeout)
}
return nil
}
func (f *jwksFetcher) Run() {
for {
select {
// Incoming request for a key, return key or nil in no key
case r := <-f.requests:
if v, ok := f.keyMap[r.KeyId]; ok {
r.Response <- &v
} else {
r.Response <- nil
}
case <-f.fetchTimer.C:
f.Fetch()
case <-f.done:
return
}
}
}
func (f *jwksFetcher) Done() {
f.done <- true
}
func (f *jwksFetcher) GetKey(kid string) (*jose.JSONWebKey, error) {
r := &KeyRequest{
KeyId: kid,
Response: make(chan *jose.JSONWebKey),
}
f.requests <- r
if res := <-r.Response; res == nil {
return nil, errors.Errorf("Key not found for ID")
} else {
return res, nil
}
}
|