git.iko.soy/typeable/oauth2-proxy
on master help sign in
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.

commit8e2d83600cfa6682b8969fda0c5e526d8c26d6d8 authorMike Bland <michael.bland@gsa.gov> dateFri 8 May 2015, 10:00 (11 years ago) parent5cbdb74
5 files changed+41−7
cookies.go+4−4 main.go+1 oauthproxy.go+10−2 oauthproxy_test.go+24−1 options.go+2
cookies.go+4−4
@@ -15,11 +15,11 @@ import (
1515 "time"
1616)
1717
18−func validateCookie(cookie *http.Cookie, seed string) (string, bool) {
18+func validateCookie(cookie *http.Cookie, seed string) (string, time.Time, bool) {
1919 // value, timestamp, sig
2020 parts := strings.Split(cookie.Value, "|")
2121 if len(parts) != 3 {
22− return "", false
22+ return "", time.Unix(0, 0), false
2323 }
2424 sig := cookieSignature(seed, cookie.Name, parts[0], parts[1])
2525 if checkHmac(parts[2], sig) {
@@ -28,11 +28,11 @@ func validateCookie(cookie *http.Cookie, seed string) (string, bool) {
2828 // it's a valid cookie. now get the contents
2929 rawValue, err := base64.URLEncoding.DecodeString(parts[0])
3030 if err == nil {
31− return string(rawValue), true
31+ return string(rawValue), time.Unix(int64(ts), 0), true
3232 }
3333 }
3434 }
35− return "", false
35+ return "", time.Unix(0, 0), false
3636}
3737
3838func signedCookieValue(seed string, key string, value string) string {
main.go+1
@@ -45,6 +45,7 @@ func main() {
4545 flagSet.String("cookie-secret", "", "the seed string for secure cookies")
4646 flagSet.String("cookie-domain", "", "an optional cookie domain to force cookies to (ie: .yourcompany.com)*")
4747 flagSet.Duration("cookie-expire", time.Duration(168)*time.Hour, "expire timeframe for cookie")
48+ flagSet.Duration("cookie-refresh", time.Duration(24)*time.Hour, "refresh the cookie when this much time remains before expiration")
4849 flagSet.Bool("cookie-https-only", true, "set secure (HTTPS) cookies (deprecated. use --cookie-secure setting)")
4950 flagSet.Bool("cookie-secure", true, "set secure (HTTPS) cookie flag")
5051 flagSet.Bool("cookie-httponly", true, "set HttpOnly cookie flag")
oauthproxy.go+10−2
@@ -33,6 +33,7 @@ type OauthProxy struct {
3333 CookieSecure bool
3434 CookieHttpOnly bool
3535 CookieExpire time.Duration
36+ CookieRefresh time.Duration
3637 Validator func(string) bool
3738
3839 redirectUrl *url.URL // the url to receive requests at
@@ -136,6 +137,7 @@ func NewOauthProxy(opts *Options, validator func(string) bool) *OauthProxy {
136137 CookieSecure: opts.CookieSecure,
137138 CookieHttpOnly: opts.CookieHttpOnly,
138139 CookieExpire: opts.CookieExpire,
140+ CookieRefresh: opts.CookieRefresh,
139141 Validator: validator,
140142
141143 clientID: opts.ClientID,
@@ -259,10 +261,11 @@ func (p *OauthProxy) SetCookie(rw http.ResponseWriter, req *http.Request, val st
259261}
260262
261263func (p *OauthProxy) ProcessCookie(rw http.ResponseWriter, req *http.Request) (email, user, access_token string, ok bool) {
264+ var value string
265+ var timestamp time.Time
262266 cookie, err := req.Cookie(p.CookieKey)
263267 if err == nil {
264− var value string
265− value, ok = validateCookie(cookie, p.CookieSeed)
268+ value, timestamp, ok = validateCookie(cookie, p.CookieSeed)
266269 if ok {
267270 email, user, access_token, err = parseCookieValue(
268271 value, p.AesCipher)
@@ -270,6 +273,11 @@ func (p *OauthProxy) ProcessCookie(rw http.ResponseWriter, req *http.Request) (e
270273 }
271274 if err != nil {
272275 log.Printf(err.Error())
276+ } else if p.CookieRefresh != time.Duration(0) {
277+ refresh_threshold := time.Now().Add(p.CookieRefresh)
278+ if refresh_threshold.Unix() > timestamp.Unix() {
279+ p.SetCookie(rw, req, value)
280+ }
273281 }
274282 return
275283}
oauthproxy_test.go+24−1
@@ -353,8 +353,31 @@ func TestProcessCookie(t *testing.T) {
353353 assert.Equal(t, "michael.bland", user)
354354}
355355
356−func TestProcessCookieError(t *testing.T) {
356+func TestProcessCookieNoCookieError(t *testing.T) {
357357 pc_test := NewProcessCookieTest()
358358 _, _, _, ok := pc_test.ProcessCookie()
359359 assert.Equal(t, false, ok)
360360}
361+
362+func TestProcessCookieRefreshNotSet(t *testing.T) {
363+ pc_test := NewProcessCookieTest()
364+ cookie := pc_test.MakeCookie("michael.bland@gsa.gov")
365+ cookie.Expires = time.Now().Add(time.Duration(23) * time.Hour)
366+ pc_test.req.AddCookie(cookie)
367+
368+ _, _, _, ok := pc_test.ProcessCookie()
369+ assert.Equal(t, true, ok)
370+ assert.Equal(t, []string(nil), pc_test.rw.HeaderMap["Set-Cookie"])
371+}
372+
373+func TestProcessCookieRefresh(t *testing.T) {
374+ pc_test := NewProcessCookieTest()
375+ cookie := pc_test.MakeCookie("michael.bland@gsa.gov")
376+ cookie.Expires = time.Now().Add(time.Duration(23) * time.Hour)
377+ pc_test.req.AddCookie(cookie)
378+
379+ pc_test.proxy.CookieRefresh = time.Duration(24) * time.Hour
380+ _, _, _, ok := pc_test.ProcessCookie()
381+ assert.Equal(t, true, ok)
382+ assert.NotEqual(t, []string(nil), pc_test.rw.HeaderMap["Set-Cookie"])
383+}
options.go+2
@@ -26,6 +26,7 @@ type Options struct {
2626 CookieSecret string `flag:"cookie-secret" cfg:"cookie_secret" env:"GOOGLE_AUTH_PROXY_COOKIE_SECRET"`
2727 CookieDomain string `flag:"cookie-domain" cfg:"cookie_domain" env:"GOOGLE_AUTH_PROXY_COOKIE_DOMAIN"`
2828 CookieExpire time.Duration `flag:"cookie-expire" cfg:"cookie_expire" env:"GOOGLE_AUTH_PROXY_COOKIE_EXPIRE"`
29+ CookieRefresh time.Duration `flag:"cookie-refresh" cfg:"cookie_refresh" env:"GOOGLE_AUTH_PROXY_COOKIE_REFRESH"`
2930 CookieHttpsOnly bool `flag:"cookie-https-only" cfg:"cookie_https_only"` // deprecated use cookie-secure
3031 CookieSecure bool `flag:"cookie-secure" cfg:"cookie_secure"`
3132 CookieHttpOnly bool `flag:"cookie-httponly" cfg:"cookie_httponly"`
@@ -61,6 +62,7 @@ func NewOptions() *Options {
6162 CookieSecure: true,
6263 CookieHttpOnly: true,
6364 CookieExpire: time.Duration(168) * time.Hour,
65+ CookieRefresh: time.Duration(0),
6466 PassBasicAuth: true,
6567 PassAccessToken: false,
6668 PassHostHeader: true,