Skip to content

Commit

Permalink
FEAT: 카카오 로그인 클라이언트 기준으로 변경
Browse files Browse the repository at this point in the history
  • Loading branch information
dongwooklee96 committed Sep 23, 2023
1 parent 4ac2192 commit 902652f
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 81 deletions.
5 changes: 3 additions & 2 deletions database/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
11 changes: 5 additions & 6 deletions src/auth/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"}
83 changes: 24 additions & 59 deletions src/router/auth.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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)}
19 changes: 13 additions & 6 deletions src/user/application/user.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
from database.models import User
from src.user.repository.users import UserRepository


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)
35 changes: 27 additions & 8 deletions src/user/repository/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 902652f

Please sign in to comment.