# backend/app/auth/deps.py — 인증 의존성 (데모 모드 우회 포함) from datetime import UTC, datetime from fastapi import Depends, HTTPException, Request from sqlmodel import Session, select from ..config import get_settings from ..db import get_session from ..models import ApiToken, AuthSession, Person, UserCredential from .tokens import hash_token, unsign_session_id DEMO_USER_ID = "jiwoo" # AUTH_ENABLED=false 일 때 고정 사용자(데모 무손상) def current_user(request: Request, session: Session = Depends(get_session)) -> Person: st = get_settings() # 1) 데모 모드: 인증 끔 → 항상 지우 if not st.auth_enabled: user = session.get(Person, DEMO_USER_ID) if user is None: raise HTTPException(500, "demo user 'jiwoo' missing — run_seed 필요") return user # 2) 쿠키 세션(웹) 우선 raw = request.cookies.get(st.session_cookie_name) if raw: sid = unsign_session_id(raw) if sid: sess = session.get(AuthSession, sid) if sess and not sess.revoked: exp = sess.expires_at if exp.tzinfo is None: # SQLite 는 naive 로 저장 → UTC 로 정규화 exp = exp.replace(tzinfo=UTC) if exp > datetime.now(UTC): u = session.get(Person, sess.user_id) if u: return u # 3) 베어러 토큰(스크립트/모바일) auth = request.headers.get("Authorization", "") if auth.startswith("Bearer "): th = hash_token(auth[7:]) tok = session.exec(select(ApiToken).where(ApiToken.token_hash == th)).first() if tok and not tok.revoked: tok.last_used_at = datetime.now(UTC) session.add(tok) session.commit() u = session.get(Person, tok.user_id) if u: return u raise HTTPException(status_code=401, detail="인증이 필요해요") def require_admin( user: Person = Depends(current_user), session: Session = Depends(get_session) ) -> Person: cred = session.exec(select(UserCredential).where(UserCredential.user_id == user.id)).first() if not cred or cred.role != "admin": raise HTTPException(403, "관리자만 접근할 수 있어요") return user