-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgoogle.go
128 lines (109 loc) · 3.48 KB
/
google.go
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
119
120
121
122
123
124
125
126
127
128
package gopenid
import (
"encoding/json"
"fmt"
jwt "github.com/dgrijalva/jwt-go"
"github.com/lestrrat/go-jwx/jwk"
"net/http"
"net/url"
"time"
)
// GoogleService contains business logic to the domain
type GoogleService struct {
cache Cache
}
// NewGoogleService return new object
func NewGoogleService(cache Cache) *GoogleService {
return &GoogleService{
cache: cache,
}
}
const timeout = time.Duration(10 * time.Second)
const (
googleDomain = "https://accounts.google.com"
openIDURI = "/.well-known/openid-configuration"
)
func getKeyFunction(cc Cache) jwt.Keyfunc {
return func(token *jwt.Token) (interface{}, error) {
keyID, ok := token.Header["kid"].(string)
if !ok {
return nil, ErrJWTHeaderMissingKID
}
if cc != nil {
value := cc.Get(keyID)
if value != nil {
fmt.Printf("cache found google_public for key: %s and value: %+v\n", keyID, value)
return value, nil
}
}
fmt.Printf("cache not found google_public for key: %s\n", keyID)
client := &http.Client{Timeout: timeout}
var jwksURI string
{
u, err := url.ParseRequestURI(googleDomain)
if err != nil {
return nil, fmt.Errorf("failed to parse request uri: %w", err)
}
u.Path = openIDURI
urlStr := u.String()
r, err := http.NewRequest("GET", urlStr, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request body: %w", err)
}
resp, err := client.Do(r)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
defer func() {
err := resp.Body.Close()
if err != nil {
fmt.Printf("failed to close response body with reason: %s\n", err.Error())
}
}()
respBody := struct {
Issuer string `json:"issuer"`
AuthorizationEndpoint string `json:"authorization_endpoint"`
TokenEndpoint string `json:"token_endpoint"`
UserinfoEndpoint string `json:"userinfo_endpoint"`
RevocationEndpoint string `json:"revocation_endpoint"`
JwksURI string `json:"jwks_uri"`
ResponseTypesSupported []string `json:"response_types_supported"`
SubjectTypesSupported []string `json:"subject_types_supported"`
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"`
ScopesSupported []string `json:"scopes_supported"`
TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported"`
ClaimsSupported []string `json:"claims_supported"`
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"`
}{}
err = json.NewDecoder(resp.Body).Decode(&respBody)
if err != nil {
return nil, fmt.Errorf("failed to decode response body: %w", err)
}
jwksURI = respBody.JwksURI
}
// we want to verify a JWT
set, err := jwk.FetchHTTP(jwksURI)
if err != nil {
return nil, err
}
if key := set.LookupKeyID(keyID); len(key) == 1 {
m, err := key[0].Materialize()
if err != nil {
return nil, err
}
cc.SetExpire(keyID, m, time.Hour*20)
fmt.Printf("cached keyID[%s] for %d hours\n", keyID, 20)
return m, nil
}
return nil, ErrPublicKeyIsNotFound
}
}
// TokenInfoForProd dao
func (s *GoogleService) TokenInfoForProd(IDToken string) (*jwt.MapClaims, error) {
token, err := jwt.Parse(IDToken, getKeyFunction(s.cache))
if err != nil {
return nil, err
}
claims := token.Claims.(jwt.MapClaims)
return &claims, nil
}