API 서버에서 네이버 로그인 검증 및 테스트 하는 방법 (3/5)
안녕하세요 제하쓰의 제이입니다.
이전 시리즈에 이어서 API 서버에서 카카오 로그인을 구현, 검증, 테스트 까지 진행해보도록 하겠습니다.
API 서버에서 애플 로그인 검증 및 테스트하는 방법 (1/5)
API 서버에서 카카오 로그인 검증 및 테스트하는 방법 (2/5)
API 서버에서 네이버 로그인 검증 및 테스트하는 방법 (3/5)
API 서버에서 구글 로그인 검증 및 테스트하는 방법 (4/5)
API 서버에서 메타(페이스북) 로그인 검증 및 테스트하는 방법 (5/5)
익숙한 소셜 로그인 구조입니다. 저희 서버는 클라이언트에게 access_token과 refresh_token을 전달하여 로그인 과정을 처리하고 있습니다. 소셜 로그인을 경우 클라이언트가 네이버 인증서버에게 토큰을 발급받아 저희 서버에 전달해주면 벡엔드 안에서 해당 토큰을 네이버 API를 사용하여 검증후 클라이언트에게 access_token과 refresh_token을 전달해주는 방식입니다.
네이버에서 주는 인증 토큰은 jwt형식이 아니기 때문에 서버에서 자체적으로 공개키를 이용해 검증할 수 없습니다.
먼저 네이버에 애플리케이션을 등록하시면 다음과 같이 네이버 로그인에서 받아올 정보를 고르실 수 있습니다.
다만 꼭 필요하신 정보만 체크하시길 추천드립니다. 네이버는 해당 정보들이 애플리케이션에서 어떻게 활용되는지 자세한 설명과 스크린샷까지 첨부해야하기에 이점 유의하시길 바랍니다.
저희 애플리케이션은 이메일을 가지고 사용자를 구분 및 토큰을 발급하기 때문에 필수로 선택하였고 회원이름은 선택적으로 받을 수 있도록 하였습니다.
async def valid_naver_token(db: Session, user_id_token: str):
verify_url = "https://openapi.naver.com/v1/nid/me"
headers = {"Authorization": f"Bearer {user_id_token}"}
response = get(verify_url, headers=headers)
id_info = response.json()
# Naver에서 반환한 응답 코드 확인
if id_info.get("resultcode") == "00":
social_id = id_info.get("response", {}).get("id")
email = id_info.get("response", {}).get("email")
social_user = await verify_social_user(db, social_id, email, social_type="naver")
# 이메일과 사용자 ID 반환
return social_user.email, social_user.user_id
else:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="Invalid NAVER access token",
)
클라이언트에서 네이버 토큰과 함께 로그인 요청을 하면 서버에서 해당 토큰을 가지고 네이버 프로필 조회 API를 통해 유저 정보를 검증할 수 있습니다. 네이버 프로필 API의 경우 포스트맨을 통해 테스트해볼 수 있는데요, 먼저 네이버에서 주는 access_token이 필요합니다.
Auth 타입은 Oauth 2.0, Callback URL에서 Authorize using browser 체크해주시고
Auth URL: https://nid.naver.com/oauth2.0/authorize
Access Token URL: https://nid.naver.com/oauth2.0/token
Client ID: 내 애플리케이션 Client ID
Client Secret: 내 애플리케이션 Client Secret
Get New Access Token 버튼을 클릭하면 네이버 로그인 페이지로 이동하고 성공적으로 토큰을 발급받을 수 있습니다.
해당 토큰으로 프로필 API를 호출하면
response의 id값이 네이버의 고유한 id로 해당 값을 이용해 DB에서 사용자를 서칭하는 구조입니다.
async def verify_social_user(db: Session, social_id: str, email: str, social_type: str):
"""
소셜 사용자의 존재 여부 및 타입을 확인하고, 필요한 경우 이메일을 업데이트합니다.
"""
if await crud.check_duplicate_email(email=email, db=db):
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="User is signed up with email type")
social_user = await crud.get_social_user_by_social_id(
db, social_id
) or await crud.get_social_user(db, email)
if not social_user:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="User is not valid, please sign up",
)
if social_user.social_type != social_type:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"User is signed up with {social_user.social_type} type",
)
if social_user.email != email:
await crud.update_email_social_user(db, email, social_user.id)
db.commit()
return social_user
순서대로 토큰의 이메일을 가지고 이미 이메일로 회원가입한 유저인지 확인하고 sub 값으로 소셜 회원가입한 유저인지 확인합니다. 추가적으로 다른 소셜로 회원가입하였는지 확인 합니다.
회원가입된 유저가 없다면 회원가입 로직으로 분기할 수 있게 status code 403 으로 리턴합니다.
회원가입한 유저가 있다면 social_id를 리턴하여 access_token과 refresh_token을 발급하여 로그인 처리를 진행합니다.
마지막으로 sub값으로 DB에서 소셜 유저를 찾았는데 토큰의 이메일과 DB에 저장된 이메일이 다른 경우 (사용자가 중간에 소셜 계정의 이메일을 변경한 경우입니다) DB의 이메일을 토큰의 이메일로 덮어씌워 업데이트를 해줍니다.
테스트 코드를 작성해봅시다!
테스트: 네이버 로그인 응답(Mock 데이터) 생성
@pytest.fixture(scope="function")
def mock_naver_response_me():
return {
"resultcode": "00",
"message": "success",
"response": {
"id": "test",
"email": "test@naver.com",
},
}
이제 테스트 코드를 통해 API가 제대로 작동하는지 검증해보겠습니다. 테스트 프레임워크로 pytest를 사용중입니다. 테스트를 위해 실제 네이버 프로필 API에 요청을 보내는 대신, 프로필 API 응답을 Mock 데이터로 생성합니다.
async def test_social_login_naver_success(
self,
test_client: httpx.AsyncClient,
create_social_user: List[SocialUser],
mocker: MockerFixture,
mock_naver_response_me,
):
mock_response_me = Response()
mock_response_me.status_code = status.HTTP_200_OK
mock_response_me.json = lambda: mock_naver_response_me
mocker.patch(mock_naver_validation, return_value=mock_response_me)
response = await test_client.post(
"/api/v1/test/auth/social-signin/naver", json={"id_token": "test", "access_token": None}
)
assert response.status_code == status.HTTP_200_OK
test_social_login_naver_success() 함수
- 네이버 토큰으로 test를 담은 토큰을 보냅니다.
- 네이버 프로필 API에 실제 요청을 보내는 대신 mocking한 응답을 리턴받아 소셜 로그인 코드를 수행합니다.
질문, 피드백은 언제든지 환영입니다.