"""认证路由:开发档登录/登出(Web Session Cookie)。 正式 OAuth 2.1 授权码流程(/oauth2/authorize、/oauth2/token、PKCE)属 WP-01 后续条目;本路由只建立 Session 边界骨架供垂直切片验证。 """ from fastapi import APIRouter, HTTPException, Request, Response, status from app.auth.service import ( SESSION_COOKIE, create_session, revoke_session, ) from app.db.session import DbSession from app.models import PersonalDomain, User router = APIRouter(prefix="/v1/auth", tags=["auth"]) @router.post("/sessions", status_code=201) async def dev_login( request: Request, response: Response, db: DbSession, body: dict[str, str] ) -> dict[str, str]: """开发档登录:按 email 找到或创建用户与个人域,签发会话。""" email = body.get("email", "").strip().lower() if not email or "@" not in email: raise HTTPException(status.HTTP_422_UNPROCESSABLE_ENTITY, "email 不合法") from sqlalchemy import select existing = ( (await db.execute(select(User).where(User.email == email))).scalar_one_or_none() ) if existing is None: user = User(email=email, display_name=email.split("@")[0]) db.add(user) await db.flush() domain = PersonalDomain(user_id=user.id, name="默认镜域") db.add(domain) else: user = existing _, token, csrf = await create_session(db, user.id) await db.commit() response.set_cookie( SESSION_COOKIE, token, httponly=True, secure=True, samesite="lax", path="/", ) return {"csrf_token": csrf, "user_id": user.id} @router.delete("/sessions/current", status_code=204) async def logout(request: Request, response: Response, db: DbSession) -> None: token = request.cookies.get(SESSION_COOKIE) if token is not None and await revoke_session(db, token): await db.commit() response.delete_cookie(SESSION_COOKIE, path="/")