Skip to content

Commit

Permalink
feat(oauth): add possibility to specify OAuthLogoutEndpoint for logou…
Browse files Browse the repository at this point in the history
…t from OAuth Identity provider (#6073)
  • Loading branch information
bednar authored Feb 2, 2024
1 parent fe31114 commit 08649d4
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 13 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
8 changes: 4 additions & 4 deletions oauth2/code_exchange_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, "")
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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}
Expand Down
12 changes: 10 additions & 2 deletions oauth2/mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
48 changes: 46 additions & 2 deletions oauth2/mux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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()

Expand Down
11 changes: 6 additions & 5 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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
}

Expand All @@ -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
}

Expand All @@ -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
}

Expand All @@ -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
}

Expand All @@ -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)
Expand Down

0 comments on commit 08649d4

Please sign in to comment.