Implement cookie auto-refresh
The intention is to refresh the cookie whenever the user accesses an authenticated service with less than `cookie-refresh` time to go before the cookie expires.
This commit is contained in:
parent
5cbdb74518
commit
8e2d83600c
@ -15,11 +15,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func validateCookie(cookie *http.Cookie, seed string) (string, bool) {
|
func validateCookie(cookie *http.Cookie, seed string) (string, time.Time, bool) {
|
||||||
// value, timestamp, sig
|
// value, timestamp, sig
|
||||||
parts := strings.Split(cookie.Value, "|")
|
parts := strings.Split(cookie.Value, "|")
|
||||||
if len(parts) != 3 {
|
if len(parts) != 3 {
|
||||||
return "", false
|
return "", time.Unix(0, 0), false
|
||||||
}
|
}
|
||||||
sig := cookieSignature(seed, cookie.Name, parts[0], parts[1])
|
sig := cookieSignature(seed, cookie.Name, parts[0], parts[1])
|
||||||
if checkHmac(parts[2], sig) {
|
if checkHmac(parts[2], sig) {
|
||||||
@ -28,11 +28,11 @@ func validateCookie(cookie *http.Cookie, seed string) (string, bool) {
|
|||||||
// it's a valid cookie. now get the contents
|
// it's a valid cookie. now get the contents
|
||||||
rawValue, err := base64.URLEncoding.DecodeString(parts[0])
|
rawValue, err := base64.URLEncoding.DecodeString(parts[0])
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return string(rawValue), true
|
return string(rawValue), time.Unix(int64(ts), 0), true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "", false
|
return "", time.Unix(0, 0), false
|
||||||
}
|
}
|
||||||
|
|
||||||
func signedCookieValue(seed string, key string, value string) string {
|
func signedCookieValue(seed string, key string, value string) string {
|
||||||
|
1
main.go
1
main.go
@ -45,6 +45,7 @@ func main() {
|
|||||||
flagSet.String("cookie-secret", "", "the seed string for secure cookies")
|
flagSet.String("cookie-secret", "", "the seed string for secure cookies")
|
||||||
flagSet.String("cookie-domain", "", "an optional cookie domain to force cookies to (ie: .yourcompany.com)*")
|
flagSet.String("cookie-domain", "", "an optional cookie domain to force cookies to (ie: .yourcompany.com)*")
|
||||||
flagSet.Duration("cookie-expire", time.Duration(168)*time.Hour, "expire timeframe for cookie")
|
flagSet.Duration("cookie-expire", time.Duration(168)*time.Hour, "expire timeframe for cookie")
|
||||||
|
flagSet.Duration("cookie-refresh", time.Duration(24)*time.Hour, "refresh the cookie when this much time remains before expiration")
|
||||||
flagSet.Bool("cookie-https-only", true, "set secure (HTTPS) cookies (deprecated. use --cookie-secure setting)")
|
flagSet.Bool("cookie-https-only", true, "set secure (HTTPS) cookies (deprecated. use --cookie-secure setting)")
|
||||||
flagSet.Bool("cookie-secure", true, "set secure (HTTPS) cookie flag")
|
flagSet.Bool("cookie-secure", true, "set secure (HTTPS) cookie flag")
|
||||||
flagSet.Bool("cookie-httponly", true, "set HttpOnly cookie flag")
|
flagSet.Bool("cookie-httponly", true, "set HttpOnly cookie flag")
|
||||||
|
@ -33,6 +33,7 @@ type OauthProxy struct {
|
|||||||
CookieSecure bool
|
CookieSecure bool
|
||||||
CookieHttpOnly bool
|
CookieHttpOnly bool
|
||||||
CookieExpire time.Duration
|
CookieExpire time.Duration
|
||||||
|
CookieRefresh time.Duration
|
||||||
Validator func(string) bool
|
Validator func(string) bool
|
||||||
|
|
||||||
redirectUrl *url.URL // the url to receive requests at
|
redirectUrl *url.URL // the url to receive requests at
|
||||||
@ -136,6 +137,7 @@ func NewOauthProxy(opts *Options, validator func(string) bool) *OauthProxy {
|
|||||||
CookieSecure: opts.CookieSecure,
|
CookieSecure: opts.CookieSecure,
|
||||||
CookieHttpOnly: opts.CookieHttpOnly,
|
CookieHttpOnly: opts.CookieHttpOnly,
|
||||||
CookieExpire: opts.CookieExpire,
|
CookieExpire: opts.CookieExpire,
|
||||||
|
CookieRefresh: opts.CookieRefresh,
|
||||||
Validator: validator,
|
Validator: validator,
|
||||||
|
|
||||||
clientID: opts.ClientID,
|
clientID: opts.ClientID,
|
||||||
@ -259,10 +261,11 @@ func (p *OauthProxy) SetCookie(rw http.ResponseWriter, req *http.Request, val st
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *OauthProxy) ProcessCookie(rw http.ResponseWriter, req *http.Request) (email, user, access_token string, ok bool) {
|
func (p *OauthProxy) ProcessCookie(rw http.ResponseWriter, req *http.Request) (email, user, access_token string, ok bool) {
|
||||||
|
var value string
|
||||||
|
var timestamp time.Time
|
||||||
cookie, err := req.Cookie(p.CookieKey)
|
cookie, err := req.Cookie(p.CookieKey)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
var value string
|
value, timestamp, ok = validateCookie(cookie, p.CookieSeed)
|
||||||
value, ok = validateCookie(cookie, p.CookieSeed)
|
|
||||||
if ok {
|
if ok {
|
||||||
email, user, access_token, err = parseCookieValue(
|
email, user, access_token, err = parseCookieValue(
|
||||||
value, p.AesCipher)
|
value, p.AesCipher)
|
||||||
@ -270,6 +273,11 @@ func (p *OauthProxy) ProcessCookie(rw http.ResponseWriter, req *http.Request) (e
|
|||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf(err.Error())
|
log.Printf(err.Error())
|
||||||
|
} else if p.CookieRefresh != time.Duration(0) {
|
||||||
|
refresh_threshold := time.Now().Add(p.CookieRefresh)
|
||||||
|
if refresh_threshold.Unix() > timestamp.Unix() {
|
||||||
|
p.SetCookie(rw, req, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -353,8 +353,31 @@ func TestProcessCookie(t *testing.T) {
|
|||||||
assert.Equal(t, "michael.bland", user)
|
assert.Equal(t, "michael.bland", user)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProcessCookieError(t *testing.T) {
|
func TestProcessCookieNoCookieError(t *testing.T) {
|
||||||
pc_test := NewProcessCookieTest()
|
pc_test := NewProcessCookieTest()
|
||||||
_, _, _, ok := pc_test.ProcessCookie()
|
_, _, _, ok := pc_test.ProcessCookie()
|
||||||
assert.Equal(t, false, ok)
|
assert.Equal(t, false, ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProcessCookieRefreshNotSet(t *testing.T) {
|
||||||
|
pc_test := NewProcessCookieTest()
|
||||||
|
cookie := pc_test.MakeCookie("michael.bland@gsa.gov")
|
||||||
|
cookie.Expires = time.Now().Add(time.Duration(23) * time.Hour)
|
||||||
|
pc_test.req.AddCookie(cookie)
|
||||||
|
|
||||||
|
_, _, _, ok := pc_test.ProcessCookie()
|
||||||
|
assert.Equal(t, true, ok)
|
||||||
|
assert.Equal(t, []string(nil), pc_test.rw.HeaderMap["Set-Cookie"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessCookieRefresh(t *testing.T) {
|
||||||
|
pc_test := NewProcessCookieTest()
|
||||||
|
cookie := pc_test.MakeCookie("michael.bland@gsa.gov")
|
||||||
|
cookie.Expires = time.Now().Add(time.Duration(23) * time.Hour)
|
||||||
|
pc_test.req.AddCookie(cookie)
|
||||||
|
|
||||||
|
pc_test.proxy.CookieRefresh = time.Duration(24) * time.Hour
|
||||||
|
_, _, _, ok := pc_test.ProcessCookie()
|
||||||
|
assert.Equal(t, true, ok)
|
||||||
|
assert.NotEqual(t, []string(nil), pc_test.rw.HeaderMap["Set-Cookie"])
|
||||||
|
}
|
||||||
|
@ -26,6 +26,7 @@ type Options struct {
|
|||||||
CookieSecret string `flag:"cookie-secret" cfg:"cookie_secret" env:"GOOGLE_AUTH_PROXY_COOKIE_SECRET"`
|
CookieSecret string `flag:"cookie-secret" cfg:"cookie_secret" env:"GOOGLE_AUTH_PROXY_COOKIE_SECRET"`
|
||||||
CookieDomain string `flag:"cookie-domain" cfg:"cookie_domain" env:"GOOGLE_AUTH_PROXY_COOKIE_DOMAIN"`
|
CookieDomain string `flag:"cookie-domain" cfg:"cookie_domain" env:"GOOGLE_AUTH_PROXY_COOKIE_DOMAIN"`
|
||||||
CookieExpire time.Duration `flag:"cookie-expire" cfg:"cookie_expire" env:"GOOGLE_AUTH_PROXY_COOKIE_EXPIRE"`
|
CookieExpire time.Duration `flag:"cookie-expire" cfg:"cookie_expire" env:"GOOGLE_AUTH_PROXY_COOKIE_EXPIRE"`
|
||||||
|
CookieRefresh time.Duration `flag:"cookie-refresh" cfg:"cookie_refresh" env:"GOOGLE_AUTH_PROXY_COOKIE_REFRESH"`
|
||||||
CookieHttpsOnly bool `flag:"cookie-https-only" cfg:"cookie_https_only"` // deprecated use cookie-secure
|
CookieHttpsOnly bool `flag:"cookie-https-only" cfg:"cookie_https_only"` // deprecated use cookie-secure
|
||||||
CookieSecure bool `flag:"cookie-secure" cfg:"cookie_secure"`
|
CookieSecure bool `flag:"cookie-secure" cfg:"cookie_secure"`
|
||||||
CookieHttpOnly bool `flag:"cookie-httponly" cfg:"cookie_httponly"`
|
CookieHttpOnly bool `flag:"cookie-httponly" cfg:"cookie_httponly"`
|
||||||
@ -61,6 +62,7 @@ func NewOptions() *Options {
|
|||||||
CookieSecure: true,
|
CookieSecure: true,
|
||||||
CookieHttpOnly: true,
|
CookieHttpOnly: true,
|
||||||
CookieExpire: time.Duration(168) * time.Hour,
|
CookieExpire: time.Duration(168) * time.Hour,
|
||||||
|
CookieRefresh: time.Duration(0),
|
||||||
PassBasicAuth: true,
|
PassBasicAuth: true,
|
||||||
PassAccessToken: false,
|
PassAccessToken: false,
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
|
Loading…
Reference in New Issue
Block a user