import hashlib import hmac import time from fastapi import HTTPException, Request from app.core.config import SESSION_MAX_AGE_DAYS, SESSION_SECRET_KEY, SITE_PASSWORD COOKIE_NAME = "playchat_session" def _sign(payload: str) -> str: return hmac.new(SESSION_SECRET_KEY.encode(), payload.encode(), hashlib.sha256).hexdigest() def create_session_token() -> str: issued_at = str(int(time.time())) return f"{issued_at}.{_sign(issued_at)}" def verify_session_token(token: str) -> bool: issued_at, _, signature = token.partition(".") if not issued_at or not signature: return False if not hmac.compare_digest(_sign(issued_at), signature): return False age_seconds = time.time() - int(issued_at) return 0 <= age_seconds <= SESSION_MAX_AGE_DAYS * 86400 def require_site_session(request: Request) -> None: """サイト全体(設定画面以外の全API)を保護するセッションCookie認証。 SITE_PASSWORD が未設定の場合は既存のローカル開発体験を壊さないよう常に通す。 """ if not SITE_PASSWORD: return token = request.cookies.get(COOKIE_NAME) if not token or not verify_session_token(token): raise HTTPException(status_code=401, detail="Login required")