[0.9.28] - OAuth/Auth with httpOnly cookie
All checks were successful
Deploy on push / deploy (push) Successful in 4m32s

This commit is contained in:
2025-09-28 12:22:37 +03:00
parent 6451ba7de5
commit fb98a1c6c8
27 changed files with 1449 additions and 2147 deletions

View File

@@ -9,7 +9,14 @@ from starlette.responses import JSONResponse
from auth.utils import extract_token_from_request, get_auth_token_from_context, get_user_data_by_token
from services.auth import auth_service
from settings import SESSION_COOKIE_NAME
from settings import (
SESSION_COOKIE_DOMAIN,
SESSION_COOKIE_HTTPONLY,
SESSION_COOKIE_MAX_AGE,
SESSION_COOKIE_NAME,
SESSION_COOKIE_SAMESITE,
SESSION_COOKIE_SECURE,
)
from storage.schema import mutation, query, type_author
from utils.logger import root_logger as logger
@@ -84,20 +91,36 @@ async def login(_: None, info: GraphQLResolveInfo, **kwargs: Any) -> dict[str, A
result = await auth_service.login(email, password, request)
# Устанавливаем cookie если есть токен
if result.get("success") and result.get("token") and request:
# Устанавливаем httpOnly cookie если есть токен
if result.get("success") and result.get("token"):
try:
if not hasattr(info.context, "response"):
response = info.context.get("response")
if not response:
response = JSONResponse({})
response.set_cookie(
key=SESSION_COOKIE_NAME,
value=result["token"],
httponly=True,
secure=True,
samesite="strict",
max_age=86400 * 30,
)
info.context["response"] = response
response.set_cookie(
key=SESSION_COOKIE_NAME,
value=result["token"],
httponly=SESSION_COOKIE_HTTPONLY,
secure=SESSION_COOKIE_SECURE,
samesite=SESSION_COOKIE_SAMESITE
if SESSION_COOKIE_SAMESITE in ["strict", "lax", "none"]
else "none",
max_age=SESSION_COOKIE_MAX_AGE,
path="/",
domain=SESSION_COOKIE_DOMAIN, # ✅ КРИТИЧНО для поддоменов
)
logger.info(
f"✅ Email/Password: httpOnly cookie установлен для пользователя {result.get('author', {}).get('id')}"
)
# 💋 НЕ возвращаем токен клиенту - он в httpOnly cookie
result_without_token = result.copy()
result_without_token["token"] = None # Скрываем токен от JavaScript
return result_without_token
except Exception as cookie_error:
logger.warning(f"Не удалось установить cookie: {cookie_error}")
@@ -129,7 +152,11 @@ async def logout(_: None, info: GraphQLResolveInfo, **kwargs: Any) -> dict[str,
# Удаляем cookie
if request and hasattr(info.context, "response"):
try:
info.context["response"].delete_cookie(SESSION_COOKIE_NAME)
info.context["response"].delete_cookie(
key=SESSION_COOKIE_NAME,
path="/",
domain=SESSION_COOKIE_DOMAIN, # ✅ КРИТИЧНО: тот же domain что при установке
)
except Exception as e:
logger.warning(f"Не удалось удалить cookie: {e}")
@@ -174,10 +201,14 @@ async def refresh_token(_: None, info: GraphQLResolveInfo, **kwargs: Any) -> dic
info.context["response"].set_cookie(
key=SESSION_COOKIE_NAME,
value=result["token"],
httponly=True,
secure=True,
samesite="strict",
max_age=86400 * 30,
httponly=SESSION_COOKIE_HTTPONLY,
secure=SESSION_COOKIE_SECURE,
samesite=SESSION_COOKIE_SAMESITE
if SESSION_COOKIE_SAMESITE in ["strict", "lax", "none"]
else "none",
max_age=SESSION_COOKIE_MAX_AGE,
path="/",
domain=SESSION_COOKIE_DOMAIN, # ✅ КРИТИЧНО для поддоменов
)
except Exception as e:
logger.warning(f"Не удалось обновить cookie: {e}")