fix(503-1 · 503-2 로그인): 성공 뒤 세션 캐시 비움(되튕김) · 마무리 쓰기 한 트랜잭션 · bcrypt 스레드

- requestLogin 응답 뒤 clearSessionCache — 화면 뜬 뒤 4 s 안 누름도 대시보드
- finish_login = 한 커넥션 · begin 1 · commit 1(users UPDATE · 신뢰 기기 · 세션 · login_logs) · 실패 = rollback
- verify_password = asyncio.to_thread(로그인 · OTP 확인) — 이벤트 루프 안 막음
- 시험 test_309_1_login.py

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-21 12:13:35 +09:00
co-authored by Claude Opus 5.5
parent 163ab72881
commit 0b8c34a2c2
5 changed files with 271 additions and 65 deletions
+1 -15
View File
@@ -3,7 +3,7 @@
import hashlib
import re
import secrets
from datetime import datetime, timedelta
from datetime import datetime
from typing import Any
import bcrypt
@@ -88,20 +88,6 @@ def delete_session_cookie(response: Response) -> None:
)
async def create_session(user_id: int, user_agent: str) -> str:
session_id = secrets.token_urlsafe(32)
expires_at = datetime.utcnow() + timedelta(seconds=SESSION_MAX_AGE_SECONDS)
pool = get_db_pool()
async with pool.acquire() as connection, connection.cursor() as cursor:
await cursor.execute(
"""INSERT INTO sessions (id, user_id, user_agent_hash, expires_at)
VALUES (%s, %s, %s, %s)""",
(session_id, user_id, hash_user_agent(user_agent), expires_at),
)
await connection.commit()
return session_id
async def verify_session(request: Request) -> dict[str, Any]:
session_id = request.cookies.get(SESSION_COOKIE_NAME)
if not session_id: