go.temporal.io/server@v1.23.0/common/authorization/default_token_key_provider.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package authorization 26 27 import ( 28 "crypto/ecdsa" 29 "crypto/rsa" 30 "encoding/json" 31 "fmt" 32 "net/http" 33 "strings" 34 "sync" 35 "time" 36 37 "github.com/golang-jwt/jwt/v4" 38 "go.uber.org/multierr" 39 "gopkg.in/square/go-jose.v2" 40 41 "go.temporal.io/server/common/config" 42 "go.temporal.io/server/common/log" 43 "go.temporal.io/server/common/log/tag" 44 ) 45 46 // Default token key provider 47 type defaultTokenKeyProvider struct { 48 config config.JWTKeyProvider 49 rsaKeys map[string]*rsa.PublicKey 50 ecKeys map[string]*ecdsa.PublicKey 51 keysLock sync.RWMutex 52 ticker *time.Ticker 53 logger log.Logger 54 stop chan bool 55 } 56 57 var _ TokenKeyProvider = (*defaultTokenKeyProvider)(nil) 58 59 func NewDefaultTokenKeyProvider(cfg *config.Authorization, logger log.Logger) *defaultTokenKeyProvider { 60 provider := defaultTokenKeyProvider{config: cfg.JWTKeyProvider, logger: logger} 61 provider.initialize() 62 return &provider 63 } 64 65 func (a *defaultTokenKeyProvider) initialize() { 66 a.rsaKeys = make(map[string]*rsa.PublicKey) 67 a.ecKeys = make(map[string]*ecdsa.PublicKey) 68 if a.config.HasSourceURIsConfigured() { 69 err := a.updateKeys() 70 if err != nil { 71 a.logger.Error("error during initial retrieval of token keys: ", tag.Error(err)) 72 } 73 } 74 if a.config.RefreshInterval > 0 { 75 a.stop = make(chan bool) 76 a.ticker = time.NewTicker(a.config.RefreshInterval) 77 go a.timerCallback() 78 } 79 } 80 81 func (a *defaultTokenKeyProvider) Close() { 82 a.ticker.Stop() 83 a.stop <- true 84 close(a.stop) 85 } 86 87 func (a *defaultTokenKeyProvider) RsaKey(alg string, kid string) (*rsa.PublicKey, error) { 88 if !strings.EqualFold(alg, jwt.SigningMethodRS256.Name) { 89 return nil, fmt.Errorf("unexpected signing algorithm: %s", alg) 90 } 91 92 a.keysLock.RLock() 93 key, found := a.rsaKeys[kid] 94 a.keysLock.RUnlock() 95 if !found { 96 return nil, fmt.Errorf("RSA key not found for key ID: %s", kid) 97 } 98 return key, nil 99 } 100 101 func (a *defaultTokenKeyProvider) EcdsaKey(alg string, kid string) (*ecdsa.PublicKey, error) { 102 if !strings.EqualFold(alg, jwt.SigningMethodES256.Name) { 103 return nil, fmt.Errorf("unexpected signing algorithm: %s", alg) 104 } 105 106 a.keysLock.RLock() 107 key, found := a.ecKeys[kid] 108 a.keysLock.RUnlock() 109 if !found { 110 return nil, fmt.Errorf("ECDSA key not found for key ID: %s", kid) 111 } 112 return key, nil 113 } 114 115 func (a *defaultTokenKeyProvider) SupportedMethods() []string { 116 return []string{jwt.SigningMethodRS256.Name, jwt.SigningMethodES256.Name} 117 } 118 119 func (a *defaultTokenKeyProvider) timerCallback() { 120 for { 121 select { 122 case <-a.stop: 123 return 124 case <-a.ticker.C: 125 } 126 if a.config.HasSourceURIsConfigured() { 127 err := a.updateKeys() 128 if err != nil { 129 a.logger.Error("error while refreshing token keys: ", tag.Error(err)) 130 } 131 } 132 } 133 } 134 135 func (a *defaultTokenKeyProvider) updateKeys() error { 136 if !a.config.HasSourceURIsConfigured() { 137 return fmt.Errorf("no URIs configured for retrieving token keys") 138 } 139 140 rsaKeys := make(map[string]*rsa.PublicKey) 141 ecKeys := make(map[string]*ecdsa.PublicKey) 142 143 for _, uri := range a.config.KeySourceURIs { 144 if strings.TrimSpace(uri) == "" { 145 continue 146 } 147 err := a.updateKeysFromURI(uri, rsaKeys, ecKeys) 148 if err != nil { 149 return err 150 } 151 } 152 // swap old keys with the new ones 153 a.keysLock.Lock() 154 a.rsaKeys = rsaKeys 155 a.ecKeys = ecKeys 156 a.keysLock.Unlock() 157 return nil 158 } 159 160 func (a *defaultTokenKeyProvider) updateKeysFromURI( 161 uri string, 162 rsaKeys map[string]*rsa.PublicKey, 163 ecKeys map[string]*ecdsa.PublicKey, 164 ) (err error) { 165 166 resp, err := http.Get(uri) 167 if err != nil { 168 return err 169 } 170 defer func() { 171 err = multierr.Combine(err, resp.Body.Close()) 172 }() 173 174 jwks := jose.JSONWebKeySet{} 175 err = json.NewDecoder(resp.Body).Decode(&jwks) 176 if err != nil { 177 return err 178 } 179 180 for _, k := range jwks.Keys { 181 switch k.Key.(type) { 182 case *rsa.PublicKey: 183 rsaKeys[k.KeyID] = k.Key.(*rsa.PublicKey) 184 case *ecdsa.PublicKey: 185 ecKeys[k.KeyID] = k.Key.(*ecdsa.PublicKey) 186 default: 187 a.logger.Warn(fmt.Sprintf("unexpected type of JWKS public key %s", k.Algorithm)) 188 } 189 } 190 return nil 191 } 192 193 func (a *defaultTokenKeyProvider) HmacKey(alg string, kid string) ([]byte, error) { 194 return nil, fmt.Errorf("unsupported key type HMAC for: %s", alg) 195 }