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 }