[DRF] 구글 소셜 로그인 구현하기 (JWT) (240328 수정)

JinUk Lee·2023년 4월 11일
0

DRF 학습하기

목록 보기
22/57
post-custom-banner

[DRF] 카카오 소셜 로그인 구현하기 (JWT), 쿠키 설정 및 주의사항 (CORS관련)

전체적인 코드는 카카오 로그인에서 사용한 것과 비슷하다.

#views.py

GOOGLE_CALLBACK_URI = "http://localhost:8080/login"  # 프론트 로그인 URI 입력




GOOGLE_CALLBACK_URI = "http://localhost:8080/login"  # 프론트 로그인 URI 입력

@api_view(["GET", "POST"])
@permission_classes([AllowAny])
def google_callback(request):
    client_id = os.getenv("구글 클라이언트 ID 환경변수")
    client_secret = os.getenv("구글 시크릿키 환경변수")
    code = request.GET.get("code")
    """
    Access Token Request
    """
    state = os.getenv("STATE") # 난수
    token_req = requests.post(
        f"https://oauth2.googleapis.com/token?client_id={client_id}&client_secret={client_secret}&code={code}&grant_type=authorization_code&redirect_uri={GOOGLE_CALLBACK_URI}&state={state}"
    )
    token_req_json = token_req.json()
    error = token_req_json.get("error")
    if error is not None:
        raise JSONDecodeError(error)
    access_token = token_req_json.get("access_token")
    """
    Email Request
    """
    email_req = requests.get(
        f"https://www.googleapis.com/oauth2/v1/tokeninfo?access_token={access_token}"
    )
    email_req_status = email_req.status_code
    if email_req_status != 200:
        return JsonResponse(
            {"err_msg": "failed to get email"}, status=status.HTTP_400_BAD_REQUEST
        )
    email_req_json = email_req.json()
    email = email_req_json.get("email")
    """
    Signup or Signin Request
    """

    cookie_max_age = 3600 * 24 * 14 # 14 days

    try:
        user = User.objects.get(email=email)
        # 기존에 가입된 유저의 Provider가 google이 아니면 에러 발생, 맞으면 로그인
        # 다른 SNS로 가입된 유저
        social_user = SocialAccount.objects.get(user=user)
        if social_user is None:
            return JsonResponse(
                {"err_msg": "email exists but not social user"},
                status=status.HTTP_400_BAD_REQUEST,
            )
        if social_user.provider != "google":
            return JsonResponse(
                {"err_msg": "no matching social type"},
                status=status.HTTP_400_BAD_REQUEST,
            )
        # 기존에 Google로 가입된 유저
        accept = requests.post(f"{BASE_URL}accounts/google/login/finish/", data=token_req_json)
        
        accept_status = accept.status_code
        if accept_status != 200:
            return JsonResponse({"err_msg": "failed to signin"}, status=accept_status)
        accept_json = accept.json()
        refresh_token = accept.headers['Set-Cookie']
        refresh_token = refresh_token.replace('=',';').replace(',',';').split(';')
        token_index = refresh_token.index(' refresh_token')
        refresh_token = refresh_token[token_index+1]
        accept_json.pop("user", None)
        response_cookie = JsonResponse(accept_json)
        response_cookie.set_cookie('refresh_token', refresh_token, max_age=cookie_max_age, httponly=True, samesite='Lax')
        return response_cookie
    
    
    except User.DoesNotExist:
        # 기존에 가입된 유저가 없으면 새로 가입
        accept = requests.post("http://localhost:8000/accounts/google/login/finish/", data=token_req_json)
        accept_status = accept.status_code
        if accept_status != 200:
            return JsonResponse({"err_msg": "failed to signup"}, status=accept_status)
        accept_json = accept.json()
        refresh_token = accept.headers['Set-Cookie']
        refresh_token = refresh_token.replace('=',';').replace(',',';').split(';')
        token_index = refresh_token.index(' refresh_token')
        refresh_token = refresh_token[token_index+1]
        accept_json.pop("user", None)
        response_cookie = JsonResponse(accept_json)
        response_cookie.set_cookie('refresh_token', refresh_token, max_age=cookie_max_age, httponly=True, samesite='Lax')
        return response_cookie

카카오와 아주 조금 다른게 있다면 state 라는 랜덤한 문자열이 필요하다.

state는 csrf 공격을 방지하기 위한 토큰같은 개념이다.

구글 로그인에 필요한 키는 아래의 링크에서 생성할 수 있다.

https://console.cloud.google.com/getting-started?hl=ko


클라이언트 ID와 보안 비밀번호를 받고, 리디렉션 URI에 콜백URI를 입력해주면 된다.


24.03.28 수정

allauth 패키지 업데이트로 인해 코드가 달라져 에러가 발생하는 부분을 발견해서 수정, 보완했다.

profile
개발자 지망생
post-custom-banner

0개의 댓글