github.com/minio/console@v1.4.1/pkg/auth/idp/oauth2/config.go (about)

     1  // This file is part of MinIO Console Server
     2  // Copyright (c) 2021 MinIO, Inc.
     3  //
     4  // This program is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Affero General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // This program is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  // GNU Affero General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Affero General Public License
    15  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Package oauth2 contains all the necessary configurations to initialize the
    18  // idp communication using oauth2 protocol
    19  package oauth2
    20  
    21  import (
    22  	"crypto/sha1"
    23  	"fmt"
    24  	"net/http"
    25  	"strings"
    26  
    27  	"github.com/minio/console/pkg/auth/token"
    28  	"github.com/minio/minio-go/v7/pkg/set"
    29  	"github.com/minio/pkg/v3/env"
    30  	"golang.org/x/crypto/pbkdf2"
    31  	"golang.org/x/oauth2"
    32  	xoauth2 "golang.org/x/oauth2"
    33  )
    34  
    35  // ProviderConfig - OpenID IDP Configuration for console.
    36  type ProviderConfig struct {
    37  	URL                      string
    38  	DisplayName              string // user-provided - can be empty
    39  	ClientID, ClientSecret   string
    40  	HMACSalt, HMACPassphrase string
    41  	Scopes                   string
    42  	Userinfo                 bool
    43  	RedirectCallbackDynamic  bool
    44  	RedirectCallback         string
    45  	EndSessionEndpoint       string
    46  	RoleArn                  string // can be empty
    47  }
    48  
    49  // GetOauth2Provider instantiates a new oauth2 client using the configured credentials
    50  // it returns a *Provider object that contains the necessary configuration to initiate an
    51  // oauth2 authentication flow.
    52  //
    53  // We only support Authentication with the Authorization Code Flow - spec:
    54  // https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth
    55  func (pc ProviderConfig) GetOauth2Provider(name string, scopes []string, r *http.Request, clnt *http.Client) (provider *Provider, err error) {
    56  	var ddoc DiscoveryDoc
    57  	ddoc, err = parseDiscoveryDoc(r.Context(), pc.URL, clnt)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  
    62  	supportedResponseTypes := set.NewStringSet()
    63  	for _, responseType := range ddoc.ResponseTypesSupported {
    64  		// FIXME: ResponseTypesSupported is a JSON array of strings - it
    65  		// may not actually have strings with spaces inside them -
    66  		// making the following code unnecessary.
    67  		for _, s := range strings.Fields(responseType) {
    68  			supportedResponseTypes.Add(s)
    69  		}
    70  	}
    71  
    72  	isSupported := requiredResponseTypes.Difference(supportedResponseTypes).IsEmpty()
    73  	if !isSupported {
    74  		return nil, fmt.Errorf("expected 'code' response type - got %s, login not allowed", ddoc.ResponseTypesSupported)
    75  	}
    76  
    77  	// If provided scopes are empty we use the user configured list or a default list.
    78  	if len(scopes) == 0 {
    79  		for _, s := range strings.Split(pc.Scopes, ",") {
    80  			w := strings.TrimSpace(s)
    81  			if w == "" {
    82  				continue
    83  			}
    84  			scopes = append(scopes, w)
    85  		}
    86  		if len(scopes) == 0 {
    87  			scopes = defaultScopes
    88  		}
    89  	}
    90  
    91  	redirectURL := pc.RedirectCallback
    92  	if pc.RedirectCallbackDynamic {
    93  		// dynamic redirect if set, will generate redirect URLs
    94  		// dynamically based on incoming requests.
    95  		redirectURL = getLoginCallbackURL(r)
    96  	}
    97  
    98  	// add "openid" scope always.
    99  	scopes = append(scopes, "openid")
   100  
   101  	client := new(Provider)
   102  	client.oauth2Config = &xoauth2.Config{
   103  		ClientID:     pc.ClientID,
   104  		ClientSecret: pc.ClientSecret,
   105  		RedirectURL:  redirectURL,
   106  		Endpoint: oauth2.Endpoint{
   107  			AuthURL:  ddoc.AuthEndpoint,
   108  			TokenURL: ddoc.TokenEndpoint,
   109  		},
   110  		Scopes: scopes,
   111  	}
   112  
   113  	client.IDPName = name
   114  	client.UserInfo = pc.Userinfo
   115  	client.client = clnt
   116  
   117  	return client, nil
   118  }
   119  
   120  // GetStateKeyFunc - return the key function used to generate the authorization
   121  // code flow state parameter.
   122  
   123  func (pc ProviderConfig) GetStateKeyFunc() StateKeyFunc {
   124  	return func() []byte {
   125  		return pbkdf2.Key([]byte(pc.HMACPassphrase), []byte(pc.HMACSalt), 4096, 32, sha1.New)
   126  	}
   127  }
   128  
   129  func (pc ProviderConfig) GetARNInf() string {
   130  	return pc.RoleArn
   131  }
   132  
   133  type OpenIDPCfg map[string]ProviderConfig
   134  
   135  func GetSTSEndpoint() string {
   136  	return strings.TrimSpace(env.Get(ConsoleMinIOServer, "http://localhost:9000"))
   137  }
   138  
   139  func GetIDPURL() string {
   140  	return env.Get(ConsoleIDPURL, "")
   141  }
   142  
   143  func GetIDPClientID() string {
   144  	return env.Get(ConsoleIDPClientID, "")
   145  }
   146  
   147  func GetIDPUserInfo() bool {
   148  	return env.Get(ConsoleIDPUserInfo, "") == "on"
   149  }
   150  
   151  func GetIDPSecret() string {
   152  	return env.Get(ConsoleIDPSecret, "")
   153  }
   154  
   155  // Public endpoint used by the identity oidcProvider when redirecting
   156  // the user after identity verification
   157  func GetIDPCallbackURL() string {
   158  	return env.Get(ConsoleIDPCallbackURL, "")
   159  }
   160  
   161  func GetIDPCallbackURLDynamic() bool {
   162  	return env.Get(ConsoleIDPCallbackURLDynamic, "") == "on"
   163  }
   164  
   165  func IsIDPEnabled() bool {
   166  	return GetIDPURL() != "" &&
   167  		GetIDPClientID() != ""
   168  }
   169  
   170  // GetPassphraseForIDPHmac returns passphrase for the pbkdf2 function used to sign the oauth2 state parameter
   171  func getPassphraseForIDPHmac() string {
   172  	return env.Get(ConsoleIDPHmacPassphrase, token.GetPBKDFPassphrase())
   173  }
   174  
   175  // GetSaltForIDPHmac returns salt for the pbkdf2 function used to sign the oauth2 state parameter
   176  func getSaltForIDPHmac() string {
   177  	return env.Get(ConsoleIDPHmacSalt, token.GetPBKDFSalt())
   178  }
   179  
   180  // getIDPScopes return default scopes during the IDP login request
   181  func getIDPScopes() string {
   182  	return env.Get(ConsoleIDPScopes, "openid,profile,email")
   183  }