from uuid import UUID from fastapi import APIRouter, Depends, Request, Response, HTTPException, Query from fastapi.responses import RedirectResponse from common.models.gmodel import UserInfo from router.v1.validator.dependencies import IsValidAccessToken from services import social_account_service as service from services.external.social import SocialError router = APIRouter(prefix="/v1/social/oauth", tags=["Social"]) COOKIE = "social_oauth_browser" @router.post("/connect") async def connect(response: Response, user: UserInfo = Depends(IsValidAccessToken)): try: url, browser = service.begin(UUID(user.user_id), 2) except SocialError as ex: raise HTTPException(409, str(ex)) from ex response.set_cookie( COOKIE, browser, httponly=True, secure=True, samesite="lax", max_age=600, path="/v1/social/oauth", ) response.headers["Cache-Control"] = "no-store" return {"url": url} @router.get("/callback") async def callback( request: Request, state: str = Query("", max_length=2048), code: str = Query("", max_length=4096), error: str = Query("", max_length=200), ): ok = False if not error and code and state: try: await service.finish(state, request.cookies.get(COOKIE), code) ok = True except Exception: # OAuth 응답·state에는 자격증명이 있으므로 예외 원문을 전파하지 않는다. pass response = RedirectResponse( "/sites?social=" + ("connected" if ok else "failed"), status_code=303 ) response.delete_cookie( COOKIE, path="/v1/social/oauth", secure=True, httponly=True, samesite="lax" ) response.headers["Cache-Control"] = "no-store" response.headers["Referrer-Policy"] = "no-referrer" return response @router.post("/disconnect") async def disconnect(user: UserInfo = Depends(IsValidAccessToken)): await service.disconnect(UUID(user.user_id), 2) return {"disconnected": True}