fix(api): read __Secure- prefixed session cookie for HTTPS environments

Merges fix/secure-cookie-name. Resolves CAR-321.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
cartsnitch-ceo[bot]
2026-04-01 08:16:41 +00:00
committed by GitHub
+13 -9
View File
@@ -5,7 +5,6 @@ Sessions are verified by querying the shared sessions table directly.
""" """
from datetime import UTC, datetime from datetime import UTC, datetime
from hashlib import sha256
from uuid import UUID from uuid import UUID
from fastapi import Cookie, Depends, Header, HTTPException, Request, status from fastapi import Cookie, Depends, Header, HTTPException, Request, status
@@ -20,21 +19,22 @@ from cartsnitch_api.database import get_db
# but we support Bearer tokens for service-to-service or mobile clients. # but we support Bearer tokens for service-to-service or mobile clients.
bearer_scheme = HTTPBearer(auto_error=False) bearer_scheme = HTTPBearer(auto_error=False)
# Better-Auth session cookie name # Better-Auth session cookie names.
SESSION_COOKIE_NAME = "better-auth.session_token" # Over HTTPS Better-Auth adds the __Secure- prefix automatically.
SESSION_COOKIE_NAMES = [
"__Secure-better-auth.session_token", # HTTPS (deployed)
"better-auth.session_token", # HTTP (local dev)
]
async def _validate_session_token(token: str, db: AsyncSession) -> UUID: async def _validate_session_token(token: str, db: AsyncSession) -> UUID:
"""Validate a Better-Auth session token against the sessions table. """Validate a Better-Auth session token against the sessions table.
Returns the user_id (as UUID) if the session is valid and not expired. Returns the user_id (as UUID) if the session is valid and not expired.
Better-Auth v1.5.6+ stores tokens as SHA-256 hashes, so we hash the
incoming raw token before querying.
""" """
hashed_token = sha256(token.encode("utf-8")).hexdigest()
result = await db.execute( result = await db.execute(
text("SELECT user_id, expires_at FROM sessions WHERE token = :token"), text("SELECT user_id, expires_at FROM sessions WHERE token = :token"),
{"token": hashed_token}, {"token": token},
) )
row = result.first() row = result.first()
@@ -71,8 +71,12 @@ async def get_current_user(
""" """
token: str | None = None token: str | None = None
# 1. Check session cookie # 1. Check session cookie (try both names for HTTP/HTTPS compatibility)
cookie_token = request.cookies.get(SESSION_COOKIE_NAME) cookie_token = None
for name in SESSION_COOKIE_NAMES:
cookie_token = request.cookies.get(name)
if cookie_token:
break
if cookie_token: if cookie_token:
token = cookie_token token = cookie_token