Skip to content

Commit 64cc691

Browse files
authored
Fix safari cookie session bug (#24772)
Partically backport #24330 Related: #24176 Maybe fix #24770
1 parent 1bad05d commit 64cc691

File tree

2 files changed

+44
-0
lines changed

2 files changed

+44
-0
lines changed

modules/context/context.go

+20
Original file line numberDiff line numberDiff line change
@@ -473,13 +473,33 @@ func (ctx *Context) JSON(status int, content interface{}) {
473473
}
474474
}
475475

476+
func removeSessionCookieHeader(w http.ResponseWriter) {
477+
cookies := w.Header()["Set-Cookie"]
478+
w.Header().Del("Set-Cookie")
479+
for _, cookie := range cookies {
480+
if strings.HasPrefix(cookie, setting.SessionConfig.CookieName+"=") {
481+
continue
482+
}
483+
w.Header().Add("Set-Cookie", cookie)
484+
}
485+
}
486+
476487
// Redirect redirects the request
477488
func (ctx *Context) Redirect(location string, status ...int) {
478489
code := http.StatusSeeOther
479490
if len(status) == 1 {
480491
code = status[0]
481492
}
482493

494+
if strings.Contains(location, "://") || strings.HasPrefix(location, "//") {
495+
// Some browsers (Safari) have buggy behavior for Cookie + Cache + External Redirection, eg: /my-path => https://other/path
496+
// 1. the first request to "/my-path" contains cookie
497+
// 2. some time later, the request to "/my-path" doesn't contain cookie (caused by Prevent web tracking)
498+
// 3. Gitea's Sessioner doesn't see the session cookie, so it generates a new session id, and returns it to browser
499+
// 4. then the browser accepts the empty session, then the user is logged out
500+
// So in this case, we should remove the session cookie from the response header
501+
removeSessionCookieHeader(ctx.Resp)
502+
}
483503
http.Redirect(ctx.Resp, ctx.Req, location, code)
484504
}
485505

modules/context/context_test.go

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package context
5+
6+
import (
7+
"net/http"
8+
"net/http/httptest"
9+
"testing"
10+
11+
"code.gitea.io/gitea/modules/setting"
12+
13+
"github.com/stretchr/testify/assert"
14+
)
15+
16+
func TestRemoveSessionCookieHeader(t *testing.T) {
17+
w := httptest.NewRecorder()
18+
w.Header().Add("Set-Cookie", (&http.Cookie{Name: setting.SessionConfig.CookieName, Value: "foo"}).String())
19+
w.Header().Add("Set-Cookie", (&http.Cookie{Name: "other", Value: "bar"}).String())
20+
assert.Len(t, w.Header().Values("Set-Cookie"), 2)
21+
removeSessionCookieHeader(w)
22+
assert.Len(t, w.Header().Values("Set-Cookie"), 1)
23+
assert.Contains(t, "other=bar", w.Header().Get("Set-Cookie"))
24+
}

0 commit comments

Comments
 (0)