fix(auth): set SameSite=None for viewer session cookie over HTTPS and update docs
This commit is contained in:
1 parent
85454610f4
commit
4fe09a0a3d
5 files changed
+69
-16
No files matched your search
@@ -88,29 +88,48 @@ func CookieSecure(r *http.Request) bool {
|
||||
return r.TLS != nil
|
||||
}
|
||||
|
||||
// SetCookie writes the admin session cookie.
|
||||
func SetCookie(w http.ResponseWriter, r *http.Request, cookieName, token string, expiresAt time.Time) {
|
||||
// SameSiteForUserCookie returns the SameSite policy for the viewer session cookie.
|
||||
// Over HTTPS it returns SameSite=None so the cookie is sent cross-site from the
|
||||
// Workers-served SPA; over plain HTTP (local dev) it falls back to Lax.
|
||||
func SameSiteForUserCookie(r *http.Request) http.SameSite {
|
||||
if CookieSecure(r) {
|
||||
return http.SameSiteNoneMode
|
||||
}
|
||||
return http.SameSiteLaxMode
|
||||
}
|
||||
|
||||
// SetCookie writes a session cookie with the given SameSite policy.
|
||||
// SameSite=None is only valid together with Secure, which is enforced here.
|
||||
func SetCookie(w http.ResponseWriter, r *http.Request, cookieName, token string, expiresAt time.Time, sameSite http.SameSite) {
|
||||
secure := CookieSecure(r)
|
||||
if sameSite == http.SameSiteNoneMode {
|
||||
secure = true
|
||||
}
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: cookieName,
|
||||
Value: token,
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteStrictMode,
|
||||
Secure: CookieSecure(r),
|
||||
SameSite: sameSite,
|
||||
Secure: secure,
|
||||
Expires: expiresAt,
|
||||
MaxAge: int(time.Until(expiresAt).Seconds()),
|
||||
})
|
||||
}
|
||||
|
||||
// ClearCookie removes the admin session cookie.
|
||||
func ClearCookie(w http.ResponseWriter, r *http.Request, cookieName string) {
|
||||
// ClearCookie removes a session cookie with the given SameSite policy.
|
||||
func ClearCookie(w http.ResponseWriter, r *http.Request, cookieName string, sameSite http.SameSite) {
|
||||
secure := CookieSecure(r)
|
||||
if sameSite == http.SameSiteNoneMode {
|
||||
secure = true
|
||||
}
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: cookieName,
|
||||
Value: "",
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteStrictMode,
|
||||
Secure: CookieSecure(r),
|
||||
SameSite: sameSite,
|
||||
Secure: secure,
|
||||
Expires: time.Unix(0, 0),
|
||||
MaxAge: -1,
|
||||
})
|
||||
|
||||
@@ -134,7 +134,7 @@ func TestSetCookie(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
expiresAt := time.Now().Add(1 * time.Hour)
|
||||
SetCookie(w, req, "test_cookie", "abc123", expiresAt)
|
||||
SetCookie(w, req, "test_cookie", "abc123", expiresAt, http.SameSiteStrictMode)
|
||||
|
||||
cookies := w.Result().Cookies()
|
||||
if len(cookies) != 1 {
|
||||
@@ -165,7 +165,7 @@ func TestClearCookie(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
ClearCookie(w, req, "test_cookie")
|
||||
ClearCookie(w, req, "test_cookie", http.SameSiteStrictMode)
|
||||
|
||||
cookies := w.Result().Cookies()
|
||||
if len(cookies) != 1 {
|
||||
@@ -183,6 +183,38 @@ func TestClearCookie(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSameSiteForUserCookie(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req.Header.Set("X-Forwarded-Proto", "https")
|
||||
if got := SameSiteForUserCookie(req); got != http.SameSiteNoneMode {
|
||||
t.Errorf("SameSiteForUserCookie(https) = %v, want None", got)
|
||||
}
|
||||
|
||||
req2 := httptest.NewRequest("GET", "/", nil)
|
||||
if got := SameSiteForUserCookie(req2); got != http.SameSiteLaxMode {
|
||||
t.Errorf("SameSiteForUserCookie(http) = %v, want Lax", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetCookie_NoneForcesSecure(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
w := httptest.NewRecorder()
|
||||
expiresAt := time.Now().Add(1 * time.Hour)
|
||||
SetCookie(w, req, "test_cookie", "abc123", expiresAt, http.SameSiteNoneMode)
|
||||
|
||||
cookies := w.Result().Cookies()
|
||||
if len(cookies) != 1 {
|
||||
t.Fatalf("expected 1 cookie, got %d", len(cookies))
|
||||
}
|
||||
c := cookies[0]
|
||||
if c.SameSite != http.SameSiteNoneMode {
|
||||
t.Errorf("cookie SameSite = %v, want None", c.SameSite)
|
||||
}
|
||||
if !c.Secure {
|
||||
t.Error("cookie Secure = false, want true (SameSite=None requires Secure)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTokenFromCookie(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req.AddCookie(&http.Cookie{Name: "session", Value: "token123"})
|
||||
|
||||
Reference in new issue
Block a user