Django에 Social Login 붙이기: Django세팅부터 Facebook/Google 개발 설정까지

Django에 Social Login 붙이기: Django세팅부터 Facebook/Google 개발 설정까지

Django등 웹 서비스를 제공하며 항상 다루게 되는 주제가 있습니다. 유저를 우리 서비스의 유저 모델을 통해 직접 가입시키느냐, 혹은 타사의 oAuth를 이용한 Social Login을 붙여 가입없이(혹은 최소화) 서비스를 이용할 수 있도록 유도 하느냐 등입니다.

Django에서 이러한 Social Login을 이용하기 위한 라이브러리는 여러개가 있었고, 대표적으로는 django-social-authpython-social-auth가 있었지만, 두 프로젝트 모두 현재(2017.02.08기준) Deprecated되었고 이 프로젝트들은 python-social-auth가 org자체로 이전해 social-auth-app-django로 바뀌었습니다.

한편 -social-auth들의 대체재로 django-allauth가 있는데, 올해 1월에도 새 버전 릴리즈가 있는만큼 활동적인 프로젝트입니다. (하지만 이번 글에서는 다루지 않습니다.)

이번 게시글에서는 social-auth-app-django을 이용해 Django 프로젝트에 social login을 붙여봅니다.

참조한 공식 docs는 python-social-auth configuration django에서 볼 수 있습니다.

참고: social-auth-app-django는 pip패키지 이름이며, 프로젝트 이름은 python-social-auth로 동일합니다.

설치하기

widgets:

1
pip install social-auth-app-django

Django의 기본 ORM을 이용하고 있다면 social-auth-app-django를 pip로 설치하면 됩니다.

settings.py 설정하기

widgets:

INSTALLED_APPS 추가하기

settings.py에 social_django를 추가해줍니다.

1
2
3
4
5
INSTALLED_APPS = (
...
'social_django',
...
)

앱 추가후 migrate를 해줘야 정상적으로 Social Auth용 DB Table이 생성됩니다.

1
python manage.py migrate

AUTHENTICATION_BACKENDS 추가하기

Social Login은 기존 유저모델과 함께 사용이 가능합니다. 하지만 기본 유저 ModelBackend를 사용하지 않고 독자적인 ModelBackend를 사용하기 때문에 settings.py의 AUTHENTICATION_BACKENDS에 Social login용 Backends를 추가해줘야 합니다.

1
2
3
4
5
6
AUTHENTICATION_BACKENDS = [
'social_core.backends.google.GoogleOAuth2', # Google
'social_core.backends.facebook.FacebookOAuth2', # Facebook
...
'django.contrib.auth.backends.ModelBackend', # Django 기본 유저모델
]

django.contrib.auth.backends.ModelBackend가 있어야 createsuperuser로 만들어진 계정의 로그인이 가능해집니다.

Social Login용 URL Namespace 지정

최상위 프로젝트 urls.py에 지정할 social login의 namespace를 지정해줍니다. 또한, Login 후 어떤 URL로 장고가 유저를 Redirect시킬지 지정해 줍니다.

1
2
3
SOCIAL_AUTH_URL_NAMESPACE = 'social'

LOGIN_REDIRECT_URL='/'

꼭 namespace가 ‘social’일 필요는 없습니다. 하지만 가이드에서는 ‘social’을 사용하기에 아래 urls.py 설정에서도 동일하게 사용할 예정입니다.

Social Login을 위한 API Key/Secret 설정하기

우선 프로젝트 BASE_DIR(manage.py파일이 있는 폴더)에 envs.json이라는 이름의 환경변수를 담은 json 파일을 만들어 줍니다.

1
2
3
4
5
6
{
"FACEBOOK_KEY":"숫자숫자숫자들",
"FACEBOOK_SECRET":"숫자영어숫자영어들",
"GOOGLE_KEY":"숫자-영어.apps.googleusercontent.com",
"GOOGLE_SECRET":"숫자영어대문자들"
}

당연하게도 위 파일은 실제 동작하는 Key와 Secret이 아닙니다.

Social Login을 사용하기 위해 Google에서는 Google+ API를 활성화 하고 OAuth 2.0 클라이언트 ID를 ‘웹 애플리케이션’으로 생성해 API Key/Secret을 발급받아야 합니다.

Google Login은 Google+ API에 연결되어있기 때문에 다른 Login API는 없습니다.

Facebook의 경우에는 Facebook for Developers에서 새 앱 추가 후 ‘Facebook 로그인’ 제품을 활성화 시킨 후 앱의 대시보드에서 앱 ID와 앱 시크릿 코드를 받아 이용하면 됩니다.

