forked from someone1/gcp-jwt-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
iam_appengine.go
122 lines (100 loc) · 3.04 KB
/
iam_appengine.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
package gcpjwt
import (
"context"
"sync"
"time"
"github.com/golang-jwt/jwt/v4"
"google.golang.org/appengine"
)
const appEngineSvcAcct = "APPENGINE"
// SigningMethodAppEngineImpl implements singing JWT's using the built-in AppEngine signing method. This method uses a
// private key unique to your AppEngine application and the key may rotate from time to time.
// https://cloud.google.com/appengine/docs/go/reference#SignBytes
// https://cloud.google.com/appengine/docs/go/appidentity/#Go_Asserting_identity_to_other_systems
type SigningMethodAppEngineImpl struct {
*SigningMethodIAM
sync.RWMutex
lastKeyID string
}
var (
SigningMethodAppEngine *SigningMethodAppEngineImpl
)
func init() {
SigningMethodAppEngine = &SigningMethodAppEngineImpl{
SigningMethodIAM: &SigningMethodIAM{
alg: "AppEngine",
override: jwt.SigningMethodRS256.Alg(),
sign: nil,
},
}
jwt.RegisterSigningMethod(SigningMethodAppEngine.Alg(), func() jwt.SigningMethod {
return SigningMethodAppEngine
})
}
// Sign implements the Sign method from jwt.SigningMethod. For this signing method, a valid AppEngine context.Context
// must be passed as the key.
func (s *SigningMethodAppEngineImpl) Sign(signingString string, key interface{}) (string, error) {
var ctx context.Context
switch k := key.(type) {
case context.Context:
ctx = k
default:
return "", jwt.ErrInvalidKey
}
keyName, signature, err := appengine.SignBytes(ctx, []byte(signingString))
if err != nil {
return "", err
}
s.Lock()
defer s.Unlock()
s.lastKeyID = keyName
return jwt.EncodeSegment(signature), nil
}
// KeyID will return the last used KeyID to sign the JWT.
// Helper function for adding the kid header to your token.
func (s *SigningMethodAppEngineImpl) KeyID() string {
s.RLock()
defer s.RUnlock()
return s.lastKeyID
}
var (
appEngineKeyfunc = &keyFuncHelper{
compareMethod: func(j jwt.SigningMethod) bool {
_, ok := j.(*SigningMethodAppEngineImpl)
return ok
},
certificates: getAppEngineCertificates,
}
)
// AppEngineVerfiyKeyfunc is a helper meant that returns a jwt.Keyfunc. It will handle pulling and selecting the
// certificates to verify signatures with, caching when enabled.
func AppEngineVerfiyKeyfunc(ctx context.Context, enableCache bool, cacheExpiration time.Duration) jwt.Keyfunc {
config := &IAMConfig{
EnableCache: enableCache,
CacheExpiration: cacheExpiration,
}
return appEngineKeyfunc.verifyKeyfunc(ctx, config)
}
func getAppEngineCertificates(ctx context.Context, config *IAMConfig) (certificates, error) {
if config.EnableCache {
if certsResp, ok := getCertsFromCache(appEngineSvcAcct); ok {
return certsResp, nil
}
}
aeCerts, err := appengine.PublicCertificates(ctx)
if err != nil {
return nil, err
}
certs := make(certificates)
for _, cert := range aeCerts {
rsaKey, err := jwt.ParseRSAPublicKeyFromPEM([]byte(cert.Data))
if err != nil {
return nil, err
}
certs[cert.KeyName] = rsaKey
}
if config.EnableCache {
updateCache(appEngineSvcAcct, certs, time.Now().Add(config.CacheExpiration))
}
return certs, nil
}