Skip to content
This repository was archived by the owner on May 24, 2024. It is now read-only.

Commit 376c27d

Browse files
Ajinkya Naharajinkyan83
authored andcommitted
DA-4596: dto changes for caching auth0 jwks
Signed-off-by: Ajinkya Nahar <[email protected]>
1 parent 6bbfdb8 commit 376c27d

File tree

3 files changed

+184
-49
lines changed

3 files changed

+184
-49
lines changed

auth0/dto.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ type AuthToken struct {
99
CreatedAt time.Time `json:"created_at"`
1010
}
1111

12+
// AuthJwks Struct
13+
type AuthJwks struct {
14+
Name string `json:"name"`
15+
Jwks string `json:"jwks"`
16+
CreatedAt time.Time `json:"created_at"`
17+
}
18+
1219
// Resp struct
1320
type Resp struct {
1421
AccessToken string `json:"access_token"`
@@ -46,6 +53,36 @@ type ESTokenSchema struct {
4653
} `json:"hits"`
4754
}
4855

56+
// ESJwksSchema ...
57+
type ESJwksSchema struct {
58+
Took int `json:"took"`
59+
TimedOut bool `json:"timed_out"`
60+
Shards struct {
61+
Total int `json:"total"`
62+
Successful int `json:"successful"`
63+
Skipped int `json:"skipped"`
64+
Failed int `json:"failed"`
65+
} `json:"_shards"`
66+
Hits struct {
67+
Total struct {
68+
Value int `json:"value"`
69+
Relation string `json:"relation"`
70+
} `json:"total"`
71+
MaxScore float64 `json:"max_score"`
72+
Hits []struct {
73+
Index string `json:"_index"`
74+
Type string `json:"_type"`
75+
ID string `json:"_id"`
76+
Score float64 `json:"_score"`
77+
Source struct {
78+
Name string `json:"name"`
79+
Jwks string `json:"jwks"`
80+
CreatedAt time.Time `json:"created_at"`
81+
} `json:"_source"`
82+
} `json:"hits"`
83+
} `json:"hits"`
84+
}
85+
4986
// LastActionSchema ...
5087
type LastActionSchema struct {
5188
Took int `json:"took"`
@@ -79,12 +116,14 @@ const (
79116
lastAuth0TokenRequest = "last-auth0-token-request-"
80117
auth0TokenCache = "auth0-token-cache-"
81118
tokenDoc = "token"
119+
auth0JwksCache = "auth0-jwks-cache-"
120+
jwksDoc = "jwks"
82121
)
83122

84123
// RefreshResult ...
85124
type RefreshResult string
86125

87-
const(
126+
const (
88127
// RefreshError ...
89128
RefreshError RefreshResult = "error refreshing auth0 token"
90129
// RefreshSuccessful ...

auth0/jwks.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package auth0
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
"fmt"
7+
"log"
8+
"strings"
9+
"time"
10+
11+
"github.com/dgrijalva/jwt-go"
12+
)
13+
14+
// Jwks result from auth0 well know keys
15+
type Jwks struct {
16+
Keys []JSONWebKeys `json:"keys"`
17+
}
18+
19+
// JSONWebKeys auth0 token key
20+
type JSONWebKeys struct {
21+
Kty string `json:"kty"`
22+
Kid string `json:"kid"`
23+
Use string `json:"use"`
24+
N string `json:"n"`
25+
E string `json:"e"`
26+
X5c []string `json:"x5c"`
27+
}
28+
29+
func (a *ClientProvider) createAuthJwks(cert string) error {
30+
log.Println("creating new auth jwks cert string")
31+
at := AuthJwks{
32+
Name: "AuthJwks",
33+
Jwks: cert,
34+
CreatedAt: time.Now().UTC(),
35+
}
36+
_, err := a.esClient.UpdateDocument(fmt.Sprintf("%s%s", auth0JwksCache, a.Environment), jwksDoc, at)
37+
if err != nil {
38+
log.Println("could not write the data", err)
39+
return err
40+
}
41+
42+
return nil
43+
}
44+
45+
func (a *ClientProvider) getPemCert(token *jwt.Token, refreshJwks bool) (string, error) {
46+
cert := ""
47+
cert, expired, err := a.getCachedJwks()
48+
if err != nil {
49+
return cert, err
50+
}
51+
52+
// check if the cache expired as well is not invoked via refresh token cron
53+
if !expired && !refreshJwks {
54+
return cert, nil
55+
}
56+
57+
_, resp, err := a.httpClient.Request(fmt.Sprintf("%s/oauth/.well-known/jwks.json", a.AuthURL), "GET", nil, nil, nil)
58+
if err != nil {
59+
return cert, err
60+
}
61+
62+
var jwks = Jwks{}
63+
if err := json.Unmarshal(resp, &jwks); err != nil {
64+
return cert, err
65+
}
66+
67+
for _, k := range jwks.Keys {
68+
if token.Header["kid"] == k.Kid {
69+
cert = "-----BEGIN CERTIFICATE-----\n" + k.X5c[0] + "\n-----END CERTIFICATE-----"
70+
}
71+
}
72+
73+
if cert == "" {
74+
err := errors.New("unable to find appropriate key")
75+
return cert, err
76+
}
77+
78+
err = a.createAuthJwks(cert)
79+
if err != nil {
80+
return "", err
81+
}
82+
83+
return cert, nil
84+
}
85+
86+
func (a *ClientProvider) getCachedJwks() (string, bool, error) {
87+
expired := true
88+
res, err := a.esClient.Search(strings.TrimSpace(auth0JwksCache+a.Environment), searchJwksQuery)
89+
if err != nil {
90+
go func() {
91+
errMsg := fmt.Sprintf("%s-%s: error cached jwks not found\n %s", a.appName, a.Environment, err)
92+
err := a.slackClient.SendText(errMsg)
93+
fmt.Println("Err: send to slack: ", err)
94+
}()
95+
96+
return "", expired, err
97+
}
98+
99+
var e ESJwksSchema
100+
err = json.Unmarshal(res, &e)
101+
if err != nil {
102+
log.Println("repository: GetOauthJwks: could not unmarshal the data", err)
103+
return "", expired, err
104+
}
105+
106+
if len(e.Hits.Hits) > 0 {
107+
data := e.Hits.Hits[0]
108+
// compare current time v/s existing cached time + 30 mins
109+
if data.Source.CreatedAt.Add(30*time.Minute).Unix() <= time.Now().UTC().Unix() {
110+
expired = false
111+
}
112+
113+
return data.Source.Jwks, expired, nil
114+
}
115+
116+
return "", expired, errors.New("GetJwks: could not find the associated jwks")
117+
}
118+
119+
var searchJwksQuery = map[string]interface{}{
120+
"size": 1,
121+
"query": map[string]interface{}{
122+
"term": map[string]interface{}{
123+
"_id": jwksDoc,
124+
},
125+
},
126+
}

auth0/token.go

Lines changed: 18 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,10 @@ func (a *ClientProvider) GetToken(input bool) (string, error) {
8585
if authToken == "" {
8686
return authToken, errors.New("cached token is empty")
8787
}
88+
8889
if input {
8990
// check token validity
90-
ok, _, err := a.isValid(authToken)
91+
ok, _, err := a.isValid(authToken, false)
9192
if err != nil {
9293
log.Println(err)
9394
return "", err
@@ -99,6 +100,7 @@ func (a *ClientProvider) GetToken(input bool) (string, error) {
99100

100101
return authToken, errors.New("cached token is not valid")
101102
}
103+
102104
return authToken, nil
103105
}
104106

@@ -137,6 +139,7 @@ func (a *ClientProvider) generateToken() (string, error) {
137139
}()
138140
log.Println("Err: GenerateToken ", err)
139141
}
142+
140143
go func() {
141144
err = a.createLastActionDate()
142145
log.Println(err)
@@ -149,7 +152,8 @@ func (a *ClientProvider) generateToken() (string, error) {
149152
if result.AccessToken != "" {
150153
log.Println("GenerateToken: Token generated successfully.")
151154
}
152-
ok, _, err := a.isValid(result.AccessToken)
155+
156+
ok, _, err := a.isValid(result.AccessToken, true)
153157
if !ok || err != nil {
154158
go func() {
155159
errMsg := fmt.Sprintf("%s-%s: error validating the newly created token\n %s", a.appName, a.Environment, err)
@@ -222,13 +226,13 @@ var searchCacheQuery = map[string]interface{}{
222226
},
223227
}
224228

225-
func (a *ClientProvider) isValid(token string) (bool, jwt.MapClaims, error) {
229+
func (a *ClientProvider) isValid(token string, refreshJwks bool) (bool, jwt.MapClaims, error) {
226230
p, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
227231
if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok {
228232
return nil, errors.New("unexpected signing method")
229233
}
230234

231-
cert, err := a.getPemCert(t)
235+
cert, err := a.getPemCert(t, refreshJwks)
232236
if err != nil {
233237
return nil, err
234238
}
@@ -240,6 +244,7 @@ func (a *ClientProvider) isValid(token string) (bool, jwt.MapClaims, error) {
240244

241245
return key, nil
242246
})
247+
243248
if err != nil {
244249
return false, nil, err
245250
}
@@ -252,47 +257,6 @@ func (a *ClientProvider) isValid(token string) (bool, jwt.MapClaims, error) {
252257
return p.Valid, claims, err
253258
}
254259

255-
// Jwks result from auth0 well know keys
256-
type Jwks struct {
257-
Keys []JSONWebKeys `json:"keys"`
258-
}
259-
260-
// JSONWebKeys auth0 token key
261-
type JSONWebKeys struct {
262-
Kty string `json:"kty"`
263-
Kid string `json:"kid"`
264-
Use string `json:"use"`
265-
N string `json:"n"`
266-
E string `json:"e"`
267-
X5c []string `json:"x5c"`
268-
}
269-
270-
func (a *ClientProvider) getPemCert(token *jwt.Token) (string, error) {
271-
cert := ""
272-
_, resp, err := a.httpClient.Request(fmt.Sprintf("%s/oauth/.well-known/jwks.json", a.AuthURL), "GET", nil, nil, nil)
273-
if err != nil {
274-
return cert, err
275-
}
276-
277-
var jwks = Jwks{}
278-
if err := json.Unmarshal(resp, &jwks); err != nil {
279-
return cert, err
280-
}
281-
282-
for _, k := range jwks.Keys {
283-
if token.Header["kid"] == k.Kid {
284-
cert = "-----BEGIN CERTIFICATE-----\n" + k.X5c[0] + "\n-----END CERTIFICATE-----"
285-
}
286-
}
287-
288-
if cert == "" {
289-
err := errors.New("unable to find appropriate key")
290-
return cert, err
291-
}
292-
293-
return cert, nil
294-
}
295-
296260
func (a *ClientProvider) createLastActionDate() error {
297261
s := struct {
298262
Date time.Time `json:"date"`
@@ -357,22 +321,28 @@ func (a *ClientProvider) RefreshToken() (RefreshResult, error) {
357321
}
358322

359323
if authToken == "" || err != nil {
360-
authToken, err = a.refreshCachedToken()
324+
_, err = a.refreshCachedToken()
361325
if err != nil {
362326
return RefreshError, err
363327
}
328+
364329
return RefreshSuccessful, nil
365330
}
366331

367-
ok, claims, err := a.isValid(authToken)
332+
ok, claims, err := a.isValid(authToken, false)
368333
if ok && err == nil {
369-
if claims.VerifyExpiresAt(time.Now().Add(60*time.Minute).Unix(), false) == false {
334+
if !claims.VerifyExpiresAt(time.Now().Add(60*time.Minute).Unix(), false) {
335+
if _, err := a.refreshCachedToken(); err != nil {
336+
log.Printf("Error refresh auth0 token %s\n", err.Error())
337+
return RefreshError, err
338+
}
370339
if _, err := a.refreshCachedToken(); err != nil {
371340
log.Printf("Error refresh auth0 token %s\n", err.Error())
372341
return RefreshError, err
373342
}
374343
return RefreshSuccessful, nil
375344
}
345+
376346
return NotExpireSoon, nil
377347
}
378348

0 commit comments

Comments
 (0)