Skip to content

Commit 68ed622

Browse files
gorecodesclaude
andcommitted
fix(auth): derive cookie Secure flag from request scheme
Previously session and CSRF cookies always had Secure=True, causing browsers to reject them on plain-HTTP connections (e.g. VPN access without TLS termination). Now secure= follows request.url.scheme so cookies work on HTTP while remaining Secure on HTTPS. When behind a TLS-terminating proxy the scheme is rewritten to https via X-Forwarded-Proto (trusted proxy), so Secure is preserved there. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 5fd73ad commit 68ed622

3 files changed

Lines changed: 7 additions & 6 deletions

File tree

backend/arbor/csrf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ def generate_csrf_token() -> str:
3030
return secrets.token_urlsafe(_TOKEN_BYTES)
3131

3232

33-
def set_csrf_cookie(response: Response, token: str) -> None:
33+
def set_csrf_cookie(response: Response, token: str, *, secure: bool = True) -> None:
3434
response.set_cookie(
3535
key=CSRF_COOKIE_NAME,
3636
value=token,
3737
httponly=False,
38-
secure=True,
38+
secure=secure,
3939
samesite="strict",
4040
max_age=session_ttl_seconds(),
4141
path="/",

backend/arbor/main.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,9 @@ async def auth_login(request: Request):
259259
"step_up_method": session.get("step_up_method", ""),
260260
},
261261
)
262-
set_session_cookie(response, session["session_id"])
263-
set_csrf_cookie(response, generate_csrf_token())
262+
_secure = request.url.scheme == "https"
263+
set_session_cookie(response, session["session_id"], secure=_secure)
264+
set_csrf_cookie(response, generate_csrf_token(), secure=_secure)
264265
return response
265266

266267

backend/arbor/session.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,12 +276,12 @@ def session_from_cookie_header(cookie_header: str | None) -> str:
276276
return morsel.value if morsel is not None else ""
277277

278278

279-
def set_session_cookie(response: Response, session_id: str) -> None:
279+
def set_session_cookie(response: Response, session_id: str, *, secure: bool = True) -> None:
280280
response.set_cookie(
281281
key=session_cookie_name(),
282282
value=session_id,
283283
httponly=True,
284-
secure=True,
284+
secure=secure,
285285
samesite="strict",
286286
max_age=session_ttl_seconds(),
287287
path="/",

0 commit comments

Comments
 (0)