fix: add support for userInfo endpoint (#1048)
With support for GitLab OpenID service, we need to make sure to support userInfo endpoint for this MinIO server requires that access_token is additionally sent along with id_token as before to make sure we can fetch additional claims from /userinfo endpoint. This PR brings support on console for this feature. Refer https://github.com/minio/minio/pull/12469
This commit is contained in:
@@ -29,49 +29,53 @@ func GetSTSEndpoint() string {
|
||||
return strings.TrimSpace(env.Get(ConsoleMinIOServer, "http://localhost:9000"))
|
||||
}
|
||||
|
||||
func GetIdpURL() string {
|
||||
return env.Get(ConsoleIdpURL, "")
|
||||
func GetIDPURL() string {
|
||||
return env.Get(ConsoleIDPURL, "")
|
||||
}
|
||||
|
||||
func GetIdpClientID() string {
|
||||
return env.Get(ConsoleIdpClientID, "")
|
||||
func GetIDPClientID() string {
|
||||
return env.Get(ConsoleIDPClientID, "")
|
||||
}
|
||||
|
||||
func GetIdpSecret() string {
|
||||
return env.Get(ConsoleIdpSecret, "")
|
||||
func GetIDPUserInfo() bool {
|
||||
return env.Get(ConsoleIDPUserInfo, "") == "on"
|
||||
}
|
||||
|
||||
func GetIDPSecret() string {
|
||||
return env.Get(ConsoleIDPSecret, "")
|
||||
}
|
||||
|
||||
// Public endpoint used by the identity oidcProvider when redirecting the user after identity verification
|
||||
func GetIdpCallbackURL() string {
|
||||
return env.Get(ConsoleIdpCallbackURL, "")
|
||||
func GetIDPCallbackURL() string {
|
||||
return env.Get(ConsoleIDPCallbackURL, "")
|
||||
}
|
||||
|
||||
func IsIdpEnabled() bool {
|
||||
return GetIdpURL() != "" &&
|
||||
GetIdpClientID() != "" &&
|
||||
GetIdpCallbackURL() != ""
|
||||
func IsIDPEnabled() bool {
|
||||
return GetIDPURL() != "" &&
|
||||
GetIDPClientID() != "" &&
|
||||
GetIDPCallbackURL() != ""
|
||||
}
|
||||
|
||||
var defaultPassphraseForIdpHmac = utils.RandomCharString(64)
|
||||
var defaultPassphraseForIDPHmac = utils.RandomCharString(64)
|
||||
|
||||
// GetPassphraseForIdpHmac returns passphrase for the pbkdf2 function used to sign the oauth2 state parameter
|
||||
func getPassphraseForIdpHmac() string {
|
||||
return env.Get(ConsoleIdpHmacPassphrase, defaultPassphraseForIdpHmac)
|
||||
// GetPassphraseForIDPHmac returns passphrase for the pbkdf2 function used to sign the oauth2 state parameter
|
||||
func getPassphraseForIDPHmac() string {
|
||||
return env.Get(ConsoleIDPHmacPassphrase, defaultPassphraseForIDPHmac)
|
||||
}
|
||||
|
||||
var defaultSaltForIdpHmac = utils.RandomCharString(64)
|
||||
var defaultSaltForIDPHmac = utils.RandomCharString(64)
|
||||
|
||||
// GetSaltForIdpHmac returns salt for the pbkdf2 function used to sign the oauth2 state parameter
|
||||
func getSaltForIdpHmac() string {
|
||||
return env.Get(ConsoleIdpHmacSalt, defaultSaltForIdpHmac)
|
||||
// GetSaltForIDPHmac returns salt for the pbkdf2 function used to sign the oauth2 state parameter
|
||||
func getSaltForIDPHmac() string {
|
||||
return env.Get(ConsoleIDPHmacSalt, defaultSaltForIDPHmac)
|
||||
}
|
||||
|
||||
// getIdpScopes return default scopes during the IDP login request
|
||||
func getIdpScopes() string {
|
||||
// getIDPScopes return default scopes during the IDP login request
|
||||
func getIDPScopes() string {
|
||||
return env.Get(ConsoleIDPScopes, "openid,profile,email")
|
||||
}
|
||||
|
||||
// getIdpTokenExpiration return default token expiration for access token (in seconds)
|
||||
func getIdpTokenExpiration() string {
|
||||
// getIDPTokenExpiration return default token expiration for access token (in seconds)
|
||||
func getIDPTokenExpiration() string {
|
||||
return env.Get(ConsoleIDPTokenExpiration, "3600")
|
||||
}
|
||||
|
||||
@@ -16,15 +16,16 @@
|
||||
|
||||
package oauth2
|
||||
|
||||
// Environment constants for console IDP/SSO configuration
|
||||
const (
|
||||
// const for idp configuration
|
||||
ConsoleMinIOServer = "CONSOLE_MINIO_SERVER"
|
||||
ConsoleIdpURL = "CONSOLE_IDP_URL"
|
||||
ConsoleIdpClientID = "CONSOLE_IDP_CLIENT_ID"
|
||||
ConsoleIdpSecret = "CONSOLE_IDP_SECRET"
|
||||
ConsoleIdpCallbackURL = "CONSOLE_IDP_CALLBACK"
|
||||
ConsoleIdpHmacPassphrase = "CONSOLE_IDP_HMAC_PASSPHRASE"
|
||||
ConsoleIdpHmacSalt = "CONSOLE_IDP_HMAC_SALT"
|
||||
ConsoleIDPURL = "CONSOLE_IDP_URL"
|
||||
ConsoleIDPClientID = "CONSOLE_IDP_CLIENT_ID"
|
||||
ConsoleIDPSecret = "CONSOLE_IDP_SECRET"
|
||||
ConsoleIDPCallbackURL = "CONSOLE_IDP_CALLBACK"
|
||||
ConsoleIDPHmacPassphrase = "CONSOLE_IDP_HMAC_PASSPHRASE"
|
||||
ConsoleIDPHmacSalt = "CONSOLE_IDP_HMAC_SALT"
|
||||
ConsoleIDPScopes = "CONSOLE_IDP_SCOPES"
|
||||
ConsoleIDPUserInfo = "CONSOLE_IDP_USERINFO"
|
||||
ConsoleIDPTokenExpiration = "CONSOLE_IDP_TOKEN_EXPIRATION"
|
||||
)
|
||||
|
||||
@@ -88,7 +88,9 @@ type Provider struct {
|
||||
// often available via site-specific packages, such as
|
||||
// google.Endpoint or github.Endpoint.
|
||||
// - Scopes specifies optional requested permissions.
|
||||
ClientID string
|
||||
ClientID string
|
||||
// if enabled means that we need extrace access_token as well
|
||||
UserInfo bool
|
||||
oauth2Config Configuration
|
||||
oidcProvider *oidc.Provider
|
||||
provHTTPClient *http.Client
|
||||
@@ -97,7 +99,7 @@ type Provider struct {
|
||||
// derivedKey is the key used to compute the HMAC for signing the oauth state parameter
|
||||
// its derived using pbkdf on CONSOLE_IDP_HMAC_PASSPHRASE with CONSOLE_IDP_HMAC_SALT
|
||||
var derivedKey = func() []byte {
|
||||
return pbkdf2.Key([]byte(getPassphraseForIdpHmac()), []byte(getSaltForIdpHmac()), 4096, 32, sha1.New)
|
||||
return pbkdf2.Key([]byte(getPassphraseForIDPHmac()), []byte(getSaltForIDPHmac()), 4096, 32, sha1.New)
|
||||
}
|
||||
|
||||
// NewOauth2ProviderClient instantiates a new oauth2 client using the configured credentials
|
||||
@@ -105,34 +107,30 @@ var derivedKey = func() []byte {
|
||||
// oauth2 authentication flow
|
||||
func NewOauth2ProviderClient(ctx context.Context, scopes []string, httpClient *http.Client) (*Provider, error) {
|
||||
customCtx := oidc.ClientContext(ctx, httpClient)
|
||||
provider, err := oidc.NewProvider(customCtx, GetIdpURL())
|
||||
provider, err := oidc.NewProvider(customCtx, GetIDPURL())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// if google, change scopes
|
||||
u, err := url.Parse(GetIdpURL())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// below verification should not be necessary if the user configure exactly the
|
||||
// scopes he need, will be removed on a future release
|
||||
if u.Host == "google.com" {
|
||||
scopes = []string{oidc.ScopeOpenID}
|
||||
}
|
||||
|
||||
// If provided scopes are empty we use a default list or the user configured list
|
||||
if len(scopes) == 0 {
|
||||
scopes = strings.Split(getIdpScopes(), ",")
|
||||
scopes = strings.Split(getIDPScopes(), ",")
|
||||
}
|
||||
|
||||
// add "openid" scope always.
|
||||
scopes = append(scopes, oidc.ScopeOpenID)
|
||||
|
||||
client := new(Provider)
|
||||
client.oauth2Config = &xoauth2.Config{
|
||||
ClientID: GetIdpClientID(),
|
||||
ClientSecret: GetIdpSecret(),
|
||||
RedirectURL: GetIdpCallbackURL(),
|
||||
ClientID: GetIDPClientID(),
|
||||
ClientSecret: GetIDPSecret(),
|
||||
RedirectURL: GetIDPCallbackURL(),
|
||||
Endpoint: provider.Endpoint(),
|
||||
Scopes: scopes,
|
||||
}
|
||||
client.oidcProvider = provider
|
||||
client.ClientID = GetIdpClientID()
|
||||
client.ClientID = GetIDPClientID()
|
||||
client.UserInfo = GetIDPUserInfo()
|
||||
client.provHTTPClient = httpClient
|
||||
|
||||
return client, nil
|
||||
@@ -184,18 +182,26 @@ func (client *Provider) VerifyIdentity(ctx context.Context, code, state string)
|
||||
|
||||
// check if user configured a hardcoded expiration for console via env variables
|
||||
// and override the incoming expiration
|
||||
userConfiguredExpiration := getIdpTokenExpiration()
|
||||
userConfiguredExpiration := getIDPTokenExpiration()
|
||||
if userConfiguredExpiration != "" {
|
||||
expiration, _ = strconv.Atoi(userConfiguredExpiration)
|
||||
}
|
||||
idToken := oauth2Token.Extra("id_token")
|
||||
if idToken == nil {
|
||||
return nil, errors.New("returned token is missing id_token claim")
|
||||
return nil, errors.New("missing id_token")
|
||||
}
|
||||
return &credentials.WebIdentityToken{
|
||||
token := &credentials.WebIdentityToken{
|
||||
Token: idToken.(string),
|
||||
Expiry: expiration,
|
||||
}, nil
|
||||
}
|
||||
if client.UserInfo { // look for access_token only if userinfo is requested.
|
||||
accessToken := oauth2Token.Extra("access_token")
|
||||
if accessToken == nil {
|
||||
return nil, errors.New("missing access_token")
|
||||
}
|
||||
token.AccessToken = accessToken.(string)
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
stsEndpoint := GetSTSEndpoint()
|
||||
sts := credentials.New(&credentials.STSWebIdentity{
|
||||
|
||||
Reference in New Issue
Block a user