ssulbox/backend/app/kakao_client.py

59 lines
2.2 KiB
Python

# -*- coding: utf-8 -*-
"""카카오 OAuth 클라이언트 (castad app/user/services/kakao.py 이식·축약).
흐름: get_authorization_url() → 사용자 카카오 로그인 → 인가코드(code)
→ get_access_token(code) → get_user_info(token) → (id, nickname, email)
"""
from fastapi import HTTPException
import aiohttp
from .config import settings
AUTH_URL = "https://kauth.kakao.com/oauth/authorize"
TOKEN_URL = "https://kauth.kakao.com/oauth/token"
USER_INFO_URL = "https://kapi.kakao.com/v2/user/me"
def get_authorization_url() -> str:
if not settings.KAKAO_CLIENT_ID:
raise HTTPException(501, "카카오 로그인 미설정 — KAKAO_CLIENT_ID 필요")
return (
f"{AUTH_URL}?client_id={settings.KAKAO_CLIENT_ID}"
f"&redirect_uri={settings.KAKAO_REDIRECT_URI}&response_type=code"
)
async def get_access_token(code: str) -> str:
data = {
"grant_type": "authorization_code",
"client_id": settings.KAKAO_CLIENT_ID,
"redirect_uri": settings.KAKAO_REDIRECT_URI,
"code": code,
}
if settings.KAKAO_CLIENT_SECRET:
data["client_secret"] = settings.KAKAO_CLIENT_SECRET
async with aiohttp.ClientSession() as s:
async with s.post(TOKEN_URL, data=data) as r:
res = await r.json()
if "error" in res:
raise HTTPException(400, f"카카오 토큰 발급 실패: {res.get('error_description', res.get('error'))}")
return res["access_token"]
async def get_user_info(access_token: str) -> dict:
"""카카오 사용자 → {kakao_id:int, nickname:str|None, email:str|None, profile_image:str|None}."""
async with aiohttp.ClientSession() as s:
async with s.get(USER_INFO_URL, headers={"Authorization": f"Bearer {access_token}"}) as r:
if r.status != 200:
raise HTTPException(500, "카카오 사용자 정보 조회 실패")
info = await r.json()
acc = info.get("kakao_account") or {}
prof = acc.get("profile") or {}
return {
"kakao_id": int(info["id"]),
"nickname": prof.get("nickname"),
"email": acc.get("email"),
"profile_image": prof.get("profile_image_url"),
}