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

Commit 81c7892

Browse files
authored
Upstream Authorization Cookie (#287)
- adding an option to stop the proxy from including the authorization cookies in the upstream request
1 parent 7179eac commit 81c7892

File tree

7 files changed

+89
-27
lines changed

7 files changed

+89
-27
lines changed

config.go

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ func newDefaultConfig() *Config {
3131
CookieAccessName: "kc-access",
3232
CookieRefreshName: "kc-state",
3333
EnableAuthorizationHeader: true,
34+
EnableAuthorizationCookies: true,
3435
EnableTokenHeader: true,
3536
Headers: make(map[string]string),
3637
LetsEncryptCacheDir: "./cache/",

doc.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,6 @@ type Config struct {
134134
// Headers permits adding customs headers across the board
135135
Headers map[string]string `json:"headers" yaml:"headers" usage:"custom headers to the upstream request, key=value"`
136136

137-
// EnableTokenHeader adds the JWT token to the upstream authentication headers
138-
EnableTokenHeader bool `json:"enable-token-header" yaml:"enable-token-header" usage:"enables the token authentication header X-Auth-Token to upstream"`
139137
// EnableEncryptedToken indicates the access token should be encoded
140138
EnableEncryptedToken bool `json:"enable-encrypted-token" yaml:"enable-encrypted-token" usage:"enable encryption for the access tokens"`
141139
// EnableLogging indicates if we should log all the requests
@@ -150,8 +148,12 @@ type Config struct {
150148
EnableRefreshTokens bool `json:"enable-refresh-tokens" yaml:"enable-refresh-tokens" usage:"enables the handling of the refresh tokens" env:"ENABLE_REFRESH_TOKEN"`
151149
// EnableLoginHandler indicates we want the login handler enabled
152150
EnableLoginHandler bool `json:"enable-login-handler" yaml:"enable-login-handler" usage:"enables the handling of the refresh tokens" env:"ENABLE_LOGIN_HANDLER"`
151+
// EnableTokenHeader adds the JWT token to the upstream authentication headers
152+
EnableTokenHeader bool `json:"enable-token-header" yaml:"enable-token-header" usage:"enables the token authentication header X-Auth-Token to upstream"`
153153
// EnableAuthorizationHeader indicates we should pass the authorization header
154-
EnableAuthorizationHeader bool `json:"enable-authorization-header" yaml:"enable-authorization-header" usage:"adds the authorization header to the proxy request"`
154+
EnableAuthorizationHeader bool `json:"enable-authorization-header" yaml:"enable-authorization-header" usage:"adds the authorization header to the proxy request" env:"ENABLE_AUTHORIZATION_HEADER"`
155+
// EnableAuthorizationCookies indicates we should pass the authorization cookies to the upstream endpoint
156+
EnableAuthorizationCookies bool `json:"enable-authorization-cookies" yaml:"enable-authorization-cookies" usage:"adds the authorization cookies to the uptream proxy request" env:"ENABLE_AUTHORIZATION_COOKIES"`
155157
// EnableHTTPSRedirect indicate we should redirection http -> https
156158
EnableHTTPSRedirect bool `json:"enable-https-redirection" yaml:"enable-https-redirection" usage:"enable the http to https redirection on the http service"`
157159
// EnableProfiling indicates if profiles is switched on

handlers_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -288,19 +288,19 @@ func TestCallbackURL(t *testing.T) {
288288
},
289289
{
290290
URI: oauthURL + callbackURL + "?code=fake",
291-
ExpectedCookies: []string{cfg.CookieAccessName},
291+
ExpectedCookies: map[string]string{cfg.CookieAccessName: ""},
292292
ExpectedLocation: "/",
293293
ExpectedCode: http.StatusTemporaryRedirect,
294294
},
295295
{
296296
URI: oauthURL + callbackURL + "?code=fake&state=/admin",
297-
ExpectedCookies: []string{cfg.CookieAccessName},
297+
ExpectedCookies: map[string]string{cfg.CookieAccessName: ""},
298298
ExpectedLocation: "/",
299299
ExpectedCode: http.StatusTemporaryRedirect,
300300
},
301301
{
302302
URI: oauthURL + callbackURL + "?code=fake&state=L2FkbWlu",
303-
ExpectedCookies: []string{cfg.CookieAccessName},
303+
ExpectedCookies: map[string]string{cfg.CookieAccessName: ""},
304304
ExpectedLocation: "/admin",
305305
ExpectedCode: http.StatusTemporaryRedirect,
306306
},

middleware.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,8 @@ func (r *oauthProxy) headersMiddleware(custom []string) func(http.Handler) http.
317317
customClaims[x] = fmt.Sprintf("X-Auth-%s", toHeader(x))
318318
}
319319

320+
cookieFilter := []string{r.config.CookieAccessName, r.config.CookieRefreshName}
321+
320322
return func(next http.Handler) http.Handler {
321323
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
322324
scope := req.Context().Value(contextScopeName).(*RequestScope)
@@ -328,6 +330,7 @@ func (r *oauthProxy) headersMiddleware(custom []string) func(http.Handler) http.
328330
req.Header.Set("X-Auth-Subject", user.id)
329331
req.Header.Set("X-Auth-Userid", user.name)
330332
req.Header.Set("X-Auth-Username", user.name)
333+
331334
// should we add the token header?
332335
if r.config.EnableTokenHeader {
333336
req.Header.Set("X-Auth-Token", user.token.Encode())
@@ -336,7 +339,10 @@ func (r *oauthProxy) headersMiddleware(custom []string) func(http.Handler) http.
336339
if r.config.EnableAuthorizationHeader {
337340
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", user.token.Encode()))
338341
}
339-
342+
// are we filtering out the cookies
343+
if !r.config.EnableAuthorizationCookies {
344+
filterCookies(req, cookieFilter)
345+
}
340346
// inject any custom claims
341347
for claim, header := range customClaims {
342348
if claim, found := user.claims[claim]; found {

middleware_test.go

+10-7
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ type fakeRequest struct {
5757
ExpectedCode int
5858
ExpectedContent string
5959
ExpectedContentContains string
60-
ExpectedCookies []string
60+
ExpectedCookies map[string]string
6161
ExpectedHeaders map[string]string
6262
ExpectedProxyHeaders map[string]string
6363
ExpectedLocation string
@@ -238,11 +238,14 @@ func (f *fakeProxy) RunTests(t *testing.T, requests []fakeRequest) {
238238
assert.Contains(t, e, c.ExpectedContentContains, "case %d, expected content: %s, got: %s", i, c.ExpectedContentContains, e)
239239
}
240240
if len(c.ExpectedCookies) > 0 {
241-
l := len(c.ExpectedCookies)
242-
g := len(resp.Cookies())
243-
assert.Equal(t, l, g, "case %d, expected %d cookies, got: %d", i, l, g)
244-
for _, x := range c.ExpectedCookies {
245-
assert.NotNil(t, findCookie(x, resp.Cookies()), "case %d, expected cookie %s not found", i, x)
241+
for k, v := range c.ExpectedCookies {
242+
cookie := findCookie(k, resp.Cookies())
243+
if !assert.NotNil(t, cookie, "case %d, expected cookie %s not found", i, k) {
244+
continue
245+
}
246+
if v != "" {
247+
assert.Equal(t, cookie.Value, v, "case %d, expected cookie value: %s, got: %s", i, v, cookie.Value)
248+
}
246249
}
247250
}
248251
if c.OnResponse != nil {
@@ -883,7 +886,7 @@ func TestCheckRefreshTokens(t *testing.T) {
883886
Redirects: false,
884887
ExpectedProxy: true,
885888
ExpectedCode: http.StatusOK,
886-
ExpectedCookies: []string{cfg.CookieAccessName},
889+
ExpectedCookies: map[string]string{cfg.CookieAccessName: ""},
887890
},
888891
}
889892
p.RunTests(t, requests)

misc.go

+26
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,32 @@ import (
2727
"go.uber.org/zap"
2828
)
2929

30+
// filterCookies is responsible for censoring any cookies we don't want sent
31+
func filterCookies(req *http.Request, filter []string) error {
32+
// @NOTE: there doesn't appear to be a way of removing a cookie from the http.Request as
33+
// AddCookie() just append
34+
cookies := req.Cookies()
35+
// @step: empty the current cookies
36+
req.Header.Set("Cookie", "")
37+
// @step: iterate the cookies and filter out anything we
38+
for _, x := range cookies {
39+
var found bool
40+
// @step: does this cookie match our filter?
41+
for _, n := range filter {
42+
if x.Name == n {
43+
req.AddCookie(&http.Cookie{Name: x.Name, Value: "censored"})
44+
found = true
45+
break
46+
}
47+
}
48+
if !found {
49+
req.AddCookie(x)
50+
}
51+
}
52+
53+
return nil
54+
}
55+
3056
// revokeProxy is responsible to stopping the middleware from proxying the request
3157
func (r *oauthProxy) revokeProxy(w http.ResponseWriter, req *http.Request) context.Context {
3258
var scope *RequestScope

server_test.go

+37-13
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,29 @@ func TestAuthTokenHeaderDisabled(t *testing.T) {
327327
p.RunTests(t, requests)
328328
}
329329

330+
func TestDisableAuthorizationCookie(t *testing.T) {
331+
c := newFakeKeycloakConfig()
332+
c.EnableAuthorizationCookies = false
333+
p := newFakeProxy(c)
334+
token := newTestToken(p.idp.getLocation())
335+
signed, _ := p.idp.signToken(token.claims)
336+
337+
requests := []fakeRequest{
338+
{
339+
URI: "/auth_all/test",
340+
Cookies: []*http.Cookie{
341+
{Name: c.CookieAccessName, Value: signed.Encode()},
342+
{Name: "mycookie", Value: "myvalue"},
343+
},
344+
HasToken: true,
345+
ExpectedContentContains: "kc-access=censored; mycookie=myvalue",
346+
ExpectedCode: http.StatusOK,
347+
ExpectedProxy: true,
348+
},
349+
}
350+
p.RunTests(t, requests)
351+
}
352+
330353
func newTestService() string {
331354
_, _, u := newTestProxyService(nil)
332355
return u
@@ -375,19 +398,20 @@ func newFakeHTTPRequest(method, path string) *http.Request {
375398

376399
func newFakeKeycloakConfig() *Config {
377400
return &Config{
378-
ClientID: fakeClientID,
379-
ClientSecret: fakeSecret,
380-
CookieAccessName: "kc-access",
381-
CookieRefreshName: "kc-state",
382-
DisableAllLogging: true,
383-
DiscoveryURL: "127.0.0.1:0",
384-
EnableAuthorizationHeader: true,
385-
EnableLogging: false,
386-
EnableLoginHandler: true,
387-
EnableTokenHeader: true,
388-
Listen: "127.0.0.1:0",
389-
Scopes: []string{},
390-
Verbose: true,
401+
ClientID: fakeClientID,
402+
ClientSecret: fakeSecret,
403+
CookieAccessName: "kc-access",
404+
CookieRefreshName: "kc-state",
405+
DisableAllLogging: true,
406+
DiscoveryURL: "127.0.0.1:0",
407+
EnableAuthorizationHeader: true,
408+
EnableAuthorizationCookies: true,
409+
EnableLogging: false,
410+
EnableLoginHandler: true,
411+
EnableTokenHeader: true,
412+
Listen: "127.0.0.1:0",
413+
Scopes: []string{},
414+
Verbose: true,
391415
Resources: []*Resource{
392416
{
393417
URL: fakeAdminRoleURL,

0 commit comments

Comments
 (0)