pip install dj-rest-auth
pip install django-allauth
- dj-rest-auth : 회원가입, 로그인, 소셜로그인 기능 제공
(비밀번호 찾기 / 리셋, 회원가입 시 이메일 인증 등 유저 관련 기능 커버)- django-allauth : rest-auth를 사용하기 위한 라이브러리
(소셜로그인 기능을 사용하기 위해서 필요)
Installed apps = [
#설치한 라이브러리 목록에 추가
...
'allauth',
'allauth.account',
'allauth.socialaccount',
'allauth.socialaccount.providers.google',
]
REST_USE_JWT = True
#계정 인증 방식 = 이메일
ACCOUNT_AUTHENTICATION_METHOD = 'email'
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
)
}
Installed apps에 있는 allauth.socialaccount.providers.소셜로그인
에 google 이외에도 소셜로그인이 가능한 네이버나 카카오를 사용 가능하다.
추가적으로 dj_rest_auth.registration.views.SocialLoginView
를 사용하기 위해서는 REST_USE_JWT = True
를 추가해야 한다.
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/user/', include('allauth.urls')),
path('api/user/', include('user.urls')),
]
구글 클라우드에서 새 프로젝트 생성 이후에 앱 기본정보 입력 이후 사용자 인증 정보에서 call back url
지정
받아온 키를 client id와 secret key를 admin의 social application
해당 필드에 넣어주고, env 파일에 넣는다.
#.env
SOCIAL_AUTH_GOOGLE_CLIENT_ID = 'client id'
SOCIAL_AUTH_GOOGLE_SECRET = 'secret key'
STATE = '아무 문자열'
# urls.py
urlpatterns = [
# 구글 소셜로그인
path('google/login', google_login, name='name 지정'),
path('google/callback/', google_callback, name='name 지정'),
path('google/login/finish/', GoogleLogin.as_view(), name='name 지정'),
]
# views.py
import os
# 구글 소셜로그인 변수 설정
state = os.environ.get("STATE")
BASE_URL = 'http://localhost:8000/'
GOOGLE_CALLBACK_URI = BASE_URL + 'api/user/google/callback/'
# views.py
from django.shortcuts import redirect
import os
# 구글 로그인
def google_login(request):
scope = "https://www.googleapis.com/auth/userinfo.email"
client_id = os.environ.get("SOCIAL_AUTH_GOOGLE_CLIENT_ID")
return redirect(f"https://accounts.google.com/o/oauth2/v2/auth?client_id={client_id}&response_type=code&redirect_uri={GOOGLE_CALLBACK_URI}&scope={scope}")
# views.py
from json import JSONDecodeError
from django.http import JsonResponse
import requests
import os
from rest_framework import status
from .models import *
from allauth.socialaccount.models import SocialAccount
def google_callback(request):
client_id = os.environ.get("SOCIAL_AUTH_GOOGLE_CLIENT_ID")
client_secret = os.environ.get("SOCIAL_AUTH_GOOGLE_SECRET")
code = request.GET.get('code')
# 1. 받은 코드로 구글에 access token 요청
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}")
### 1-1. json으로 변환 & 에러 부분 파싱
token_req_json = token_req.json()
error = token_req_json.get("error")
### 1-2. 에러 발생 시 종료
if error is not None:
raise JSONDecodeError(error)
### 1-3. 성공 시 access_token 가져오기
access_token = token_req_json.get('access_token')
# 2. 가져온 access_token으로 이메일값을 구글에 요청
email_req = requests.get(f"https://www.googleapis.com/oauth2/v1/tokeninfo?access_token={access_token}")
email_req_status = email_req.status_code
### 2-1. 에러 발생 시 400 에러 반환
if email_req_status != 200:
return JsonResponse({'err_msg': 'failed to get email'}, status=status.HTTP_400_BAD_REQUEST)
### 2-2. 성공 시 이메일 가져오기
email_req_json = email_req.json()
email = email_req_json.get('email')
# return JsonResponse({'access': access_token, 'email':email})
# 3. 전달받은 이메일, access_token, code를 바탕으로 회원가입/로그인
try:
# 전달받은 이메일로 등록된 유저가 있는지 탐색
user = User.objects.get(email=email)
# FK로 연결되어 있는 socialaccount 테이블에서 해당 이메일의 유저가 있는지 확인
social_user = SocialAccount.objects.get(user=user)
# 있는데 구글계정이 아니어도 에러
if social_user.provider != 'google':
return JsonResponse({'err_msg': 'no matching social type'}, status=status.HTTP_400_BAD_REQUEST)
# 이미 Google로 제대로 가입된 유저 => 로그인 & 해당 우저의 jwt 발급
data = {'access_token': access_token, 'code': code}
accept = requests.post(f"{BASE_URL}api/user/google/login/finish/", data=data)
accept_status = accept.status_code
# 뭔가 중간에 문제가 생기면 에러
if accept_status != 200:
return JsonResponse({'err_msg': 'failed to signin'}, status=accept_status)
accept_json = accept.json()
accept_json.pop('user', None)
return JsonResponse(accept_json)
except User.DoesNotExist:
# 전달받은 이메일로 기존에 가입된 유저가 아예 없으면 => 새로 회원가입 & 해당 유저의 jwt 발급
data = {'access_token': access_token, 'code': code}
accept = requests.post(f"{BASE_URL}api/user/google/login/finish/", data=data)
accept_status = accept.status_code
# 뭔가 중간에 문제가 생기면 에러
if accept_status != 200:
return JsonResponse({'err_msg': 'failed to signup'}, status=accept_status)
accept_json = accept.json()
accept_json.pop('user', None)
return JsonResponse(accept_json)
except SocialAccount.DoesNotExist:
# User는 있는데 SocialAccount가 없을 때 (=일반회원으로 가입된 이메일일때)
return JsonResponse({'err_msg': 'email exists but not social user'}, status=status.HTTP_400_BAD_REQUEST)
# views.py
from dj_rest_auth.registration.views import SocialLoginView
from allauth.socialaccount.providers.oauth2.client import OAuth2Client
from allauth.socialaccount.providers.google import views as google_view
class GoogleLogin(SocialLoginView):
adapter_class = google_view.GoogleOAuth2Adapter
callback_url = GOOGLE_CALLBACK_URI
client_class = OAuth2Client