Skip to content

Commit 96eef7b

Browse files
committed
fix get JWK
use http.DefaultTransport.(*http.Transport).Clone()
1 parent 01d6b5d commit 96eef7b

17 files changed

Lines changed: 495 additions & 16 deletions

File tree

bundle/client/grpc/main.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import (
1818
"github.com/plgd-dev/go-coap/v2/message"
1919
"github.com/plgd-dev/kit/codec/json"
2020
kitNetGrpc "github.com/plgd-dev/kit/net/grpc"
21-
"github.com/plgd-dev/kit/net/http/transport"
2221
)
2322

2423
func toJSON(v interface{}) string {
@@ -86,7 +85,7 @@ func main() {
8685
InsecureSkipVerify: true,
8786
}
8887
if *accesstoken == "" {
89-
t := transport.NewDefaultTransport()
88+
t := http.DefaultTransport.(*http.Transport).Clone()
9089
t.TLSClientConfig = &tlsCfg
9190
c := http.Client{
9291
Transport: t,

certificate-authority/refImpl/refImpl.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import (
1010
grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
1111
grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags"
1212

13+
"github.com/plgd-dev/cloud/pkg/security/jwt"
1314
"github.com/plgd-dev/kit/security/certManager"
14-
"github.com/plgd-dev/kit/security/jwt"
1515

1616
"github.com/plgd-dev/cloud/certificate-authority/pb"
1717
"github.com/plgd-dev/cloud/certificate-authority/service"

cloud2cloud-connector/store/linkedCloud.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88

99
"github.com/plgd-dev/cloud/authorization/oauth"
1010
"github.com/plgd-dev/cloud/cloud2cloud-connector/events"
11-
"github.com/plgd-dev/kit/net/http/transport"
1211
"golang.org/x/oauth2"
1312
)
1413

@@ -87,7 +86,7 @@ func (l LinkedCloud) GetHTTPClient() *http.Client {
8786
for _, ca := range l.Endpoint.RootCAs {
8887
pool.AppendCertsFromPEM([]byte(ca))
8988
}
90-
t := transport.NewDefaultTransport()
89+
t := http.DefaultTransport.(*http.Transport).Clone()
9190
t.TLSClientConfig = &tls.Config{
9291
RootCAs: pool,
9392
InsecureSkipVerify: l.Endpoint.InsecureSkipVerify,

cloud2cloud-gateway/service/emitEvent.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,13 @@ import (
1515

1616
"github.com/plgd-dev/cloud/cloud2cloud-gateway/store"
1717
"github.com/plgd-dev/kit/log"
18-
"github.com/plgd-dev/kit/net/http/transport"
1918
)
2019

2120
type incrementSubscriptionSequenceNumberFunc func(ctx context.Context) (uint64, error)
2221
type emitEventFunc func(ctx context.Context, eventType events.EventType, s store.Subscription, incrementSubscriptionSequenceNumber incrementSubscriptionSequenceNumberFunc, rep interface{}) (remove bool, err error)
2322

2423
func createEmitEventFunc(tls *tls.Config, timeout time.Duration) emitEventFunc {
25-
trans := transport.NewDefaultTransport()
24+
trans := http.DefaultTransport.(*http.Transport).Clone()
2625
trans.TLSClientConfig = tls
2726
client := netHttp.Client{
2827
Transport: trans,

coap-gateway/service/devicesStatusUpdater.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ func (u *devicesStatusUpdater) run() {
108108
d.expires = expires
109109
}
110110
}
111-
log.Debugf("update devices statuses to online takes: %v", time.Now().Sub(now))
112111
}
113112
}
114113
}

grpc-gateway/refImpl/refImpl.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ import (
1515
"google.golang.org/grpc"
1616

1717
"github.com/plgd-dev/cloud/grpc-gateway/service"
18+
"github.com/plgd-dev/cloud/pkg/security/jwt"
1819
"github.com/plgd-dev/kit/log"
1920
kitNetGrpc "github.com/plgd-dev/kit/net/grpc"
20-
"github.com/plgd-dev/kit/security/jwt"
2121
"google.golang.org/grpc/credentials"
2222
)
2323

http-gateway/test/test.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import (
1111
"testing"
1212
"time"
1313

14-
"github.com/plgd-dev/kit/net/http/transport"
15-
1614
"github.com/jtacoma/uritemplates"
1715
"github.com/kelseyhightower/envconfig"
1816
"github.com/plgd-dev/cloud/coap-gateway/schema/device/status"
@@ -118,7 +116,7 @@ func (c *requestBuilder) Build() *http.Request {
118116
}
119117

120118
func HTTPDo(t *testing.T, req *http.Request) *http.Response {
121-
trans := transport.NewDefaultTransport()
119+
trans := http.DefaultTransport.(*http.Transport).Clone()
122120
trans.TLSClientConfig = &tls.Config{
123121
InsecureSkipVerify: true,
124122
}

pkg/security/jwt/claims.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package jwt
2+
3+
import (
4+
"fmt"
5+
"time"
6+
7+
"github.com/plgd-dev/kit/strings"
8+
)
9+
10+
type Claims struct {
11+
ClientID string `json:"client_id"`
12+
Email string `json:"email"`
13+
Scope interface{} `json:"scope"`
14+
StandardClaims
15+
}
16+
17+
func (c Claims) GetScope() []string {
18+
return strings.ToSlice(c.Scope)
19+
}
20+
21+
// https://tools.ietf.org/html/rfc7519#section-4.1
22+
type StandardClaims struct {
23+
Audience interface{} `json:"aud,omitempty"`
24+
ExpiresAt int64 `json:"exp,omitempty"`
25+
Id string `json:"jti,omitempty"`
26+
IssuedAt int64 `json:"iat,omitempty"`
27+
Issuer string `json:"iss,omitempty"`
28+
NotBefore int64 `json:"nbf,omitempty"`
29+
Subject string `json:"sub,omitempty"`
30+
}
31+
32+
func (c StandardClaims) GetAudience() []string {
33+
return strings.ToSlice(c.Audience)
34+
}
35+
36+
func (c StandardClaims) Valid() error {
37+
now := timeFunc().Unix()
38+
if now > c.ExpiresAt {
39+
return fmt.Errorf("token is expired")
40+
}
41+
if now < c.IssuedAt {
42+
return fmt.Errorf("token used before issued")
43+
}
44+
if now < c.NotBefore {
45+
return fmt.Errorf("token is not valid yet")
46+
}
47+
return nil
48+
}
49+
50+
var timeFunc = time.Now
51+
52+
func SetTimeFunc(f func() time.Time) (restore func()) {
53+
prev := timeFunc
54+
timeFunc = f
55+
return func() { timeFunc = prev }
56+
}

pkg/security/jwt/claims_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package jwt
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
func TestValid(t *testing.T) {
11+
c := testClaims()
12+
require.NoError(t, c.Valid())
13+
}
14+
15+
func TestExpired(t *testing.T) {
16+
c := testClaims()
17+
c.StandardClaims.ExpiresAt = now.Add(-time.Hour).Unix()
18+
require.Error(t, c.Valid())
19+
}
20+
21+
func TestIssuedLater(t *testing.T) {
22+
c := testClaims()
23+
c.StandardClaims.IssuedAt = now.Add(time.Hour).Unix()
24+
require.Error(t, c.Valid())
25+
}
26+
27+
func TestNotBefore(t *testing.T) {
28+
c := testClaims()
29+
c.StandardClaims.NotBefore = now.Add(time.Hour).Unix()
30+
require.Error(t, c.Valid())
31+
}
32+
33+
func TestEmptyAudience(t *testing.T) {
34+
c := testClaims()
35+
require.Nil(t, c.GetAudience())
36+
}
37+
38+
func TestAudienceOfOne(t *testing.T) {
39+
aud := "test"
40+
c := testClaims()
41+
c.Audience = aud
42+
require.Equal(t, []string{aud}, c.GetAudience())
43+
}
44+
45+
func TestAudienceOfTwo(t *testing.T) {
46+
c := testClaims()
47+
c.Audience = []interface{}{"test1", "test2"}
48+
require.Equal(t, []string{"test1", "test2"}, c.GetAudience())
49+
}
50+
51+
var now = time.Now()
52+
53+
func testClaims() Claims {
54+
return Claims{
55+
ClientID: "testClientID",
56+
Email: "testEmail",
57+
Scope: []string{"testScope"},
58+
StandardClaims: StandardClaims{
59+
ExpiresAt: now.Add(time.Hour).Unix(),
60+
IssuedAt: now.Unix(),
61+
NotBefore: now.Unix(),
62+
},
63+
}
64+
}

pkg/security/jwt/jwk.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package jwt
2+
3+
import (
4+
"crypto/tls"
5+
"fmt"
6+
"net/http"
7+
"sync"
8+
"time"
9+
10+
"github.com/dgrijalva/jwt-go"
11+
"github.com/lestrrat-go/jwx/jwk"
12+
)
13+
14+
type KeyCache struct {
15+
url string
16+
http *http.Client
17+
m sync.Mutex
18+
keys *jwk.Set
19+
}
20+
21+
func NewKeyCache(url string, tls *tls.Config) *KeyCache {
22+
t := http.DefaultTransport.(*http.Transport).Clone()
23+
t.MaxIdleConns = 100
24+
t.MaxConnsPerHost = 100
25+
t.MaxIdleConnsPerHost = 100
26+
t.IdleConnTimeout = time.Second * 30
27+
t.TLSClientConfig = tls
28+
client := &http.Client{
29+
Transport: t,
30+
Timeout: time.Second * 10,
31+
}
32+
return &KeyCache{url: url, http: client}
33+
}
34+
35+
func (c *KeyCache) GetOrFetchKey(token *jwt.Token) (interface{}, error) {
36+
if k, err := c.GetKey(token); err == nil {
37+
return k, nil
38+
}
39+
if err := c.FetchKeys(); err != nil {
40+
return nil, err
41+
}
42+
return c.GetKey(token)
43+
}
44+
45+
func (c *KeyCache) GetKey(token *jwt.Token) (interface{}, error) {
46+
key, err := c.LookupKey(token)
47+
if err != nil {
48+
return nil, err
49+
}
50+
var v interface{}
51+
return v, key.Raw(&v)
52+
}
53+
54+
func (c *KeyCache) LookupKey(token *jwt.Token) (jwk.Key, error) {
55+
id, ok := token.Header["kid"].(string)
56+
if !ok {
57+
return nil, fmt.Errorf("missing key id in token")
58+
}
59+
60+
c.m.Lock()
61+
defer c.m.Unlock()
62+
63+
if c.keys == nil {
64+
return nil, fmt.Errorf("empty JWK cache")
65+
}
66+
for _, key := range c.keys.LookupKeyID(id) {
67+
if key.Algorithm() == token.Method.Alg() {
68+
return key, nil
69+
}
70+
}
71+
return nil, fmt.Errorf("could not find JWK")
72+
}
73+
74+
func (c *KeyCache) FetchKeys() error {
75+
keys, err := jwk.FetchHTTP(c.url, jwk.WithHTTPClient(c.http))
76+
if err != nil {
77+
return fmt.Errorf("could not fetch JWK: %w", err)
78+
}
79+
80+
c.m.Lock()
81+
defer c.m.Unlock()
82+
83+
c.keys = keys
84+
return nil
85+
}

0 commit comments

Comments
 (0)