From 4bca9815822256e39c76293d3148a12479951104 Mon Sep 17 00:00:00 2001 From: Samuel Lang <slang@bethel.jw.org> Date: Thu, 28 Feb 2019 13:51:09 +0100 Subject: [PATCH] [KEYCLOAK-9703] Add client auth method option to gatekeeper config Gatekeeper is still uses go-oidc v1, which makes Gatekeeper to send auth-code to exchange for access token at server's token endpoint with both client-authentication methods: basic and post. As soon as Gatekeeper gets an upgrade to go-oidc v2 this commit no longer will be necessary. --- config.go | 4 ++++ config_test.go | 55 +++++++++++++++++++++++++++++++------------------- doc.go | 4 ++++ handlers.go | 16 +++++++++++++-- oauth.go | 4 ++-- 5 files changed, 58 insertions(+), 25 deletions(-) diff --git a/config.go b/config.go index cd4410a..d49d4bd 100644 --- a/config.go +++ b/config.go @@ -35,6 +35,7 @@ func newDefaultConfig() *Config { return &Config{ AccessTokenDuration: time.Duration(720) * time.Hour, + ClientAuthMethod: authMethodBasic, CookieAccessName: accessCookie, CookieRefreshName: refreshCookie, EnableAuthorizationCookies: true, @@ -189,6 +190,9 @@ func (r *Config) isValid() error { return fmt.Errorf("the store url is invalid, error: %s", err) } } + if r.ClientAuthMethod != authMethodBasic && r.ClientAuthMethod != authMethodBody { + return fmt.Errorf("invalid client auth method %q (valid values: %s, %s)", r.ClientAuthMethod, authMethodBasic, authMethodBody) + } } // check: ensure each of the resource are valid for _, resource := range r.Resources { diff --git a/config_test.go b/config_test.go index 5234291..8f6064d 100644 --- a/config_test.go +++ b/config_test.go @@ -35,39 +35,44 @@ func TestIsConfig(t *testing.T) { }, { Config: &Config{ - DiscoveryURL: "http://127.0.0.1:8080", + DiscoveryURL: "http://127.0.0.1:8080", + ClientAuthMethod: "secret-basic", }, }, { Config: &Config{ - DiscoveryURL: "http://127.0.0.1:8080", - ClientID: "client", - ClientSecret: "client", + DiscoveryURL: "http://127.0.0.1:8080", + ClientAuthMethod: "secret-basic", + ClientID: "client", + ClientSecret: "client", }, }, { Config: &Config{ - Listen: ":8080", - DiscoveryURL: "http://127.0.0.1:8080", - ClientID: "client", - ClientSecret: "client", - RedirectionURL: "http://120.0.0.1", + Listen: ":8080", + DiscoveryURL: "http://127.0.0.1:8080", + ClientAuthMethod: "secret-basic", + ClientID: "client", + ClientSecret: "client", + RedirectionURL: "http://120.0.0.1", }, }, { Config: &Config{ - Listen: ":8080", - DiscoveryURL: "http://127.0.0.1:8080", - ClientID: "client", - ClientSecret: "client", - RedirectionURL: "http://120.0.0.1", - Upstream: "http://120.0.0.1", + Listen: ":8080", + DiscoveryURL: "http://127.0.0.1:8080", + ClientAuthMethod: "secret-basic", + ClientID: "client", + ClientSecret: "client", + RedirectionURL: "http://120.0.0.1", + Upstream: "http://120.0.0.1", }, }, { Config: &Config{ Listen: ":8080", DiscoveryURL: "http://127.0.0.1:8080", + ClientAuthMethod: "secret-basic", ClientID: "client", ClientSecret: "client", RedirectionURL: "http://120.0.0.1", @@ -81,6 +86,7 @@ func TestIsConfig(t *testing.T) { Config: &Config{ Listen: ":8080", DiscoveryURL: "http://127.0.0.1:8080", + ClientAuthMethod: "secret-basic", ClientID: "client", ClientSecret: "client", RedirectionURL: "http://120.0.0.1", @@ -93,6 +99,7 @@ func TestIsConfig(t *testing.T) { Config: &Config{ Listen: ":8080", DiscoveryURL: "http://127.0.0.1:8080", + ClientAuthMethod: "secret-basic", ClientID: "client", ClientSecret: "client", RedirectionURL: "http://120.0.0.1", @@ -103,12 +110,13 @@ func TestIsConfig(t *testing.T) { }, { Config: &Config{ - Listen: ":8080", - DiscoveryURL: "http://127.0.0.1:8080", - ClientID: "client", - ClientSecret: "client", - RedirectionURL: "http://120.0.0.1", - Upstream: "http://120.0.0.1", + Listen: ":8080", + DiscoveryURL: "http://127.0.0.1:8080", + ClientAuthMethod: "secret-basic", + ClientID: "client", + ClientSecret: "client", + RedirectionURL: "http://120.0.0.1", + Upstream: "http://120.0.0.1", MatchClaims: map[string]string{ "test": "&&&[", }, @@ -129,6 +137,7 @@ func TestIsConfig(t *testing.T) { { Config: &Config{ DiscoveryURL: "http://127.0.0.1:8080", + ClientAuthMethod: "secret-basic", ClientID: "client", ClientSecret: "client", RedirectionURL: "http://120.0.0.1", @@ -141,6 +150,7 @@ func TestIsConfig(t *testing.T) { Config: &Config{ Listen: ":8080", DiscoveryURL: "http://127.0.0.1:8080", + ClientAuthMethod: "secret-basic", ClientID: "client", ClientSecret: "client", RedirectionURL: "http://120.0.0.1", @@ -152,6 +162,7 @@ func TestIsConfig(t *testing.T) { Config: &Config{ Listen: ":8080", DiscoveryURL: "http://127.0.0.1:8080", + ClientAuthMethod: "secret-basic", ClientID: "client", ClientSecret: "client", RedirectionURL: "http://120.0.0.1", @@ -164,6 +175,7 @@ func TestIsConfig(t *testing.T) { Config: &Config{ Listen: ":8080", DiscoveryURL: "http://127.0.0.1:8080", + ClientAuthMethod: "secret-basic", ClientID: "client", ClientSecret: "client", RedirectionURL: "http://120.0.0.1", @@ -177,6 +189,7 @@ func TestIsConfig(t *testing.T) { Config: &Config{ Listen: ":8080", DiscoveryURL: "http://127.0.0.1:8080", + ClientAuthMethod: "secret-basic", ClientID: "client", ClientSecret: "client", RedirectionURL: "https://120.0.0.1", diff --git a/doc.go b/doc.go index 0b11c4b..cfa461d 100644 --- a/doc.go +++ b/doc.go @@ -70,6 +70,8 @@ const ( unsecureScheme = "http" secureScheme = "https" anyMethod = "ANY" + authMethodBasic = "secret-basic" + authMethodBody = "secret-body" _ contextKey = iota contextScopeName @@ -248,6 +250,8 @@ type Config struct { // AccessTokenDuration is default duration applied to the access token cookie AccessTokenDuration time.Duration `json:"access-token-duration" yaml:"access-token-duration" usage:"fallback cookie duration for the access token when using refresh tokens"` + // ClientAuthMethod defines the method for authenticating the oauth client to the server + ClientAuthMethod string `json:"client-auth-method" yaml:"client-auth-method" usage:"the auth method to use with oauth (secret-basic, secret-body)" env:"CLIENT_AUTH_METHOD"` // CookieDomain is a list of domains the cookie is available to CookieDomain string `json:"cookie-domain" yaml:"cookie-domain" usage:"domain the access cookie is available to, defaults host header"` // CookieAccessName is the name of the access cookie holding the access token diff --git a/handlers.go b/handlers.go index 0c5520e..a635b56 100644 --- a/handlers.go +++ b/handlers.go @@ -71,7 +71,7 @@ func (r *oauthProxy) oauthAuthorizationHandler(w http.ResponseWriter, req *http. w.WriteHeader(http.StatusNotAcceptable) return } - client, err := r.getOAuthClient(r.getRedirectionURL(w, req)) + client, err := r.getOAuthClient(r.getRedirectionURL(w, req), getClientAuthMethod(r.config.ClientAuthMethod)) if err != nil { r.log.Error("failed to retrieve the oauth client for authorization", zap.Error(err)) w.WriteHeader(http.StatusInternalServerError) @@ -103,6 +103,18 @@ func (r *oauthProxy) oauthAuthorizationHandler(w http.ResponseWriter, req *http. r.redirectToURL(authURL, w, req, http.StatusTemporaryRedirect) } +// getClientAuthMethod maps the config value CLIENT_AUTH_METHOD to valid OAuth2 auth method keys +func getClientAuthMethod(authMethod string) string { + switch authMethod { + case authMethodBasic: + return oauth2.AuthMethodClientSecretBasic + case authMethodBody: + return oauth2.AuthMethodClientSecretPost + default: + return "" + } +} + // oauthCallbackHandler is responsible for handling the response from oauth service func (r *oauthProxy) oauthCallbackHandler(w http.ResponseWriter, req *http.Request) { if r.config.SkipTokenVerification { @@ -116,7 +128,7 @@ func (r *oauthProxy) oauthCallbackHandler(w http.ResponseWriter, req *http.Reque return } - client, err := r.getOAuthClient(r.getRedirectionURL(w, req)) + client, err := r.getOAuthClient(r.getRedirectionURL(w, req), getClientAuthMethod(r.config.ClientAuthMethod)) if err != nil { r.log.Error("unable to create a oauth2 client", zap.Error(err)) w.WriteHeader(http.StatusInternalServerError) diff --git a/oauth.go b/oauth.go index 4261b1b..5e1a53b 100644 --- a/oauth.go +++ b/oauth.go @@ -30,13 +30,13 @@ import ( ) // getOAuthClient returns a oauth2 client from the openid client -func (r *oauthProxy) getOAuthClient(redirectionURL string) (*oauth2.Client, error) { +func (r *oauthProxy) getOAuthClient(redirectionURL string, clientAuthMethod string) (*oauth2.Client, error) { return oauth2.NewClient(r.idpClient, oauth2.Config{ Credentials: oauth2.ClientCredentials{ ID: r.config.ClientID, Secret: r.config.ClientSecret, }, - AuthMethod: oauth2.AuthMethodClientSecretBasic, + AuthMethod: clientAuthMethod, AuthURL: r.idp.AuthEndpoint.String(), RedirectURL: redirectionURL, Scope: append(r.config.Scopes, oidc.DefaultScope...), -- GitLab