diff --git a/database/models.py b/database/models.py index 9d4dec3..781dfd2 100644 --- a/database/models.py +++ b/database/models.py @@ -32,8 +32,9 @@ class User(TimestampModel, table=True): __tablename__ = "user" user_key: int = Field(primary_key=True) - email: str - nickname: str + id: int + nick_name: Optional[str] = None + profile_url: Optional[str] = None class Category(TimestampModel, table=True): diff --git a/src/auth/auth.py b/src/auth/auth.py index 9df68f0..702cc66 100644 --- a/src/auth/auth.py +++ b/src/auth/auth.py @@ -83,13 +83,12 @@ async def get_current_user( user_service = UserService(user_repository=UserRepository(session=session)) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) - user_email: str = payload.get("sub") - if user_email is None: + user_key: int = payload.get("sub") + if user_key is None: raise credentials_exception - token_data = TokenEmail(user_email=user_email) except JWTError: raise credentials_exception - user = await user_service.get_user_by_email(email=user_email) + user = await user_service.get_user_by_user_key(user_key=int(user_key)) if user is None: raise credentials_exception return user @@ -101,12 +100,12 @@ async def get_current_active_user( return current_user -async def generate_access_token(email: str): +async def generate_access_token(user_key: int): """ :return: 액세스 토큰을 만들어서, 반환한다. """ access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) access_token = create_access_token( - data={"sub": email}, expires_delta=access_token_expires + data={"sub": str(user_key)}, expires_delta=access_token_expires ) return {"access_token": access_token, "token_type": "bearer"} diff --git a/src/router/auth.py b/src/router/auth.py index 752d8f1..c52dad2 100644 --- a/src/router/auth.py +++ b/src/router/auth.py @@ -1,9 +1,12 @@ -import requests +from typing import Optional + from fastapi import APIRouter, Depends +from jose import jwt +from pydantic import BaseModel from sqlalchemy.ext.asyncio import AsyncSession from database.connection import get_session -from src.auth.auth import generate_access_token +from src.auth.auth import generate_access_token, SECRET_KEY, ALGORITHM from src.user.application.user import UserService from src.user.repository.users import UserRepository @@ -12,63 +15,25 @@ ) -@auth_router.get("/login/kakao") -async def kakao_login(code: str, session: AsyncSession = Depends(get_session)): - """ - https://kauth.kakao.com/oauth/authorize?client_id=d12d5a49f7a6ae9c1fb44443ec2f18fb&redirect_uri=http://localhost:8000/api/v1/auth/login/kakao&response_type=code - 위의 URL로 접속한 다음에, 이루어지는 로직 - """ - # TODO: 해당 함수 리펙토링 하기 - """ - 문제점이라고 생각되는 것들 - 1. 페이로드가 좀 더럽다? - 2. 함수 하나가 너무 길다. - 3. - """ - # STEP 1. 토큰 발급 - url = "https://kauth.kakao.com/oauth/token" - kakao_data = { - "grant_type": "authorization_code", - "client_id": "d12d5a49f7a6ae9c1fb44443ec2f18fb", - "redirect_uri": "http://localhost:8000/api/v1/auth/login/kakao", - "code": code, - "client_secret": "Xh89YsF1r4D3BnrzcM7hHSluF7TrkIxz", - } - - headers = {"Content-Type": "application/x-www-form-urlencoded;charset=utf-8"} - response = requests.post(url, data=kakao_data, headers=headers) - access_token_dict = response.json() - access_token = access_token_dict["access_token"] +class LoginRequest(BaseModel): + id: int + nickname: str + profile_url: Optional[str] = None - # STEP 2. 액세스 토큰으로 부터 유저 정보 조회 - headers = { - "Content-Type": "Content-type: application/x-www-form-urlencoded;charset=utf-8", - "Authorization": f"Bearer {access_token}", - } - data = { - "property_keys": '["kakao_account.email", "kakao_account.profile, "kakao_account.name"]' - } - url = "https://kapi.kakao.com/v2/user/me" - response = requests.post(url, headers=headers, data=data) - response_dict = response.json().get("kakao_account") - - email = response_dict.get("email") - nickname = response_dict.get("profile").get("nickname") - response_dict.get("profile").get("thumbnail_image_url") - - # STEP 3. 이메일을 조회한다. - # 유저 이메일을 조회했을 때 회원이 존재한다면, 회원가입 아니면 액세스 토큰을 만들어서 반환 +@auth_router.post("/login/kakao") +async def kakao_login(req: LoginRequest, session: AsyncSession = Depends(get_session)): + """ + 로그인 정보로 로그인 한다. + """ user_service = UserService(user_repository=UserRepository(session=session)) - user = await user_service.get_user_by_email(email=email) - # 유저가 존재하면, 액세스 토큰을 만들어서 전달한다. - # TODO: 3. 여기 로직을 개선할 수 있을 것 같다. 4. 토큰값을 리턴해야하는데 현재 그냥 "test" 문자열 리턴하고 있음 - - if user: - return await generate_access_token(email=email) - else: - # 유저가 존재하지 않으면 회원 가입 및 생성 및 토큰을 전달한다. - user = await user_service.create_user(nickname=nickname, email=email) - response = await generate_access_token(email=email) - await session.commit() - return "test" + user = await user_service.get_user_by_id(id=req.id) + if not user: + user = await user_service.create_user( + id=req.id, nickname=req.nickname, profile_url=req.profile_url + ) + await session.commit() + token = await generate_access_token(user_key=user.user_key) + payload = jwt.decode(token["access_token"], SECRET_KEY, algorithms=[ALGORITHM]) + + return {"access_token": await generate_access_token(user_key=user.user_key)} diff --git a/src/user/application/user.py b/src/user/application/user.py index eb2077c..80347cc 100644 --- a/src/user/application/user.py +++ b/src/user/application/user.py @@ -1,4 +1,3 @@ -from database.models import User from src.user.repository.users import UserRepository @@ -6,14 +5,22 @@ class UserService: def __init__(self, user_repository: UserRepository): self.user_repository = user_repository - async def create_user(self, nickname: str, email: str): + async def create_user(self, id: int, nickname: str, profile_url: str): """ 유저를 생성한다. """ - return await self.user_repository.create_user(nickname=nickname, email=email) + return await self.user_repository.create_user( + id=id, nickname=nickname, profile_url=profile_url + ) - async def get_user_by_email(self, email: str) -> User: + async def get_user_by_id(self, id: int): """ - 유저를 유저 이메일로 조회한다. + 유저를 유저 아이디로 조회한다. """ - return await self.user_repository.get_user_by_email(email=email) + return await self.user_repository.get_user_by_id(id=id) + + async def get_user_by_user_key(self, user_key: int): + """ + 유저를 유저 키로 조회한다. + """ + return await self.user_repository.get_user_by_user_key(user_key=user_key) diff --git a/src/user/repository/users.py b/src/user/repository/users.py index f2a70ec..94bbd40 100644 --- a/src/user/repository/users.py +++ b/src/user/repository/users.py @@ -9,25 +9,44 @@ class UserRepository: def __init__(self, session): self.session = session - async def create_user(self, nickname, email): + async def create_user(self, id, nickname, profile_url): """ 이메일로 유저를 생성한다. """ user = User( + id=id, nickname=nickname, - email=email, - created_at=datetime.now(), - updated_at=datetime.now(), - deleted=False, + profile_url=profile_url, + created_at=datetime.utcnow(), + updated_at=datetime.utcnow(), ) self.session.add(user) + await self.session.flush() return user - async def get_user_by_email(self, email): + async def get_user_by_id(self, id: int): """ - 이메일에 일치하는 유저를 반환한다. + 아이디와 일치하는 유저를 반환한다. """ - statement = select(User).where(User.email == email) + statement = select(User).where(User.id == id) + result = await self.session.execute(statement) + user = result.scalar_one_or_none() + return user + + async def get_user_by_id(self, id: int): + """ + 아이디와 일치하는 유저를 반환한다. + """ + statement = select(User).where(User.id == id) + result = await self.session.execute(statement) + user = result.scalar_one_or_none() + return user + + async def get_user_by_user_key(self, user_key: int): + """ + 유저를 유저 키로 조회한다. + """ + statement = select(User).where(User.user_key == user_key) result = await self.session.execute(statement) user = result.scalar_one_or_none() return user