@@ -15,6 +15,7 @@ import (
1515 "time"
1616
1717 "github.com/coreos/go-oidc/v3/oidc"
18+ middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
1819 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
1920 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
2021 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/authentication/hmacauth"
@@ -53,7 +54,7 @@ func TestRobotsTxt(t *testing.T) {
5354 t.Fatal(err)
5455 }
5556 rw := httptest.NewRecorder()
56− req, _ := http.NewRequest("GET", "/robots.txt", nil)
57+ req, _ := http.NewRequest(http.MethodGet, "/robots.txt", nil)
5758 proxy.ServeHTTP(rw, req)
5859 assert.Equal(t, 200, rw.Code)
5960 assert.Equal(t, "User-agent: *\nDisallow: /\n", rw.Body.String())
@@ -240,7 +241,7 @@ func TestBasicAuthPassword(t *testing.T) {
240241
241242 // Save the required session
242243 rw := httptest.NewRecorder()
243− req, _ := http.NewRequest("GET", "/", nil)
244+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
244245 err = proxy.sessionStore.Save(rw, req, &sessions.SessionState{
245246 Email: emailAddress,
246247 })
@@ -249,7 +250,7 @@ func TestBasicAuthPassword(t *testing.T) {
249250 // Extract the cookie value to inject into the test request
250251 cookie := rw.Header().Values("Set-Cookie")[0]
251252
252− req, _ = http.NewRequest("GET", "/", nil)
253+ req, _ = http.NewRequest(http.MethodGet, "/", nil)
253254 req.Header.Set("Cookie", cookie)
254255 rw = httptest.NewRecorder()
255256 proxy.ServeHTTP(rw, req)
@@ -299,14 +300,14 @@ func TestPassGroupsHeadersWithGroups(t *testing.T) {
299300
300301 // Save the required session
301302 rw := httptest.NewRecorder()
302− req, _ := http.NewRequest("GET", "/", nil)
303+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
303304 err = proxy.sessionStore.Save(rw, req, session)
304305 assert.NoError(t, err)
305306
306307 // Extract the cookie value to inject into the test request
307308 cookie := rw.Header().Values("Set-Cookie")[0]
308309
309− req, _ = http.NewRequest("GET", "/", nil)
310+ req, _ = http.NewRequest(http.MethodGet, "/", nil)
310311 req.Header.Set("Cookie", cookie)
311312 rw = httptest.NewRecorder()
312313 proxy.ServeHTTP(rw, req)
@@ -456,7 +457,7 @@ func (patTest *PassAccessTokenTest) getEndpointWithCookie(cookie string, endpoin
456457 return 0, ""
457458 }
458459
459− req, err := http.NewRequest("GET", endpoint, strings.NewReader(""))
460+ req, err := http.NewRequest(http.MethodGet, endpoint, strings.NewReader(""))
460461 if err != nil {
461462 return 0, ""
462463 }
@@ -607,7 +608,7 @@ func NewSignInPageTest(skipProvider bool) (*SignInPageTest, error) {
607608
608609func (sipTest *SignInPageTest) GetEndpoint(endpoint string) (int, string) {
609610 rw := httptest.NewRecorder()
610− req, _ := http.NewRequest("GET", endpoint, strings.NewReader(""))
611+ req, _ := http.NewRequest(http.MethodGet, endpoint, strings.NewReader(""))
611612 sipTest.proxy.ServeHTTP(rw, req)
612613 return rw.Code, rw.Body.String()
613614}
@@ -712,6 +713,50 @@ func TestManualSignInCorrectCredentials(t *testing.T) {
712713 assert.Equal(t, http.StatusFound, statusCode)
713714}
714715
716+func TestSignInPageClearsExistingSessionCookie(t *testing.T) {
717+ opts := baseTestOptions()
718+ err := validation.Validate(opts)
719+ require.NoError(t, err)
720+
721+ proxy, err := NewOAuthProxy(opts, func(string) bool {
722+ return true
723+ })
724+ require.NoError(t, err)
725+
726+ // Create a real session cookie using the actual session store.
727+ saveRW := httptest.NewRecorder()
728+ saveReq := httptest.NewRequest(http.MethodGet, "/", nil)
729+ err = proxy.sessionStore.Save(saveRW, saveReq, &sessions.SessionState{
730+ Email: "john.doe@example.com",
731+ })
732+ require.NoError(t, err)
733+
734+ cookies := saveRW.Result().Cookies()
735+ require.NotEmpty(t, cookies)
736+
737+ // Send that cookie to the sign-in page.
738+ req := httptest.NewRequest(http.MethodGet, "/oauth2/sign_in", nil)
739+ for _, c := range cookies {
740+ req.AddCookie(c)
741+ }
742+
743+ rw := httptest.NewRecorder()
744+ proxy.ServeHTTP(rw, req)
745+
746+ assert.Equal(t, http.StatusOK, rw.Code)
747+
748+ cleared := false
749+ for _, c := range rw.Result().Cookies() {
750+ if c.Name == proxy.CookieOptions.Name {
751+ cleared = true
752+ assert.Equal(t, "", c.Value)
753+ assert.Less(t, c.MaxAge, 0)
754+ }
755+ }
756+
757+ assert.True(t, cleared, "expected sign-in page to clear existing session cookie")
758+}
759+
715760func TestSignInPageIncludesTargetRedirect(t *testing.T) {
716761 sipTest, err := NewSignInPageTest(false)
717762 if err != nil {
@@ -849,7 +894,7 @@ func NewProcessCookieTest(opts ProcessCookieTestOpts, modifiers ...OptionsModifi
849894 // access_token validation.
850895 pcTest.proxy.CookieOptions.Refresh = time.Duration(0)
851896 pcTest.rw = httptest.NewRecorder()
852− pcTest.req, _ = http.NewRequest("GET", "/", strings.NewReader(""))
897+ pcTest.req, _ = http.NewRequest(http.MethodGet, "/", strings.NewReader(""))
853898 pcTest.validateUser = true
854899 return &pcTest, nil
855900}
@@ -982,7 +1027,7 @@ func NewUserInfoEndpointTest() (*ProcessCookieTest, error) {
9821027 if err != nil {
9831028 return nil, err
9841029 }
985− pcTest.req, _ = http.NewRequest("GET",
1030+ pcTest.req, _ = http.NewRequest(http.MethodGet,
9861031 pcTest.opts.ProxyPrefix+"/userinfo", nil)
9871032 return pcTest, nil
9881033}
@@ -1090,7 +1135,7 @@ func NewAuthOnlyEndpointTest(querystring string, modifiers ...OptionsModifier) (
10901135 return nil, err
10911136 }
10921137 pcTest.req, _ = http.NewRequest(
1093− "GET",
1138+ http.MethodGet,
10941139 fmt.Sprintf("%s/auth%s", pcTest.opts.ProxyPrefix, querystring),
10951140 nil)
10961141 return pcTest, nil
@@ -1229,7 +1274,7 @@ func TestAuthOnlyEndpointSetXAuthRequestHeaders(t *testing.T) {
12291274 pcTest.validateUser = true
12301275
12311276 pcTest.rw = httptest.NewRecorder()
1232− pcTest.req, _ = http.NewRequest("GET",
1277+ pcTest.req, _ = http.NewRequest(http.MethodGet,
12331278 pcTest.opts.ProxyPrefix+authOnlyPath, nil)
12341279
12351280 created := time.Now()
@@ -1322,7 +1367,7 @@ func TestAuthOnlyEndpointSetBasicAuthTrueRequestHeaders(t *testing.T) {
13221367 pcTest.validateUser = true
13231368
13241369 pcTest.rw = httptest.NewRecorder()
1325− pcTest.req, _ = http.NewRequest("GET",
1370+ pcTest.req, _ = http.NewRequest(http.MethodGet,
13261371 pcTest.opts.ProxyPrefix+authOnlyPath, nil)
13271372
13281373 created := time.Now()
@@ -1402,7 +1447,7 @@ func TestAuthOnlyEndpointSetBasicAuthFalseRequestHeaders(t *testing.T) {
14021447 pcTest.validateUser = true
14031448
14041449 pcTest.rw = httptest.NewRecorder()
1405− pcTest.req, _ = http.NewRequest("GET",
1450+ pcTest.req, _ = http.NewRequest(http.MethodGet,
14061451 pcTest.opts.ProxyPrefix+authOnlyPath, nil)
14071452
14081453 created := time.Now()
@@ -1450,7 +1495,7 @@ func TestAuthSkippedForPreflightRequests(t *testing.T) {
14501495 }
14511496 proxy.provider = NewTestProvider(upstreamURL, "")
14521497 rw := httptest.NewRecorder()
1453− req, _ := http.NewRequest("OPTIONS", "/preflight-request", nil)
1498+ req, _ := http.NewRequest(http.MethodOptions, "/preflight-request", nil)
14541499 proxy.ServeHTTP(rw, req)
14551500
14561501 assert.Equal(t, 200, rw.Code)
@@ -1607,19 +1652,19 @@ func TestRequestSignature(t *testing.T) {
16071652 resp string
16081653 }{
16091654 "No request signature": {
1610− method: "GET",
1655+ method: http.MethodGet,
16111656 body: "",
16121657 key: "",
16131658 resp: "no signature received",
16141659 },
16151660 "Get request": {
1616− method: "GET",
1661+ method: http.MethodGet,
16171662 body: "",
16181663 key: "7d9e1aa87a5954e6f9fc59266b3af9d7c35fda2d",
16191664 resp: "signatures match",
16201665 },
16211666 "Post request": {
1622− method: "POST",
1667+ method: http.MethodPost,
16231668 body: `{ "hello": "world!" }`,
16241669 key: "d90df39e2d19282840252612dd7c81421a372f61",
16251670 resp: "signatures match",
@@ -2144,7 +2189,33 @@ func TestTrustedIPs(t *testing.T) {
21442189 reverseProxy: false,
21452190 realClientIPHeader: "X-Real-IP", // Default value
21462191 req: func() *http.Request {
2147− req, _ := http.NewRequest("GET", "/", nil)
2192+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
2193+ return req
2194+ }(),
2195+ expectTrusted: false,
2196+ },
2197+ // Check Unix socket with no trusted IPs configured does not error.
2198+ {
2199+ name: "UnixSocketWithoutTrustedIPs",
2200+ trustedIPs: nil,
2201+ reverseProxy: false,
2202+ realClientIPHeader: "X-Real-IP",
2203+ req: func() *http.Request {
2204+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
2205+ req.RemoteAddr = "@"
2206+ return req
2207+ }(),
2208+ expectTrusted: false,
2209+ },
2210+ // Check Unix socket with trusted IPs configured returns false (no IP to match).
2211+ {
2212+ name: "UnixSocketWithTrustedIPs",
2213+ trustedIPs: []string{"127.0.0.1"},
2214+ reverseProxy: false,
2215+ realClientIPHeader: "X-Real-IP",
2216+ req: func() *http.Request {
2217+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
2218+ req.RemoteAddr = "@"
21482219 return req
21492220 }(),
21502221 expectTrusted: false,
@@ -2156,7 +2227,7 @@ func TestTrustedIPs(t *testing.T) {
21562227 reverseProxy: false,
21572228 realClientIPHeader: "X-Real-IP", // Default value
21582229 req: func() *http.Request {
2159− req, _ := http.NewRequest("GET", "/", nil)
2230+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
21602231 req.RemoteAddr = "127.0.0.1:43670"
21612232 return req
21622233 }(),
@@ -2169,7 +2240,7 @@ func TestTrustedIPs(t *testing.T) {
21692240 reverseProxy: true,
21702241 realClientIPHeader: "X-Real-IP", // Default value
21712242 req: func() *http.Request {
2172− req, _ := http.NewRequest("GET", "/", nil)
2243+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
21732244 req.RemoteAddr = "127.0.0.1:44324"
21742245 return req
21752246 }(),
@@ -2182,7 +2253,7 @@ func TestTrustedIPs(t *testing.T) {
21822253 reverseProxy: true,
21832254 realClientIPHeader: "X-Forwarded-For",
21842255 req: func() *http.Request {
2185− req, _ := http.NewRequest("GET", "/", nil)
2256+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
21862257 req.Header.Add("X-Forwarded-For", "127.0.0.1")
21872258 return req
21882259 }(),
@@ -2195,7 +2266,7 @@ func TestTrustedIPs(t *testing.T) {
21952266 reverseProxy: true,
21962267 realClientIPHeader: "X-Forwarded-For",
21972268 req: func() *http.Request {
2198− req, _ := http.NewRequest("GET", "/", nil)
2269+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
21992270 req.Header.Add("X-Forwarded-For", "::1")
22002271 return req
22012272 }(),
@@ -2208,7 +2279,7 @@ func TestTrustedIPs(t *testing.T) {
22082279 reverseProxy: true,
22092280 realClientIPHeader: "X-Forwarded-For",
22102281 req: func() *http.Request {
2211− req, _ := http.NewRequest("GET", "/", nil)
2282+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
22122283 req.Header.Add("X-Forwarded-For", "12.34.56.78")
22132284 return req
22142285 }(),
@@ -2221,7 +2292,7 @@ func TestTrustedIPs(t *testing.T) {
22212292 reverseProxy: true,
22222293 realClientIPHeader: "X-Forwarded-For",
22232294 req: func() *http.Request {
2224− req, _ := http.NewRequest("GET", "/", nil)
2295+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
22252296 req.Header.Add("X-Forwarded-For", "::2")
22262297 return req
22272298 }(),
@@ -2234,7 +2305,7 @@ func TestTrustedIPs(t *testing.T) {
22342305 reverseProxy: true,
22352306 realClientIPHeader: "X-Forwarded-For",
22362307 req: func() *http.Request {
2237− req, _ := http.NewRequest("GET", "/", nil)
2308+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
22382309 req.Header.Add("X-Real-IP", "::1")
22392310 return req
22402311 }(),
@@ -2247,7 +2318,7 @@ func TestTrustedIPs(t *testing.T) {
22472318 reverseProxy: true,
22482319 realClientIPHeader: "X-Forwarded-For",
22492320 req: func() *http.Request {
2250− req, _ := http.NewRequest("GET", "/", nil)
2321+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
22512322 req.Header.Add("X-Forwarded-For", "adsfljk29242as!!")
22522323 return req
22532324 }(),
@@ -2260,7 +2331,7 @@ func TestTrustedIPs(t *testing.T) {
22602331 reverseProxy: false,
22612332 realClientIPHeader: "X-Real-IP",
22622333 req: func() *http.Request {
2263− req, _ := http.NewRequest("GET", "/", nil)
2334+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
22642335 req.RemoteAddr = "adsfljk29242as!!"
22652336 return req
22662337 }(),
@@ -2356,12 +2427,12 @@ func Test_buildRoutesAllowlist(t *testing.T) {
23562427 },
23572428 expectedRoutes: []expectedAllowedRoute{
23582429 {
2359− method: "GET",
2430+ method: http.MethodGet,
23602431 negate: false,
23612432 regexString: "^/foo/bar",
23622433 },
23632434 {
2364− method: "POST",
2435+ method: http.MethodPost,
23652436 negate: false,
23662437 regexString: "^/baz/[0-9]+/thing",
23672438 },
@@ -2414,11 +2485,11 @@ func Test_buildRoutesAllowlist(t *testing.T) {
24142485 regexString: "^/baz/[0-9]+/thing/regex",
24152486 },
24162487 {
2417− method: "GET",
2488+ method: http.MethodGet,
24182489 regexString: "^/foo/bar",
24192490 },
24202491 {
2421− method: "POST",
2492+ method: http.MethodPost,
24222493 regexString: "^/baz/[0-9]+/thing",
24232494 },
24242495 {
@@ -2570,7 +2641,7 @@ func TestApiRoutes(t *testing.T) {
25702641
25712642 for _, tc := range testCases {
25722643 t.Run(tc.name, func(t *testing.T) {
2573− req, err := http.NewRequest("GET", tc.url, nil)
2644+ req, err := http.NewRequest(http.MethodGet, tc.url, nil)
25742645 req.Header.Set("Accept", tc.contentType)
25752646 assert.NoError(t, err)
25762647
@@ -2608,9 +2679,11 @@ func TestAllowedRequest(t *testing.T) {
26082679 }
26092680 opts.SkipAuthRegex = []string{
26102681 "^/skip/auth/regex$",
2682+ "^/public/.*/endpoint$",
26112683 }
26122684 opts.SkipAuthRoutes = []string{
26132685 "GET=^/skip/auth/routes/get",
2686+ "^/foo/.*/bar$",
26142687 }
26152688 err := validation.Validate(opts)
26162689 assert.NoError(t, err)
@@ -2627,25 +2700,37 @@ func TestAllowedRequest(t *testing.T) {
26272700 }{
26282701 {
26292702 name: "Regex GET allowed",
2630− method: "GET",
2703+ method: http.MethodGet,
26312704 url: "/skip/auth/regex",
26322705 allowed: true,
26332706 },
26342707 {
26352708 name: "Regex POST allowed ",
2636− method: "POST",
2709+ method: http.MethodPost,
26372710 url: "/skip/auth/regex",
26382711 allowed: true,
26392712 },
26402713 {
26412714 name: "Regex denied",
2642− method: "GET",
2715+ method: http.MethodGet,
26432716 url: "/wrong/denied",
26442717 allowed: false,
26452718 },
2719+ {
2720+ name: "Regex allowed with fragment-free path",
2721+ method: http.MethodGet,
2722+ url: "/public/legit/endpoint",
2723+ allowed: true,
2724+ },
2725+ {
2726+ name: "Regex denied when path contains encoded fragment suffix",
2727+ method: http.MethodGet,
2728+ url: "/public/secret%23/endpoint",
2729+ allowed: false,
2730+ },
26462731 {
26472732 name: "Route allowed",
2648− method: "GET",
2733+ method: http.MethodGet,
26492734 url: "/skip/auth/routes/get",
26502735 allowed: true,
26512736 },
@@ -2657,16 +2742,28 @@ func TestAllowedRequest(t *testing.T) {
26572742 },
26582743 {
26592744 name: "Route denied with wrong path",
2660− method: "GET",
2745+ method: http.MethodGet,
26612746 url: "/skip/auth/routes/wrong/path",
26622747 allowed: false,
26632748 },
26642749 {
26652750 name: "Route denied with wrong path and method",
2666− method: "POST",
2751+ method: http.MethodPost,
26672752 url: "/skip/auth/routes/wrong/path",
26682753 allowed: false,
26692754 },
2755+ {
2756+ name: "Route allowed with fragment-free path",
2757+ method: http.MethodGet,
2758+ url: "/foo/public/bar",
2759+ allowed: true,
2760+ },
2761+ {
2762+ name: "Route denied when path contains encoded fragment suffix",
2763+ method: http.MethodGet,
2764+ url: "/foo/secret%23/bar",
2765+ allowed: false,
2766+ },
26702767 }
26712768
26722769 for _, tc := range testCases {
@@ -2707,9 +2804,11 @@ func TestAllowedRequestWithForwardedUriHeader(t *testing.T) {
27072804 }
27082805 opts.SkipAuthRegex = []string{
27092806 "^/skip/auth/regex$",
2807+ "^/public/.*/endpoint$",
27102808 }
27112809 opts.SkipAuthRoutes = []string{
27122810 "GET=^/skip/auth/routes/get",
2811+ "^/foo/.*/bar$",
27132812 }
27142813 err := validation.Validate(opts)
27152814 assert.NoError(t, err)
@@ -2726,25 +2825,37 @@ func TestAllowedRequestWithForwardedUriHeader(t *testing.T) {
27262825 }{
27272826 {
27282827 name: "Regex GET allowed",
2729− method: "GET",
2828+ method: http.MethodGet,
27302829 url: "/skip/auth/regex",
27312830 allowed: true,
27322831 },
27332832 {
27342833 name: "Regex POST allowed ",
2735− method: "POST",
2834+ method: http.MethodPost,
27362835 url: "/skip/auth/regex",
27372836 allowed: true,
27382837 },
27392838 {
27402839 name: "Regex denied",
2741− method: "GET",
2840+ method: http.MethodGet,
27422841 url: "/wrong/denied",
27432842 allowed: false,
27442843 },
2844+ {
2845+ name: "Regex allowed with fragment-free path",
2846+ method: http.MethodGet,
2847+ url: "/public/legit/endpoint",
2848+ allowed: true,
2849+ },
2850+ {
2851+ name: "Regex denied when X-Forwarded-Uri contains an encoded fragment suffix",
2852+ method: http.MethodGet,
2853+ url: "/public/secret%23/endpoint",
2854+ allowed: false,
2855+ },
27452856 {
27462857 name: "Route allowed",
2747− method: "GET",
2858+ method: http.MethodGet,
27482859 url: "/skip/auth/routes/get",
27492860 allowed: true,
27502861 },
@@ -2756,22 +2867,35 @@ func TestAllowedRequestWithForwardedUriHeader(t *testing.T) {
27562867 },
27572868 {
27582869 name: "Route denied with wrong path",
2759− method: "GET",
2870+ method: http.MethodGet,
27602871 url: "/skip/auth/routes/wrong/path",
27612872 allowed: false,
27622873 },
27632874 {
27642875 name: "Route denied with wrong path and method",
2765− method: "POST",
2876+ method: http.MethodPost,
27662877 url: "/skip/auth/routes/wrong/path",
27672878 allowed: false,
27682879 },
2880+ {
2881+ name: "Route allowed with fragment-free path",
2882+ method: http.MethodGet,
2883+ url: "/foo/public/bar",
2884+ allowed: true,
2885+ },
2886+ {
2887+ name: "Route denied when X-Forwarded-Uri contains an encoded fragment suffix",
2888+ method: http.MethodGet,
2889+ url: "/foo/secret%23/bar",
2890+ allowed: false,
2891+ },
27692892 }
27702893
27712894 for _, tc := range testCases {
27722895 t.Run(tc.name, func(t *testing.T) {
27732896 req, err := http.NewRequest(tc.method, opts.ProxyPrefix+authOnlyPath, nil)
27742897 req.Header.Set("X-Forwarded-Uri", tc.url)
2898+ req.RemoteAddr = "127.0.0.1:4180"
27752899 assert.NoError(t, err)
27762900
27772901 rw := httptest.NewRecorder()
@@ -2786,6 +2910,43 @@ func TestAllowedRequestWithForwardedUriHeader(t *testing.T) {
27862910 }
27872911}
27882912
2913+func TestAllowedRequestWithForwardedUriHeaderRequiresTrustedProxy(t *testing.T) {
2914+ upstreamServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2915+ w.WriteHeader(200)
2916+ }))
2917+ t.Cleanup(upstreamServer.Close)
2918+
2919+ opts := baseTestOptions()
2920+ opts.ReverseProxy = true
2921+ opts.TrustedProxyIPs = []string{"127.0.0.1/32"}
2922+ opts.UpstreamServers = options.UpstreamConfig{
2923+ Upstreams: []options.Upstream{
2924+ {
2925+ ID: upstreamServer.URL,
2926+ Path: "/",
2927+ URI: upstreamServer.URL,
2928+ },
2929+ },
2930+ }
2931+ opts.SkipAuthRegex = []string{"^/skip/auth/regex$"}
2932+
2933+ err := validation.Validate(opts)
2934+ assert.NoError(t, err)
2935+
2936+ proxy, err := NewOAuthProxy(opts, func(_ string) bool { return true })
2937+ assert.NoError(t, err)
2938+
2939+ req, err := http.NewRequest(http.MethodGet, opts.ProxyPrefix+authOnlyPath, nil)
2940+ assert.NoError(t, err)
2941+ req.RemoteAddr = "192.0.2.10:4180"
2942+ req.Header.Set("X-Forwarded-Uri", "/skip/auth/regex")
2943+
2944+ rw := httptest.NewRecorder()
2945+ proxy.ServeHTTP(rw, req)
2946+
2947+ assert.Equal(t, 401, rw.Code)
2948+}
2949+
27892950func TestAllowedRequestNegateWithoutMethod(t *testing.T) {
27902951 upstreamServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
27912952 w.WriteHeader(200)
@@ -2825,37 +2986,37 @@ func TestAllowedRequestNegateWithoutMethod(t *testing.T) {
28252986 }{
28262987 {
28272988 name: "Some static file allowed",
2828− method: "GET",
2989+ method: http.MethodGet,
28292990 url: "/static/file.txt",
28302991 allowed: true,
28312992 },
28322993 {
28332994 name: "POST to contact form allowed",
2834− method: "POST",
2995+ method: http.MethodPost,
28352996 url: "/contact",
28362997 allowed: true,
28372998 },
28382999 {
28393000 name: "Regex POST allowed",
2840− method: "POST",
3001+ method: http.MethodPost,
28413002 url: "/api/public-entity",
28423003 allowed: true,
28433004 },
28443005 {
28453006 name: "Regex POST with trailing slash allowed",
2846− method: "POST",
3007+ method: http.MethodPost,
28473008 url: "/api/public-entity/",
28483009 allowed: true,
28493010 },
28503011 {
28513012 name: "Regex GET api route denied",
2852− method: "GET",
3013+ method: http.MethodGet,
28533014 url: "/api/users",
28543015 allowed: false,
28553016 },
28563017 {
28573018 name: "Regex POST api route denied",
2858− method: "POST",
3019+ method: http.MethodPost,
28593020 url: "/api/users",
28603021 allowed: false,
28613022 },
@@ -2925,37 +3086,37 @@ func TestAllowedRequestNegateWithMethod(t *testing.T) {
29253086 }{
29263087 {
29273088 name: "Some static file allowed",
2928− method: "GET",
3089+ method: http.MethodGet,
29293090 url: "/static/file.txt",
29303091 allowed: true,
29313092 },
29323093 {
29333094 name: "POST to contact form not allowed",
2934− method: "POST",
3095+ method: http.MethodPost,
29353096 url: "/contact",
29363097 allowed: false,
29373098 },
29383099 {
29393100 name: "Regex POST allowed",
2940− method: "POST",
3101+ method: http.MethodPost,
29413102 url: "/api/public-entity",
29423103 allowed: true,
29433104 },
29443105 {
29453106 name: "Regex POST with trailing slash allowed",
2946− method: "POST",
3107+ method: http.MethodPost,
29473108 url: "/api/public-entity/",
29483109 allowed: true,
29493110 },
29503111 {
29513112 name: "Regex GET api route denied",
2952− method: "GET",
3113+ method: http.MethodGet,
29533114 url: "/api/users",
29543115 allowed: false,
29553116 },
29563117 {
29573118 name: "Regex POST api route denied",
2958− method: "POST",
3119+ method: http.MethodPost,
29593120 url: "/api/users",
29603121 allowed: false,
29613122 },
@@ -3095,7 +3256,7 @@ func TestProxyAllowedGroups(t *testing.T) {
30953256 t.Fatal(err)
30963257 }
30973258
3098− test.req, _ = http.NewRequest("GET", fmt.Sprintf("/%s", tt.querystring), nil)
3259+ test.req, _ = http.NewRequest(http.MethodGet, fmt.Sprintf("/%s", tt.querystring), nil)
30993260
31003261 test.req.Header.Add("accept", applicationJSON)
31013262 err = test.SaveSession(session)
@@ -3239,7 +3400,7 @@ func TestAuthOnlyAllowedGroupsWithSkipMethods(t *testing.T) {
32393400 {
32403401 name: "UserWithGroupSkipAuthPreflight",
32413402 groups: []string{"a", "c"},
3242− method: "OPTIONS",
3403+ method: http.MethodOptions,
32433404 ip: "1.2.3.5:43670",
32443405 withSession: true,
32453406 expectedStatusCode: http.StatusAccepted,
@@ -3247,7 +3408,7 @@ func TestAuthOnlyAllowedGroupsWithSkipMethods(t *testing.T) {
32473408 {
32483409 name: "UserWithGroupTrustedIp",
32493410 groups: []string{"a", "c"},
3250− method: "GET",
3411+ method: http.MethodGet,
32513412 ip: "1.2.3.4:43670",
32523413 withSession: true,
32533414 expectedStatusCode: http.StatusAccepted,
@@ -3255,7 +3416,7 @@ func TestAuthOnlyAllowedGroupsWithSkipMethods(t *testing.T) {
32553416 {
32563417 name: "UserWithoutGroupSkipAuthPreflight",
32573418 groups: []string{"c"},
3258− method: "OPTIONS",
3419+ method: http.MethodOptions,
32593420 ip: "1.2.3.5:43670",
32603421 withSession: true,
32613422 expectedStatusCode: http.StatusForbidden,
@@ -3263,21 +3424,21 @@ func TestAuthOnlyAllowedGroupsWithSkipMethods(t *testing.T) {
32633424 {
32643425 name: "UserWithoutGroupTrustedIp",
32653426 groups: []string{"c"},
3266− method: "GET",
3427+ method: http.MethodGet,
32673428 ip: "1.2.3.4:43670",
32683429 withSession: true,
32693430 expectedStatusCode: http.StatusForbidden,
32703431 },
32713432 {
32723433 name: "UserWithoutSessionSkipAuthPreflight",
3273− method: "OPTIONS",
3434+ method: http.MethodOptions,
32743435 ip: "1.2.3.5:43670",
32753436 withSession: false,
32763437 expectedStatusCode: http.StatusAccepted,
32773438 },
32783439 {
32793440 name: "UserWithoutSessionTrustedIp",
3280− method: "GET",
3441+ method: http.MethodGet,
32813442 ip: "1.2.3.4:43670",
32823443 withSession: false,
32833444 expectedStatusCode: http.StatusAccepted,
@@ -3377,6 +3538,24 @@ func TestAuthOnlyAllowedEmailDomains(t *testing.T) {
33773538 querystring: "?allowed_email_domains=a.b.c.example.com,*.c.example.com",
33783539 expectedStatusCode: http.StatusAccepted,
33793540 },
3541+ {
3542+ name: "UserWithMultipleAtSignsExactDomain",
3543+ email: "attacker@evil.com@example.com",
3544+ querystring: "?allowed_email_domains=example.com",
3545+ expectedStatusCode: http.StatusForbidden,
3546+ },
3547+ {
3548+ name: "UserWithMultipleAtSignsWildcardDomain",
3549+ email: "attacker@evil.com@foo.example.com",
3550+ querystring: "?allowed_email_domains=*.example.com",
3551+ expectedStatusCode: http.StatusForbidden,
3552+ },
3553+ {
3554+ name: "UserWithMultipleAtSignsDotPrefixedDomain",
3555+ email: "attacker@evil.com@foo.example.com",
3556+ querystring: "?allowed_email_domains=.example.com",
3557+ expectedStatusCode: http.StatusForbidden,
3558+ },
33803559 }
33813560
33823561 for _, tc := range testCases {
@@ -3583,3 +3762,49 @@ func TestGetOAuthRedirectURI(t *testing.T) {
35833762 })
35843763 }
35853764}
3765+
3766+func TestIdTokenPlaceholderInSignOut(t *testing.T) {
3767+ opts := baseTestOptions()
3768+ opts.WhitelistDomains = []string{"my-oidc-provider.example.com"}
3769+
3770+ err := validation.Validate(opts)
3771+ assert.NoError(t, err)
3772+
3773+ const emailAddress = "john.doe@example.com"
3774+ const userName = "9fcab5c9b889a557"
3775+ created := time.Now()
3776+
3777+ session := &sessions.SessionState{
3778+ User: userName,
3779+ Groups: []string{"a", "b"},
3780+ Email: emailAddress,
3781+ IDToken: "eYjjjjjj.vvvv.ddd",
3782+ AccessToken: "oauth_token",
3783+ CreatedAt: &created,
3784+ }
3785+
3786+ proxy, err := NewOAuthProxy(opts, func(email string) bool {
3787+ return true
3788+ })
3789+ assert.NoError(t, err)
3790+
3791+ // Save the required session
3792+ rw := httptest.NewRecorder()
3793+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
3794+ err = proxy.sessionStore.Save(rw, req, session)
3795+ assert.NoError(t, err)
3796+
3797+ rw = httptest.NewRecorder()
3798+
3799+ rdUrl := url.QueryEscape("https://my-oidc-provider.example.com/sign_out_page?id_token_hint={id_token}&post_logout_redirect_uri=https://my-app.example.com/")
3800+ req, _ = http.NewRequest(http.MethodGet, "/oauth2/sign_out?rd="+rdUrl, nil)
3801+ req = middlewareapi.AddRequestScope(req, &middlewareapi.RequestScope{
3802+ RequestID: "11111111-2222-4333-8444-555555555555",
3803+ Session: session,
3804+ })
3805+
3806+ proxy.SignOut(rw, req)
3807+ newLocation := rw.Header().Values("Location")[0]
3808+
3809+ assert.Equal(t, "https://my-oidc-provider.example.com/sign_out_page?id_token_hint=eYjjjjjj.vvvv.ddd&post_logout_redirect_uri=https://my-app.example.com/", newLocation)
3810+}