@@ -114,9 +114,9 @@ type Provider struct {
114114 provHTTPClient * http.Client
115115}
116116
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
118118// its derived using pbkdf on CONSOLE_IDP_HMAC_PASSPHRASE with CONSOLE_IDP_HMAC_SALT
119- var derivedKey = func () []byte {
119+ var DefaultDerivedKey = func () []byte {
120120 return pbkdf2 .Key ([]byte (getPassphraseForIDPHmac ()), []byte (getSaltForIDPHmac ()), 4096 , 32 , sha1 .New )
121121}
122122
@@ -304,11 +304,15 @@ type User struct {
304304 Username string `json:"username"`
305305}
306306
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+
307311// VerifyIdentity will contact the configured IDP to the user identity based on the authorization code and state
308312// 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 ) {
310314 // verify the provided state is valid (prevents CSRF attacks)
311- if err := validateOauth2State (state ); err != nil {
315+ if err := validateOauth2State (state , keyFunc ); err != nil {
312316 return nil , err
313317 }
314318 getWebTokenExpiry := func () (* credentials.WebIdentityToken , error ) {
@@ -357,9 +361,9 @@ func (client *Provider) VerifyIdentity(ctx context.Context, code, state string)
357361}
358362
359363// 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 ) {
361365 // verify the provided state is valid (prevents CSRF attacks)
362- if err := validateOauth2State (state ); err != nil {
366+ if err := validateOauth2State (state , keyFunc ); err != nil {
363367 return nil , err
364368 }
365369 customCtx := context .WithValue (ctx , oauth2 .HTTPClient , client .provHTTPClient )
@@ -376,7 +380,7 @@ func (client *Provider) VerifyIdentityForOperator(ctx context.Context, code, sta
376380// validateOauth2State validates the provided state was originated using the same
377381// instance (or one configured using the same secrets) of Console, this is basically used to prevent CSRF attacks
378382// 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 {
380384 // state contains a base64 encoded string that may ends with "==", the browser encodes that to "%3D%3D"
381385 // query unescape is need it before trying to decode the base64 string
382386 encodedMessage , err := url .QueryUnescape (state )
@@ -396,7 +400,7 @@ func validateOauth2State(state string) error {
396400 // extract the state and hmac
397401 incomingState , incomingHmac := s [0 ], s [1 ]
398402 // 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 {
400404 return fmt .Errorf ("oauth2 state is invalid, expected %s, got %s" , calculatedHmac , incomingHmac )
401405 }
402406 return nil
@@ -429,16 +433,16 @@ func parseDiscoveryDoc(ustr string, httpClient *http.Client) (DiscoveryDoc, erro
429433}
430434
431435// 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 {
433437 state := utils .RandomCharString (length )
434- hmac := utils .ComputeHmac256 (state , derivedKey ())
438+ hmac := utils .ComputeHmac256 (state , keyFunc ())
435439 return base64 .StdEncoding .EncodeToString ([]byte (fmt .Sprintf ("%s:%s" , state , hmac )))
436440}
437441
438442// 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 {
440444 // generates random state and sign it using HMAC256
441- state := GetRandomStateWithHMAC (25 )
445+ state := GetRandomStateWithHMAC (25 , keyFunc )
442446 loginURL := client .oauth2Config .AuthCodeURL (state )
443447 return strings .TrimSpace (loginURL )
444448}
0 commit comments