Add logout endpoint (#107)
Add logout endpoint that clears the auth cookie + optional "logout-redirect" config option, to which, when set, the user will be redirected.
This commit is contained in:
@ -144,6 +144,19 @@ func MakeCookie(r *http.Request, email string) *http.Cookie {
|
||||
}
|
||||
}
|
||||
|
||||
// ClearCookie clears the auth cookie
|
||||
func ClearCookie(r *http.Request) *http.Cookie {
|
||||
return &http.Cookie{
|
||||
Name: config.CookieName,
|
||||
Value: "",
|
||||
Path: "/",
|
||||
Domain: cookieDomain(r),
|
||||
HttpOnly: true,
|
||||
Secure: !config.InsecureCookie,
|
||||
Expires: time.Now().Local().Add(time.Hour * -1),
|
||||
}
|
||||
}
|
||||
|
||||
// MakeCSRFCookie makes a csrf cookie (used during login only)
|
||||
func MakeCSRFCookie(r *http.Request, nonce string) *http.Cookie {
|
||||
return &http.Cookie{
|
||||
|
@ -34,6 +34,7 @@ type Config struct {
|
||||
DefaultProvider string `long:"default-provider" env:"DEFAULT_PROVIDER" default:"google" choice:"google" choice:"oidc" description:"Default provider"`
|
||||
Domains CommaSeparatedList `long:"domain" env:"DOMAIN" env-delim:"," description:"Only allow given email domains, can be set multiple times"`
|
||||
LifetimeString int `long:"lifetime" env:"LIFETIME" default:"43200" description:"Lifetime in seconds"`
|
||||
LogoutRedirect string `long:"logout-redirect" env:"LOGOUT_REDIRECT" description:"URL to redirect to following logout"`
|
||||
Path string `long:"url-path" env:"URL_PATH" default:"/_oauth" description:"Callback URL Path"`
|
||||
SecretString string `long:"secret" env:"SECRET" description:"Secret used for signing (required)" json:"-"`
|
||||
Whitelist CommaSeparatedList `long:"whitelist" env:"WHITELIST" env-delim:"," description:"Only allow given email addresses, can be set multiple times"`
|
||||
|
@ -33,6 +33,7 @@ func TestConfigDefaults(t *testing.T) {
|
||||
assert.Equal("google", c.DefaultProvider)
|
||||
assert.Len(c.Domains, 0)
|
||||
assert.Equal(time.Second*time.Duration(43200), c.Lifetime)
|
||||
assert.Equal("", c.LogoutRedirect)
|
||||
assert.Equal("/_oauth", c.Path)
|
||||
assert.Len(c.Whitelist, 0)
|
||||
|
||||
|
@ -41,6 +41,9 @@ func (s *Server) buildRoutes() {
|
||||
// Add callback handler
|
||||
s.router.Handle(config.Path, s.AuthCallbackHandler())
|
||||
|
||||
// Add logout handler
|
||||
s.router.Handle(config.Path+"/logout", s.LogoutHandler())
|
||||
|
||||
// Add a default handler
|
||||
if config.DefaultAction == "allow" {
|
||||
s.router.NewRoute().Handler(s.AllowHandler("default"))
|
||||
@ -180,6 +183,23 @@ func (s *Server) AuthCallbackHandler() http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// LogoutHandler logs a user out
|
||||
func (s *Server) LogoutHandler() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// Clear cookie
|
||||
http.SetCookie(w, ClearCookie(r))
|
||||
|
||||
logger := s.logger(r, "Logout", "default", "Handling logout")
|
||||
logger.Info("Logged out user")
|
||||
|
||||
if config.LogoutRedirect != "" {
|
||||
http.Redirect(w, r, config.LogoutRedirect, http.StatusTemporaryRedirect)
|
||||
} else {
|
||||
http.Error(w, "You have been logged out", 401)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) authRedirect(logger *logrus.Entry, w http.ResponseWriter, r *http.Request, p provider.Provider) {
|
||||
// Error indicates no cookie, generate nonce
|
||||
err, nonce := Nonce()
|
||||
|
@ -170,6 +170,49 @@ func TestServerAuthCallback(t *testing.T) {
|
||||
assert.Equal("", fwd.Path, "valid request should be redirected to return url")
|
||||
}
|
||||
|
||||
func TestServerLogout(t *testing.T) {
|
||||
require := require.New(t)
|
||||
assert := assert.New(t)
|
||||
config = newDefaultConfig()
|
||||
|
||||
req := newDefaultHttpRequest("/_oauth/logout")
|
||||
res, _ := doHttpRequest(req, nil)
|
||||
require.Equal(401, res.StatusCode, "should return a 401")
|
||||
|
||||
// Check for cookie
|
||||
var cookie *http.Cookie
|
||||
for _, c := range res.Cookies() {
|
||||
if c.Name == config.CookieName {
|
||||
cookie = c
|
||||
}
|
||||
}
|
||||
require.NotNil(cookie)
|
||||
require.Less(cookie.Expires.Local().Unix(), time.Now().Local().Unix()-50, "cookie should have expired")
|
||||
|
||||
// Test with redirect
|
||||
config.LogoutRedirect = "http://redirect/path"
|
||||
req = newDefaultHttpRequest("/_oauth/logout")
|
||||
res, _ = doHttpRequest(req, nil)
|
||||
require.Equal(307, res.StatusCode, "should return a 307")
|
||||
|
||||
// Check for cookie
|
||||
cookie = nil
|
||||
for _, c := range res.Cookies() {
|
||||
if c.Name == config.CookieName {
|
||||
cookie = c
|
||||
}
|
||||
}
|
||||
require.NotNil(cookie)
|
||||
require.Less(cookie.Expires.Local().Unix(), time.Now().Local().Unix()-50, "cookie should have expired")
|
||||
|
||||
fwd, _ := res.Location()
|
||||
require.NotNil(fwd)
|
||||
assert.Equal("http", fwd.Scheme, "valid request should be redirected to return url")
|
||||
assert.Equal("redirect", fwd.Host, "valid request should be redirected to return url")
|
||||
assert.Equal("/path", fwd.Path, "valid request should be redirected to return url")
|
||||
|
||||
}
|
||||
|
||||
func TestServerDefaultAction(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
config = newDefaultConfig()
|
||||
|
Reference in New Issue
Block a user