@@ -114,9 +114,9 @@ type Provider struct {
114
114
provHTTPClient * http.Client
115
115
}
116
116
117
- // derivedKey is the key used to compute the HMAC for signing the oauth state parameter
117
+ // DefaultDerivedKey is the key used to compute the HMAC for signing the oauth state parameter
118
118
// its derived using pbkdf on CONSOLE_IDP_HMAC_PASSPHRASE with CONSOLE_IDP_HMAC_SALT
119
- var derivedKey = func () []byte {
119
+ var DefaultDerivedKey = func () []byte {
120
120
return pbkdf2 .Key ([]byte (getPassphraseForIDPHmac ()), []byte (getSaltForIDPHmac ()), 4096 , 32 , sha1 .New )
121
121
}
122
122
@@ -304,11 +304,15 @@ type User struct {
304
304
Username string `json:"username"`
305
305
}
306
306
307
+ // StateKeyFunc - is a function that returns a key used in OAuth Authorization
308
+ // flow state generation and verification.
309
+ type StateKeyFunc func () []byte
310
+
307
311
// VerifyIdentity will contact the configured IDP to the user identity based on the authorization code and state
308
312
// if the user is valid, then it will contact MinIO to get valid sts credentials based on the identity provided by the IDP
309
- func (client * Provider ) VerifyIdentity (ctx context.Context , code , state string ) (* credentials.Credentials , error ) {
313
+ func (client * Provider ) VerifyIdentity (ctx context.Context , code , state string , keyFunc StateKeyFunc ) (* credentials.Credentials , error ) {
310
314
// verify the provided state is valid (prevents CSRF attacks)
311
- if err := validateOauth2State (state ); err != nil {
315
+ if err := validateOauth2State (state , keyFunc ); err != nil {
312
316
return nil , err
313
317
}
314
318
getWebTokenExpiry := func () (* credentials.WebIdentityToken , error ) {
@@ -357,9 +361,9 @@ func (client *Provider) VerifyIdentity(ctx context.Context, code, state string)
357
361
}
358
362
359
363
// VerifyIdentityForOperator will contact the configured IDP and validate the user identity based on the authorization code and state
360
- func (client * Provider ) VerifyIdentityForOperator (ctx context.Context , code , state string ) (* xoauth2.Token , error ) {
364
+ func (client * Provider ) VerifyIdentityForOperator (ctx context.Context , code , state string , keyFunc StateKeyFunc ) (* xoauth2.Token , error ) {
361
365
// verify the provided state is valid (prevents CSRF attacks)
362
- if err := validateOauth2State (state ); err != nil {
366
+ if err := validateOauth2State (state , keyFunc ); err != nil {
363
367
return nil , err
364
368
}
365
369
customCtx := context .WithValue (ctx , oauth2 .HTTPClient , client .provHTTPClient )
@@ -376,7 +380,7 @@ func (client *Provider) VerifyIdentityForOperator(ctx context.Context, code, sta
376
380
// validateOauth2State validates the provided state was originated using the same
377
381
// instance (or one configured using the same secrets) of Console, this is basically used to prevent CSRF attacks
378
382
// https://security.stackexchange.com/questions/20187/oauth2-cross-site-request-forgery-and-state-parameter
379
- func validateOauth2State (state string ) error {
383
+ func validateOauth2State (state string , keyFunc StateKeyFunc ) error {
380
384
// state contains a base64 encoded string that may ends with "==", the browser encodes that to "%3D%3D"
381
385
// query unescape is need it before trying to decode the base64 string
382
386
encodedMessage , err := url .QueryUnescape (state )
@@ -396,7 +400,7 @@ func validateOauth2State(state string) error {
396
400
// extract the state and hmac
397
401
incomingState , incomingHmac := s [0 ], s [1 ]
398
402
// validate that hmac(incomingState + pbkdf2(secret, salt)) == incomingHmac
399
- if calculatedHmac := utils .ComputeHmac256 (incomingState , derivedKey ()); calculatedHmac != incomingHmac {
403
+ if calculatedHmac := utils .ComputeHmac256 (incomingState , keyFunc ()); calculatedHmac != incomingHmac {
400
404
return fmt .Errorf ("oauth2 state is invalid, expected %s, got %s" , calculatedHmac , incomingHmac )
401
405
}
402
406
return nil
@@ -429,16 +433,16 @@ func parseDiscoveryDoc(ustr string, httpClient *http.Client) (DiscoveryDoc, erro
429
433
}
430
434
431
435
// GetRandomStateWithHMAC computes message + hmac(message, pbkdf2(key, salt)) to be used as state during the oauth authorization
432
- func GetRandomStateWithHMAC (length int ) string {
436
+ func GetRandomStateWithHMAC (length int , keyFunc StateKeyFunc ) string {
433
437
state := utils .RandomCharString (length )
434
- hmac := utils .ComputeHmac256 (state , derivedKey ())
438
+ hmac := utils .ComputeHmac256 (state , keyFunc ())
435
439
return base64 .StdEncoding .EncodeToString ([]byte (fmt .Sprintf ("%s:%s" , state , hmac )))
436
440
}
437
441
438
442
// GenerateLoginURL returns a new login URL based on the configured IDP
439
- func (client * Provider ) GenerateLoginURL () string {
443
+ func (client * Provider ) GenerateLoginURL (keyFunc StateKeyFunc ) string {
440
444
// generates random state and sign it using HMAC256
441
- state := GetRandomStateWithHMAC (25 )
445
+ state := GetRandomStateWithHMAC (25 , keyFunc )
442
446
loginURL := client .oauth2Config .AuthCodeURL (state )
443
447
return strings .TrimSpace (loginURL )
444
448
}
0 commit comments