두 서비스 모두 지정된 url에서만 동작하기 때문에 Google의 경우에는 ‘OAuth 2.0 클라이언트 ID’에서 ‘승인된 리디렉션 URI’에 http://localhost:8000/complete/google-oauth2/을 추가해줘야 하며, Facebook의 경우에는 ‘Facebook 로그인’의 ‘클라이언트 OAuth 설정’에 있는 ‘유효한 OAuth 리디렉션 URI’에 http://localhost:8000/을 추가해주면 됩니다.

위 설정을 모두 하지 않을 경우 40x번대 에러가 발생합니다.

이제 위에서 만든 envs.json파일을 환경변수로 사용해야 합니다. settings.py파일 최상위에 이와 같은 코드를 적용해 줄 경우, 개발용 envs_dev.json와 배포용 envs.json, 그리고 환경변수로 관리되는 경우 모두 커버가 가능합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import os
import json

from django.core.exceptions import ImproperlyConfigured

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# Env for dev / deploy
def get_env(setting, envs):
try:
return envs[setting]
except KeyError:
error_msg = "You SHOULD set {} environ".format(setting)
raise ImproperlyConfigured(error_msg)

DEV_ENVS = os.path.join(BASE_DIR, "envs_dev.json")
DEPLOY_ENVS = os.path.join(BASE_DIR, "envs.json")

if os.path.exists(DEV_ENVS): # Develop Env
env_file = open(DEV_ENVS)
elif os.path.exists(DEPLOY_ENVS): # Deploy Env
env_file = open(DEPLOY_ENVS)
else:
env_file = None

if env_file is None: # System environ
try:
FACEBOOK_KEY = os.environ['FACEBOOK_KEY']
FACEBOOK_SECRET = os.environ['FACEBOOK_SECRET']
GOOGLE_KEY = os.environ['GOOGLE_KEY']
GOOGLE_SECRET = os.environ['GOOGLE_SECRET']
except KeyError as error_msg:
raise ImproperlyConfigured(error_msg)
else: # JSON env
envs = json.loads(env_file.read())
FACEBOOK_KEY = get_env('FACEBOOK_KEY', envs)
FACEBOOK_SECRET = get_env('FACEBOOK_SECRET', envs)
GOOGLE_KEY = get_env('GOOGLE_KEY', envs)
GOOGLE_SECRET = get_env('GOOGLE_SECRET', envs)

이와 같이 사용할 경우, APACHE웹서버 등에서 시스템 환경변수를 불러오지 못하는 상황이거나, HEROKU나 PythonAnywhere와 같은 PaaS에서도 Django코드와 API키들을 완전히 분리해 사용할 수 있습니다.

위에서 지정한 FACEBOOK_KEY들을 SocialLogin에 할당해 줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
# SocialLogin: Facebook
SOCIAL_AUTH_FACEBOOK_KEY = FACEBOOK_KEY
SOCIAL_AUTH_FACEBOOK_SECRET = FACEBOOK_SECRET
SOCIAL_AUTH_FACEBOOK_SCOPE = ['email']
SOCIAL_AUTH_FACEBOOK_PROFILE_EXTRA_PARAMS = {
'fields': 'id, name, email'
}

# SocialLogin: Google
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = GOOGLE_KEY
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = GOOGLE_SECRET
SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = ['email']

위 코드는 가장 기본적인 ‘email’을 유저 식별도구로 받아옵니다.

_PROFILE_EXTRA_PARAMS를 이용해 다른 Field를 받아올 수도 있습니다. (필수 아님)

project폴더의 urls.py 설정하기(최상위 urls.py)

widgets:

이제 프로젝트 폴더의 urls.py에 Social Login이 사용할 url들을 등록하고 namespace를 지정해 Template에서 사용할 수 있도록 설정해야 합니다.

1
2
3
4
5
6
7
8
from django.conf.urls import url, include # url뿐 아니라 include를 import해야 합니다.
from django.conf import settings
from django.contrib import admin

urlpatterns = [
url(r'^admin/', admin.site.urls),
url('', include('social_django.urls', namespace='social')), # 이 줄을 등록해주면 됩니다.
]

이와 같이 social_django.urls를 include하고 ‘social’ namespace를 등록해 줍니다.

Template에서 Social Login url 호출하기

widgets:

위 코드들을 추가해주는 것 만으로도 기본적인 Social Login기능은 완성되었습니다. 이제 Template에서 호출을 해봅시다.

1
2
{% raw %}<a href="{% url "social:begin" "google-oauth2" %}"><button class="btn btn-danger" style="width: 40%">G+ Login</button></a>
<a href="{% url "social:begin" "facebook" %}"><button class="btn btn-primary" style="width: 40%">FB Login</button></a>{% endraw %}

이와 같이 button을 등록해 호출할 수 있습니다.

위 버튼을 누를 경우 각각 Google/Facebook의 Social Login페이지로 넘어갑니다.

수고하셨습니다!

widgets:

위 코드만으로도 약간의 조작을 통해 더 멋진 Social Login기능을 구현하실 수 있으리라 생각합니다.

Happy Coding!

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×