Heesung Yang
[Django] Oauth2 구현하기 (google oauth2)
설치 및 django 프로젝트 초기화
~$ pip install django social-auth-app-django
~$ django-admin startproject oauth2_project
~$ cd oauth2_project
-
accounts app 생성
~$ python manage.py startapp accounts
-
accounts/models.py
from django.contrib.auth.models import AbstractUser class CustomUser(AbstractUser): pass
-
-
accounts app 등록 및 기본 user model 설정
-
oauth2_project/settings.py
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'social_django', 'accounts', ] AUTH_USER_MODEL = 'accounts.CustomUser' LOGIN_REDIRECT_URL = '/' LOGOUT_REDIRECT_URL = '/'
-
-
social-auth-app-django 등록 및 관련 설정 추가
-
oauth2_project/settings.py
AUTHENTICATION_BACKENDS = [ 'social_core.backends.google.GoogleOAuth2', # for google 'django.contrib.auth.backends.ModelBackend', # django default auth backend ] SOCIAL_AUTH_TRAILING_SLASH = False # Remove trailing slash from routes SOCIAL_AUTH_GOOGLE_OAUTH2_DOMAIN = 'https://accounts.google.com/o/oauth2/auth' SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = 'GOOGLE_AUTH_KEY' SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = 'GOOGLE_AUTH_SECRET' SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = [ 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile', 'openid', ] SOCIAL_AUTH_URL_NAMESPACE = 'accounts:social'
-
-
url 생성
-
accounts/urls.py
from django.urls import path from django.urls.conf import include from django.contrib.auth.views import LogoutView app_name = 'accounts' urlpatterns = [ path('', include('social_django.urls')), path('logout/', LogoutView.as_view(), name="logout") ]
-
oauth2_project/urls.py
from django.contrib import admin from django.urls import path from django.urls.conf import include from django.views.generic.base import TemplateView urlpatterns = [ path('admin/', admin.site.urls), path('accounts/', include('accounts.urls', namespace='accounts')), path('', TemplateView.as_view(template_name='home.html'), name='home'), ]
-
-
로그인/로그아웃 테스트용 페이지 템플릿 생성
-
templates/home.html
{% if user.is_authenticated %} <p> Hello, {{ user.username }} </p> <a href="{% url 'accounts:logout' %}">Logout</a> {% else %} <a href="{% url 'accounts:social:begin' 'google-oauth2' %}">Login with Google</a> {% endif %}
-
-
테스트용 템플릿 경로 추가
-
oauth2_project/settings.py
import os # ... TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ os.path.join(BASE_DIR, 'templates') ], # Add !!! 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
-
인증 시나리오 사용자 정의
기본 정의 함수
기본 인증 시나리오는 아래와 같다. 아래 정의된 함수들이 차례차례 실행된다.
SOCIAL_AUTH_PIPELINE = (
'social_core.pipeline.social_auth.social_details',
'social_core.pipeline.social_auth.social_uid',
'social_core.pipeline.social_auth.auth_allowed',
'social_core.pipeline.social_auth.social_user',
'social_core.pipeline.user.get_username',
'social_core.pipeline.user.create_user',
'social_core.pipeline.social_auth.associate_user',
'social_core.pipeline.social_auth.load_extra_data',
'social_core.pipeline.user.user_details',
)
https://python-social-auth.readthedocs.io/en/latest/pipeline.html#authentication-pipeline
기본 인증 시나리오를 변경하고 싶은 경우,
settings.py
파일에 위 항목 설정- 원하는 함수를 추가 또는 삭제
- 사용자 정의 함수 추가도 가능
만약 사용자 생성 시나리오를 삭제하고 싶다면(이미 등록된 사용자만 로그인을 허용하려면),
social_core.pipeline.user.create_user
항목을 삭제한다.
SOCIAL_AUTH_PIPELINE = (
'social_core.pipeline.social_auth.social_details',
'social_core.pipeline.social_auth.social_uid',
'social_core.pipeline.social_auth.auth_allowed',
'social_core.pipeline.social_auth.social_user',
'social_core.pipeline.user.get_username',
# 'social_core.pipeline.user.create_user',
'social_core.pipeline.social_auth.associate_user',
'social_core.pipeline.social_auth.load_extra_data',
'social_core.pipeline.user.user_details',
)
각 함수의 역할은 아래와 같다.
social_core.pipeline.social_auth.social_details
-
details 라는 dict에 값들을 채워준다. 다음 단계에서 user 객체를 만드는데 사용한다.
details = { 'username': 'hsyang', 'email': 'example@gmail.com', 'fullname': 'Heesung Yang', 'first_name': 'Heesung', 'last_name': 'Yang' }
social_core.pipeline.social_auth.social_uid
- oauth2 서비스 제공자(google, facebook 등)에서의 unique identifier 값을 가져온다.
- google oauth provider는 email 주소를 리턴한다.
social_core.pipeline.social_auth.auth_allowed
- email 또는 domain 이 유효한지 체크하는 등 현재 프로젝트에서 해당 사용자가 유효한지 체크한다.
social_core.pipeline.social_auth.social_user
- 이미 해당 oauth2 계정이 있는지 체크한다.
social_core.pipeline.user.get_username
- 사용자 이름을 생성한다.
- 계정 정보 중 일부가 중복될 경우, random 문자열을 사용자 이름에 추가한다.
social_core.pipeline.mail.mail_validation
- 이메일 인증을 진행한다.
- disabled by default
social_core.pipeline.social_auth.associate_by_email
- 유사한 이메일을 사용하는 다른 사용자 계정이 있을 경우, 해당 계정과 연결한다.
- disabled by default
social_core.pipeline.user.create_user
- 연결된 계정이 없을 경우 사용자 계정을 생성한다.
social_core.pipeline.social_auth.associate_user
- 사용자 계정과 oauth2 정보를 연결하는 레코드를 생성한다.
social_core.pipeline.social_auth.load_extra_data
- 설정값에 지정된 필드 데이터를 추출하여 extra_data 값을 채운다.
social_core.pipeline.user.user_details
- oauth2 의 정보가 변경된 경우, 연결된 사용자 계정의 정보를 업데이트 한다.
사용자 정의 함수
처음 oauth2 계정과 연결 시, MyGroup
이라는 그룹에 포함시키고 싶은 경우 아래와 같이 할 수 있다.
-
accounts
라는 이름의 app 생성 -
accounts/pipelines.py
파일 작성from django.contrib.auth.models import Group def initial_user_group(*args, **kwargs): if kwargs['is_new'] == False: return group, created = Group.objects.get_or_create(name='MyGroup') user = kwargs['user'] user.groups.add(group)
-
settings.py 수정
SOCIAL_AUTH_PIPELINE = ( 'social_core.pipeline.social_auth.social_details', 'social_core.pipeline.social_auth.social_uid', 'social_core.pipeline.social_auth.auth_allowed', 'social_core.pipeline.social_auth.social_user', 'social_core.pipeline.user.get_username', 'social_core.pipeline.user.create_user', 'social_core.pipeline.social_auth.associate_user', 'social_core.pipeline.social_auth.load_extra_data', 'social_core.pipeline.user.user_details', 'accounts.pipelines.initial_user_group', # Add )
kwargs에는 아래와 같은 값들이 넘어오므로, 필요한 값을 참조하여 함수를 작성하자.
kwargs: {
'response': {
'access_token': 'ACCESS_TOKEN_WILL_BE_CHANGED',
'expires_in': 3599,
'scope': 'openid https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile',
'token_type': 'Bearer',
'id_token': 'ID_TOKEN_WILL_BE_CHANGED',
'sub': '100000000000000000000',
'name': 'Heesung Yang',
'given_name': 'Heesung',
'family_name': 'Yang',
'picture': 'https://profile.googleusercontent.com/profile_picture',
'email': 'example@gmail.com',
'email_verified': True,
'locale': 'en',
},
'user': <User: hsyang>,
'strategy': <social_django.strategy.DjangoStrategy object at 0x10f16a160>,
'storage': <class 'social_django.models.DjangoStorage'>,
'backend': <social_core.backends.google.GoogleOAuth2 object at 0x10f16af40>,
'is_new': False,
'request': <WSGIRequest: GET '/accounts/complete/google-oauth2'>,
'details': {
'username': 'hsyang',
'email': 'example@gmail.com',
'fullname': 'Heesung Yang',
'first_name': 'Heesung',
'last_name': 'Yang'
},
'pipeline_index': 9,
'uid': 'example@gmail.com',
'social': <UserSocialAuth: hsyang>,
'new_association': False,
'username': 'hsyang'
}
FAQ
Django 사용자 계정이 비활성화 된 경우 처리 방법
https://python-social-auth.readthedocs.io/en/latest/configuration/settings.html#urls-options
비활성화 된 계정입니다. 관리자에게 문의하세요.
라는 메시지를 보여주기 위한 URL을 설정한다.
-
settings.py
SOCIAL_AUTH_INACTIVE_USER_URL = '/'
Previous post
Bash Color 예제 및 활용