Add client_id support for OpenID (#8579)
- One click OpenID authorization on Login page - Add client_id help, config keys etc Thanks to @egorkaru @ihostage for the original work and testing.
This commit is contained in:
@@ -36,6 +36,7 @@ export MINIO_ACCESS_KEY=minio
|
||||
export MINIO_SECRET_KEY=minio123
|
||||
export MINIO_IDENTITY_OPENID_STATE="on"
|
||||
export MINIO_IDENTITY_OPENID_CONFIG_URL=https://localhost:9443/oauth2/oidcdiscovery/.well-known/openid-configuration
|
||||
export MINIO_IDENTITY_OPENID_CLIENT_ID="843351d4-1080-11ea-aa20-271ecba3924a"
|
||||
minio server /mnt/data
|
||||
```
|
||||
|
||||
@@ -49,6 +50,7 @@ export MINIO_ACCESS_KEY=aws_access_key
|
||||
export MINIO_SECRET_KEY=aws_secret_key
|
||||
export MINIO_IDENTITY_OPENID_STATE="on"
|
||||
export MINIO_IDENTITY_OPENID_CONFIG_URL=https://localhost:9443/oauth2/oidcdiscovery/.well-known/openid-configuration
|
||||
export MINIO_IDENTITY_OPENID_CLIENT_ID="843351d4-1080-11ea-aa20-271ecba3924a"
|
||||
export MINIO_ETCD_ENDPOINTS=http://localhost:2379
|
||||
minio gateway s3
|
||||
```
|
||||
|
||||
@@ -91,17 +91,19 @@ http://minio.cluster:9000?Action=AssumeRoleWithClientGrants&DurationSeconds=3600
|
||||
|
||||
## Testing
|
||||
```
|
||||
$ export MINIO_ACCESS_KEY=minio
|
||||
$ export MINIO_SECRET_KEY=minio123
|
||||
$ export MINIO_IDENTITY_OPENID_CONFIG_URL=https://localhost:9443/oauth2/oidcdiscovery/.well-known/openid-configuration
|
||||
$ export MINIO_POLICY_OPA_URL=http://localhost:8181/v1/data/httpapi/authz
|
||||
$ minio server /mnt/export
|
||||
export MINIO_ACCESS_KEY=minio
|
||||
export MINIO_SECRET_KEY=minio123
|
||||
export MINIO_IDENTITY_OPENID_STATE="on"
|
||||
export MINIO_IDENTITY_OPENID_CONFIG_URL=https://localhost:9443/oauth2/oidcdiscovery/.well-known/openid-configuration
|
||||
export MINIO_IDENTITY_OPENID_CLIENT_ID="7a243d56-1081-11ea-b1b9-0bad8bed6ca0"
|
||||
export MINIO_POLICY_OPA_URL=http://localhost:8181/v1/data/httpapi/authz
|
||||
minio server /mnt/export
|
||||
|
||||
$ mc admin config get myminio identity_openid
|
||||
identity_openid config_url="https://localhost:9443/oauth2/oidcdiscovery/.well-known/openid-configuration" state="on"
|
||||
mc admin config get myminio identity_openid
|
||||
identity_openid config_url="https://localhost:9443/oauth2/oidcdiscovery/.well-known/openid-configuration"
|
||||
|
||||
$ mc admin config get myminio policy_opa
|
||||
policy_opa auth_token="" state="on" url="http://localhost:8181/v1/data/httpapi/authz"
|
||||
mc admin config get myminio policy_opa
|
||||
policy_opa url="http://localhost:8181/v1/data/httpapi/authz" auth_token=
|
||||
```
|
||||
|
||||
Testing with an example
|
||||
|
||||
@@ -8,24 +8,22 @@ Keycloak is an open source Identity and Access Management solution aimed at mode
|
||||
- Download and start Keycloak server by following the [installation guide](https://www.keycloak.org/docs/latest/getting_started/index.html) (finish upto section 3.4)
|
||||
|
||||
## 2. Configure Keycloak
|
||||
|
||||
- Go to Clients -> Click on account -> Settings -> Enable `Implicit Flow`, then Save.
|
||||
- Go to Users -> Click on the user -> Attribute, add a new attribute `Key` is `policy`, `Value` is name of the policy in minio (ex: `readwrite`). Click Add and then Save.
|
||||
- Go to Clients -> Click on `account` -> Settings, set `Valid Redirect URIs` to `*`, expand `Advanced Settings` and set `Access Token Lifespan` to `1 Hours`, then Save.
|
||||
- Go to Clients -> Client on `account` -> Mappers -> Create, `Name` can be any text, `Mapper Type` is `User Attribute`, `User Attribute` is `policy`, `Token Claim Name` is `policy`, `Claim JSON Type` is `string`, then Save.
|
||||
- Open http://localhost:8080/auth/realms/demo/.well-known/openid-configuration and see if it has `authorization_endpoint` and `jwks_uri`
|
||||
|
||||
## 3. Configure MinIO
|
||||
|
||||
```
|
||||
$ export MINIO_ACCESS_KEY=minio
|
||||
$ export MINIO_SECRET_KEY=minio123
|
||||
$ minio server /mnt/export
|
||||
```
|
||||
|
||||
Set `identity_openid` config and restart MinIO
|
||||
|
||||
Set `identity_openid` config with `config_url`, `client_id` and restart MinIO
|
||||
```
|
||||
~ mc admin config set myminio identity_openid config_url="http://localhost:8080/auth/realms/demo/.well-known/openid-configuration" state="on"
|
||||
~ mc admin config set myminio identity_openid config_url="http://localhost:8080/auth/realms/demo/.well-known/openid-configuration" client_id="account"
|
||||
```
|
||||
|
||||
Once successfully set restart the MinIO instance.
|
||||
@@ -34,11 +32,10 @@ mc admin service restart myminio
|
||||
```
|
||||
|
||||
## 4. Using WebIdentiy API
|
||||
|
||||
Client ID and Client Secret can be found by clicking any of the clients listed [here](http://localhost:8080/auth/admin/master/console/#/realms/demo/clients). If you have followed the above steps docs, the default Client ID will be `account` and Client Secret can be found under `Credentials` tab.
|
||||
Client ID can be found by clicking any of the clients listed [here](http://localhost:8080/auth/admin/master/console/#/realms/demo/clients). If you have followed the above steps docs, the default Client ID will be `account`.
|
||||
|
||||
```
|
||||
$ go run web-identity.go -cid account -csec e61cb282-745b-4113-bece-29b921c735f0 -auth-ep http://localhost:8080/auth/realms/demo/protocol/openid-connect/auth -token-ep http://localhost:8080/auth/realms/demo/protocol/openid-connect/token -port 8888
|
||||
$ go run docs/sts/web-identity.go -cid account -csec 072e7f00-4289-469c-9ab2-bbe843c7f5a8 -config-ep "http://localhost:8080/auth/realms/demo/.well-known/openid-configuration" -port 8888
|
||||
2018/12/26 17:49:36 listening on http://localhost:8888/
|
||||
```
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"flag"
|
||||
@@ -32,7 +33,6 @@ import (
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
googleOAuth2 "golang.org/x/oauth2/google"
|
||||
|
||||
"github.com/minio/minio-go/v6"
|
||||
"github.com/minio/minio-go/v6/pkg/credentials"
|
||||
@@ -75,41 +75,89 @@ func randomState() string {
|
||||
}
|
||||
|
||||
var (
|
||||
stsEndpoint string
|
||||
authEndpoint string
|
||||
tokenEndpoint string
|
||||
clientID string
|
||||
clientSecret string
|
||||
port int
|
||||
stsEndpoint string
|
||||
configEndpoint string
|
||||
clientID string
|
||||
clientSec string
|
||||
port int
|
||||
)
|
||||
|
||||
// DiscoveryDoc - parses the output from openid-configuration
|
||||
// for example https://accounts.google.com/.well-known/openid-configuration
|
||||
type DiscoveryDoc struct {
|
||||
Issuer string `json:"issuer,omitempty"`
|
||||
AuthEndpoint string `json:"authorization_endpoint,omitempty"`
|
||||
TokenEndpoint string `json:"token_endpoint,omitempty"`
|
||||
UserInfoEndpoint string `json:"userinfo_endpoint,omitempty"`
|
||||
RevocationEndpoint string `json:"revocation_endpoint,omitempty"`
|
||||
JwksURI string `json:"jwks_uri,omitempty"`
|
||||
ResponseTypesSupported []string `json:"response_types_supported,omitempty"`
|
||||
SubjectTypesSupported []string `json:"subject_types_supported,omitempty"`
|
||||
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported,omitempty"`
|
||||
ScopesSupported []string `json:"scopes_supported,omitempty"`
|
||||
TokenEndpointAuthMethods []string `json:"token_endpoint_auth_methods_supported,omitempty"`
|
||||
ClaimsSupported []string `json:"claims_supported,omitempty"`
|
||||
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported,omitempty"`
|
||||
}
|
||||
|
||||
func parseDiscoveryDoc(ustr string) (DiscoveryDoc, error) {
|
||||
d := DiscoveryDoc{}
|
||||
req, err := http.NewRequest(http.MethodGet, ustr, nil)
|
||||
if err != nil {
|
||||
return d, err
|
||||
}
|
||||
clnt := http.Client{
|
||||
Transport: http.DefaultTransport,
|
||||
}
|
||||
resp, err := clnt.Do(req)
|
||||
if err != nil {
|
||||
return d, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return d, err
|
||||
}
|
||||
dec := json.NewDecoder(resp.Body)
|
||||
if err = dec.Decode(&d); err != nil {
|
||||
return d, err
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&stsEndpoint, "sts-ep", "http://localhost:9000", "STS endpoint")
|
||||
flag.StringVar(&authEndpoint, "auth-ep", googleOAuth2.Endpoint.AuthURL, "Auth endpoint")
|
||||
flag.StringVar(&tokenEndpoint, "token-ep", googleOAuth2.Endpoint.TokenURL, "Token endpoint")
|
||||
flag.StringVar(&configEndpoint, "config-ep",
|
||||
"https://accounts.google.com/.well-known/openid-configuration",
|
||||
"OpenID discovery document endpoint")
|
||||
flag.StringVar(&clientID, "cid", "", "Client ID")
|
||||
flag.StringVar(&clientSecret, "csec", "", "Client secret")
|
||||
flag.StringVar(&clientSec, "csec", "", "Client Secret")
|
||||
flag.IntVar(&port, "port", 8080, "Port")
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if clientID == "" || clientSecret == "" {
|
||||
if clientID == "" || clientSec == "" {
|
||||
flag.PrintDefaults()
|
||||
return
|
||||
}
|
||||
|
||||
ddoc, err := parseDiscoveryDoc(configEndpoint)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
config := oauth2.Config{
|
||||
ClientID: clientID,
|
||||
ClientSecret: clientSecret,
|
||||
ClientSecret: clientSec,
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: authEndpoint,
|
||||
TokenURL: tokenEndpoint,
|
||||
AuthURL: ddoc.AuthEndpoint,
|
||||
TokenURL: ddoc.TokenEndpoint,
|
||||
},
|
||||
RedirectURL: fmt.Sprintf("http://localhost:%v/oauth2/callback", port),
|
||||
Scopes: []string{"openid", "profile", "email"},
|
||||
RedirectURL: fmt.Sprintf("http://localhost:%d/oauth2/callback", port),
|
||||
Scopes: []string{"openid"},
|
||||
}
|
||||
|
||||
state := randomState()
|
||||
@@ -138,6 +186,7 @@ func main() {
|
||||
Expiry: int(oauth2Token.Expiry.Sub(time.Now().UTC()).Seconds()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
sts, err := credentials.NewSTSWebIdentity(stsEndpoint, getWebTokenExpiry)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
|
||||
@@ -91,13 +91,18 @@ http://minio.cluster:9000?Action=AssumeRoleWithWebIdentity&DurationSeconds=3600&
|
||||
|
||||
## Testing
|
||||
```
|
||||
$ export MINIO_ACCESS_KEY=minio
|
||||
$ export MINIO_SECRET_KEY=minio123
|
||||
$ export MINIO_IDENTITY_OPENID_CONFIG_URL=https://accounts.google.com/.well-known/openid-configuration
|
||||
$ minio server /mnt/export
|
||||
export MINIO_ACCESS_KEY=minio
|
||||
export MINIO_SECRET_KEY=minio123
|
||||
export MINIO_IDENTITY_OPENID_STATE="on"
|
||||
export MINIO_IDENTITY_OPENID_CLIENT_ID="843351d4-1080-11ea-aa20-271ecba3924a"
|
||||
export MINIO_IDENTITY_OPENID_CONFIG_URL=https://accounts.google.com/.well-known/openid-configuration
|
||||
minio server /mnt/export
|
||||
```
|
||||
|
||||
$ mc admin config get myminio identity_openid
|
||||
identity_openid config_url="https://accounts.google.com/.well-known/openid-configuration" state="on"
|
||||
or using `mc`
|
||||
```
|
||||
mc admin config get myminio identity_openid
|
||||
identity_openid config_url=https://accounts.google.com/.well-known/openid-configuration client_id=843351d4-1080-11ea-aa20-271ecba3924a
|
||||
```
|
||||
|
||||
Testing with an example
|
||||
@@ -120,20 +125,23 @@ $ go run web-identity.go -cid 204367807228-ok7601k6gj1pgge7m09h7d79co8p35xx.apps
|
||||
## MinIO Browser
|
||||
To support WebIdentity login on MinIO Browser
|
||||
|
||||
1. Set openid configuration and restart MinIO
|
||||
```
|
||||
mc admin config set myminio identity_openid jwks_url="<JWKS_URL>" config_url="<CONFIG_URL>"
|
||||
|
||||
mc admin service restart myminio
|
||||
```
|
||||
Sample URLs for Keycloak are
|
||||
`config_url` - `http://localhost:8080/auth/realms/demo/.well-known/openid-configuration`,
|
||||
`jwks_url` - `http://localhost:8080/auth/realms/demo/protocol/openid-connect/certs`
|
||||
- Set openid configuration and restart MinIO
|
||||
|
||||
JWT token returned by the Identity Provider should include a custom claim for the policy, this is required to create a STS user in MinIO. The name of the custom claim could be either `policy` or `<NAMESPACE_PREFIX>policy`.
|
||||
If there is no namespace then `policy_claim_prefix` can be ingored. For example if the custom claim name is `https://min.io/policy` then, `policy_claim_prefix` should be set as `https://min.io/`
|
||||
```
|
||||
mc admin config set myminio identity_openid config_url="<CONFIG_URL>" client_id="<client_identifier>"
|
||||
```
|
||||
|
||||
2. Open MinIO Browser and click `Log in with OpenID`
|
||||
3. Enter the `Client ID` obtained from Identity Provider and press ENTER
|
||||
4. The user will be redirected to the Identity Provider login page
|
||||
5. Upon successful login on Identity Provider page the user will be automatically logged into MinIO Browser
|
||||
```
|
||||
mc admin service restart myminio
|
||||
```
|
||||
|
||||
Sample URLs for Keycloak are
|
||||
|
||||
`config_url` - `http://localhost:8080/auth/realms/demo/.well-known/openid-configuration`
|
||||
|
||||
JWT token returned by the Identity Provider should include a custom claim for the policy, this is required to create a STS user in MinIO. The name of the custom claim could be either `policy` or `<NAMESPACE_PREFIX>policy`. If there is no namespace then `claim_prefix` can be ingored. For example if the custom claim name is `https://min.io/policy` then, `claim_prefix` should be set as `https://min.io/`.
|
||||
|
||||
- Open MinIO Browser and click `Log in with OpenID`
|
||||
- Enter the `Client ID` obtained from Identity Provider and press ENTER, if not you can set a `client_id` on server to avoid this step.
|
||||
- The user will be redirected to the Identity Provider login page
|
||||
- Upon successful login on Identity Provider page the user will be automatically logged into MinIO Browser
|
||||
|
||||
@@ -69,7 +69,9 @@ Using the above `access_token` we can perform an STS request to MinIO to get tem
|
||||
### 5. Setup MinIO with OpenID configuration URL
|
||||
MinIO server expects environment variable for OpenID configuration url as `MINIO_IDENTITY_OPENID_CONFIG_URL`, this environment variable takes a single entry.
|
||||
```
|
||||
export MINIO_IDENTITY_OPENID_STATE="on"
|
||||
export MINIO_IDENTITY_OPENID_CONFIG_URL=https://localhost:9443/oauth2/oidcdiscovery/.well-known/openid-configuration
|
||||
export MINIO_IDENTITY_OPENID_CLIENT_ID="843351d4-1080-11ea-aa20-271ecba3924a"
|
||||
minio server /mnt/data
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user