Skip to content
This repository has been archived by the owner on Dec 7, 2020. It is now read-only.

Commit

Permalink
Use divided cookies for large access/refresh token cookie - fix #278 (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
jangaraj authored and gambol99 committed Oct 8, 2017
1 parent 8c1c2ed commit 5fc685a
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 4 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,19 @@ You can control the upstream endpoint via the --upstream-url option. Both http a
Assuming the *--enable-metrics* has been set, a Prometheus endpoint can be found on */oauth/metrics*; at present the only metric being exposed is a counter per http code.
### Limitations
Keep in mind [browser cookie limits](http://browsercookielimits.squawky.net/), if you use access or
refresh tokens in the browser cookie. Keycloak-proxy divides cookie automatically if your cookie
is longer than 4093 bytes. Real size of the cookie depends on the content of the issued access token.
Also, encryption might add additional bytes to the cookie size. If you have large cookies (>200 KB),
you might reach browser cookie limits.
All cookies are part of the header request, so you might find a problem with the max headers size
limits in your infrastructure (some load balancers have very low this value, such as 8 KB). Be
sure that all network devices have sufficient header size limits. Otherwise, your users won't be
able to obtain access token.
### **Contribution Guidelines**
----
Expand Down
55 changes: 53 additions & 2 deletions cookies.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package main

import (
"net/http"
"strconv"
"strings"
"time"
)
Expand Down Expand Up @@ -45,12 +46,42 @@ func (r *oauthProxy) dropCookie(w http.ResponseWriter, host, name, value string,

// dropAccessTokenCookie drops a access token cookie into the response
func (r *oauthProxy) dropAccessTokenCookie(req *http.Request, w http.ResponseWriter, value string, duration time.Duration) {
r.dropCookie(w, req.Host, r.config.CookieAccessName, value, duration)
// also cookie name is included in the cookie length; cookie name suffix "-xxx"
maxCookieLenght := 4089 - len(r.config.CookieAccessName)

if len(value) <= maxCookieLenght {
r.dropCookie(w, req.Host, r.config.CookieAccessName, value, duration)
} else {
// write divided cookies because payload is too long for single cookie
r.dropCookie(w, req.Host, r.config.CookieAccessName, value[0:maxCookieLenght], duration)
for i := maxCookieLenght; i < len(value); i += maxCookieLenght {
end := i + maxCookieLenght
if end > len(value) {
end = len(value)
}
r.dropCookie(w, req.Host, r.config.CookieAccessName+"-"+strconv.Itoa(i/maxCookieLenght), value[i:end], duration)
}
}
}

// dropRefreshTokenCookie drops a refresh token cookie into the response
func (r *oauthProxy) dropRefreshTokenCookie(req *http.Request, w http.ResponseWriter, value string, duration time.Duration) {
r.dropCookie(w, req.Host, r.config.CookieRefreshName, value, duration)
// also cookie name is included in the cookie length; cookie name suffix "-xxx"
maxCookieLenght := 4089 - len(r.config.CookieRefreshName)

if len(value) <= maxCookieLenght {
r.dropCookie(w, req.Host, r.config.CookieRefreshName, value, duration)
} else {
// write divided cookies because payload is too long for single cookie
r.dropCookie(w, req.Host, r.config.CookieRefreshName, value[0:maxCookieLenght], duration)
for i := maxCookieLenght; i < len(value); i += maxCookieLenght {
end := i + maxCookieLenght
if end > len(value) {
end = len(value)
}
r.dropCookie(w, req.Host, r.config.CookieRefreshName+"-"+strconv.Itoa(i/maxCookieLenght), value[i:end], duration)
}
}
}

// clearAllCookies is just a helper function for the below
Expand All @@ -62,9 +93,29 @@ func (r *oauthProxy) clearAllCookies(req *http.Request, w http.ResponseWriter) {
// clearRefreshSessionCookie clears the session cookie
func (r *oauthProxy) clearRefreshTokenCookie(req *http.Request, w http.ResponseWriter) {
r.dropCookie(w, req.Host, r.config.CookieRefreshName, "", -10*time.Hour)

// clear divided cookies
for i := 1; i < 600; i++ {
var _, err = req.Cookie(r.config.CookieRefreshName + "-" + strconv.Itoa(i))
if err == nil {
r.dropCookie(w, req.Host, r.config.CookieRefreshName+"-"+strconv.Itoa(i), "", -10*time.Hour)
} else {
break
}
}
}

// clearAccessTokenCookie clears the session cookie
func (r *oauthProxy) clearAccessTokenCookie(req *http.Request, w http.ResponseWriter) {
r.dropCookie(w, req.Host, r.config.CookieAccessName, "", -10*time.Hour)

// clear divided cookies
for i := 1; i < len(req.Cookies()); i++ {
var _, err = req.Cookie(r.config.CookieAccessName + "-" + strconv.Itoa(i))
if err == nil {
r.dropCookie(w, req.Host, r.config.CookieAccessName+"-"+strconv.Itoa(i), "", -10*time.Hour)
} else {
break
}
}
}
23 changes: 21 additions & 2 deletions session.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ limitations under the License.
package main

import (
"bytes"
"net/http"
"strconv"
"strings"

"github.com/gambol99/go-oidc/jose"
Expand Down Expand Up @@ -100,8 +102,25 @@ func getTokenInBearer(req *http.Request) (string, error) {

// getTokenInCookie retrieves the access token from the request cookies
func getTokenInCookie(req *http.Request, name string) (string, error) {
var token bytes.Buffer

if cookie := findCookie(name, req.Cookies()); cookie != nil {
return cookie.Value, nil
token.WriteString(cookie.Value)
}

// add also divided cookies
for i := 1; i < 600; i++ {
cookie := findCookie(name+"-"+strconv.Itoa(i), req.Cookies())
if cookie == nil {
break
} else {
token.WriteString(cookie.Value)
}
}
return "", ErrSessionNotFound

if token.Len() == 0 {
return "", ErrSessionNotFound
}

return token.String(), nil
}

0 comments on commit 5fc685a

Please sign in to comment.