feat(oauth): add possibility to specify OAuthLogoutEndpoint for logout from OAuth Identity provider (#6073)

pull/6077/head
Jakub Bednář 2024-02-02 10:12:44 +01:00 committed by GitHub
parent fe31114e13
commit 08649d4677
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 70 additions and 13 deletions

View File

@ -1,5 +1,9 @@
## unreleased
### Features
1. [#6073](https://github.com/influxdata/chronograf/pull/6073): Possibility to specify OAuth logout endpoint to logout from OAuth Identity provider.
### Other
1. [#6074](https://github.com/influxdata/chronograf/pull/6074): Upgrade golang to 1.20.13.

View File

@ -32,7 +32,7 @@ func Test_CodeExchangeCSRF_AuthCodeURL(t *testing.T) {
ProviderURL: "http://localhost:1234",
Orgs: "",
}
authMux := NewAuthMux(mp, auth, mt, "", clog.New(clog.ParseLevel("debug")), useidtoken, "hello", nil, nil)
authMux := NewAuthMux(mp, auth, mt, "", clog.New(clog.ParseLevel("debug")), useidtoken, "hello", nil, nil, "")
// create AuthCodeURL with code exchange without PKCE
codeExchange := NewCodeExchange(false, "")
@ -95,7 +95,7 @@ func Test_CodeExchangeCSRF_ExchangeCodeForToken(t *testing.T) {
ProviderURL: authServer.URL,
Orgs: "",
}
authMux := NewAuthMux(mp, auth, auth.Tokens, "", clog.New(clog.ParseLevel("debug")), useidtoken, "hi", nil, nil)
authMux := NewAuthMux(mp, auth, auth.Tokens, "", clog.New(clog.ParseLevel("debug")), useidtoken, "hi", nil, nil, "")
// create AuthCodeURL using CodeExchange with PKCE
codeExchange := simpleTokenExchange
@ -136,7 +136,7 @@ func Test_CodeExchangePKCE_AuthCodeURL(t *testing.T) {
ProviderURL: "http://localhost:1234",
Orgs: "",
}
authMux := NewAuthMux(mp, auth, mt, "", clog.New(clog.ParseLevel("debug")), useidtoken, "hi", nil, nil)
authMux := NewAuthMux(mp, auth, mt, "", clog.New(clog.ParseLevel("debug")), useidtoken, "hi", nil, nil, "")
// create AuthCodeURL using CodeExchange with PKCE
codeExchange := NewCodeExchange(true, "secret")
@ -213,7 +213,7 @@ func Test_CodeExchangePKCE_ExchangeCodeForToken(t *testing.T) {
ProviderURL: authServer.URL,
Orgs: "",
}
authMux := NewAuthMux(mp, auth, jwt, "", clog.New(clog.ParseLevel("debug")), useidtoken, "hi", nil, nil)
authMux := NewAuthMux(mp, auth, jwt, "", clog.New(clog.ParseLevel("debug")), useidtoken, "hi", nil, nil, "")
// create AuthCodeURL using CodeExchange with PKCE
codeExchange := CodeExchangePKCE{Secret: secret}

View File

@ -19,16 +19,24 @@ func NewAuthMux(p Provider, a Authenticator, t Tokenizer,
basepath string, l chronograf.Logger,
UseIDToken bool, LoginHint string,
client *http.Client, codeExchange CodeExchange,
) *AuthMux {
logoutCallback string) *AuthMux {
if codeExchange == nil {
codeExchange = simpleTokenExchange
}
var afterLogoutURL string
if logoutCallback != "" {
afterLogoutURL = logoutCallback
} else {
afterLogoutURL = path.Join(basepath, "/")
}
mux := &AuthMux{
Provider: p,
Auth: a,
Tokens: t,
SuccessURL: path.Join(basepath, "/landing"),
AfterLogoutURL: path.Join(basepath, "/"),
AfterLogoutURL: afterLogoutURL,
FailureURL: path.Join(basepath, "/login"),
Now: DefaultNowTime,
Logger: l,

View File

@ -24,7 +24,7 @@ type mockCallbackResponse struct {
// a function, and returning the desired handler. Cleanup is still the
// responsibility of the test writer, so the httptest.Server's Close() method
// should be deferred.
func setupMuxTest(response interface{}, selector func(*AuthMux) http.Handler) (*http.Client, *httptest.Server, *httptest.Server) {
func setupMuxTest(response interface{}, selector func(*AuthMux) http.Handler, config ...map[string]string) (*http.Client, *httptest.Server, *httptest.Server) {
provider := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.Header().Set("content-type", "application/json")
rw.WriteHeader(http.StatusOK)
@ -53,7 +53,13 @@ func setupMuxTest(response interface{}, selector func(*AuthMux) http.Handler) (*
useidtoken := false
jm := NewAuthMux(mp, auth, mt, "", clog.New(clog.ParseLevel("debug")), useidtoken, "", nil, nil)
logoutCallback := ""
if len(config) > 0 {
if v, ok := config[0]["logoutCallback"]; ok {
logoutCallback = v
}
}
jm := NewAuthMux(mp, auth, mt, "", clog.New(clog.ParseLevel("debug")), useidtoken, "", nil, nil, logoutCallback)
ts := httptest.NewServer(selector(jm))
jar, _ := cookiejar.New(nil)
hc := http.Client{
@ -111,6 +117,44 @@ func Test_AuthMux_Logout_DeletesSessionCookie(t *testing.T) {
}
}
func Test_AuthMux_Logout_RedirectToLogoutCallback(t *testing.T) {
t.Parallel()
var response interface{}
hc, ts, prov := setupMuxTest(response, func(j *AuthMux) http.Handler {
return j.Logout()
}, map[string]string{"logoutCallback": "http://custom-url:8123?redirect=http://localhost:8888"})
defer teardownMuxTest(hc, ts, prov)
tsURL, _ := url.Parse(ts.URL)
hc.Jar.SetCookies(tsURL, []*http.Cookie{
{
Name: DefaultCookieName,
Value: "",
},
})
resp, err := hc.Get(ts.URL)
if err != nil {
t.Fatal("Error communicating with Logout() handler: err:", err)
}
if resp.StatusCode < 300 || resp.StatusCode >= 400 {
t.Fatal("Expected to be redirected, but received status code", resp.StatusCode)
}
loc, err := resp.Location()
if err != nil {
t.Fatal("Expected a location to be redirected to, but wasn't present")
}
if loc.String() != "http://custom-url:8123?redirect=http://localhost:8888" {
t.Fatal("Expected to be redirected to http://custom-url:8123?redirect=http://localhost:8888, but was", loc.String())
}
}
func Test_AuthMux_Login_RedirectsToCorrectURL(t *testing.T) {
t.Parallel()

View File

@ -115,6 +115,7 @@ type Server struct {
GenericInsecure bool `long:"generic-insecure" description:"Whether or not to verify auth-url's tls certificates." env:"GENERIC_INSECURE"`
GenericRootCA flags.Filename `long:"generic-root-ca" description:"File location of root ca cert for generic oauth tls verification." env:"GENERIC_ROOT_CA"`
OAuthNoPKCE bool `long:"oauth-no-pkce" description:"Disables OAuth PKCE." env:"OAUTH_NO_PKCE"`
OAuthLogoutEndpoint string `long:"oauth-logout-endpoint" description:"OAuth endpoint to call for logout from OAuth Identity provider." env:"OAUTH_LOGOUT_ENDPOINT"`
Auth0Domain string `long:"auth0-domain" description:"Subdomain of auth0.com used for Auth0 OAuth2 authentication" env:"AUTH0_DOMAIN"`
Auth0ClientID string `long:"auth0-client-id" description:"Auth0 Client ID for OAuth2 support" env:"AUTH0_CLIENT_ID"`
@ -343,7 +344,7 @@ func (s *Server) githubOAuth(logger chronograf.Logger, auth oauth2.Authenticator
Logger: logger,
}
jwt := oauth2.NewJWT(s.TokenSecret, s.JwksURL)
ghMux := oauth2.NewAuthMux(&gh, auth, jwt, s.Basepath, logger, s.UseIDToken, s.LoginHint, &s.oauthClient, s.createCodeExchange())
ghMux := oauth2.NewAuthMux(&gh, auth, jwt, s.Basepath, logger, s.UseIDToken, s.LoginHint, &s.oauthClient, s.createCodeExchange(), s.OAuthLogoutEndpoint)
return &gh, ghMux, s.UseGithub
}
@ -357,7 +358,7 @@ func (s *Server) googleOAuth(logger chronograf.Logger, auth oauth2.Authenticator
Logger: logger,
}
jwt := oauth2.NewJWT(s.TokenSecret, s.JwksURL)
goMux := oauth2.NewAuthMux(&google, auth, jwt, s.Basepath, logger, s.UseIDToken, s.LoginHint, &s.oauthClient, s.createCodeExchange())
goMux := oauth2.NewAuthMux(&google, auth, jwt, s.Basepath, logger, s.UseIDToken, s.LoginHint, &s.oauthClient, s.createCodeExchange(), s.OAuthLogoutEndpoint)
return &google, goMux, s.UseGoogle
}
@ -369,7 +370,7 @@ func (s *Server) herokuOAuth(logger chronograf.Logger, auth oauth2.Authenticator
Logger: logger,
}
jwt := oauth2.NewJWT(s.TokenSecret, s.JwksURL)
hMux := oauth2.NewAuthMux(&heroku, auth, jwt, s.Basepath, logger, s.UseIDToken, s.LoginHint, &s.oauthClient, s.createCodeExchange())
hMux := oauth2.NewAuthMux(&heroku, auth, jwt, s.Basepath, logger, s.UseIDToken, s.LoginHint, &s.oauthClient, s.createCodeExchange(), s.OAuthLogoutEndpoint)
return &heroku, hMux, s.UseHeroku
}
@ -388,7 +389,7 @@ func (s *Server) genericOAuth(logger chronograf.Logger, auth oauth2.Authenticato
Logger: logger,
}
jwt := oauth2.NewJWT(s.TokenSecret, s.JwksURL)
genMux := oauth2.NewAuthMux(&gen, auth, jwt, s.Basepath, logger, s.UseIDToken, s.LoginHint, &s.oauthClient, s.createCodeExchange())
genMux := oauth2.NewAuthMux(&gen, auth, jwt, s.Basepath, logger, s.UseIDToken, s.LoginHint, &s.oauthClient, s.createCodeExchange(), s.OAuthLogoutEndpoint)
return &gen, genMux, s.UseGenericOAuth2
}
@ -404,7 +405,7 @@ func (s *Server) auth0OAuth(logger chronograf.Logger, auth oauth2.Authenticator)
auth0, err := oauth2.NewAuth0(s.Auth0Domain, s.Auth0ClientID, s.Auth0ClientSecret, redirectURL.String(), s.Auth0Organizations, logger)
jwt := oauth2.NewJWT(s.TokenSecret, s.JwksURL)
genMux := oauth2.NewAuthMux(&auth0, auth, jwt, s.Basepath, logger, s.UseIDToken, s.LoginHint, &s.oauthClient, s.createCodeExchange())
genMux := oauth2.NewAuthMux(&auth0, auth, jwt, s.Basepath, logger, s.UseIDToken, s.LoginHint, &s.oauthClient, s.createCodeExchange(), s.OAuthLogoutEndpoint)
if err != nil {
logger.Error("Error parsing Auth0 domain: err:", err)