Google OpenID(Oauth) 로그인 구현하기 with django

maintain·2020년 8월 23일
1

먼저 본격적으로 구현하기 전에 Google Cloud Console에 접속해 프로젝트를 생성하고 인증 키를 생성해야 합니다.

1. 클라이언트 아이디 만들기

여기에 접속해서 로그인한 후 콘솔로 이동하여 새 프로젝트를 생성합니다. 그리고 왼쪽 위의 메뉴 > API 및 서비스 > Oauth 동의 화면을 눌러 다음 화면으로 이동합니다.

게임, 웹사이트 등에서 우리가 흔히 볼 수 있는 로그인 방식은 외부입니다. 내부는 G Suite 그룹 내에서만 쓸 수 있는 앱이라는 것을 뜻합니다. 외부를 선택하고 만들기를 누릅니다. 그럼 다음 화면이 나타납니다.

여기서 지원 이메일과 애플리케이션 이름을 작성하고 저장하면 됩니다. 기타 다른 설정들은 로컬 서버에서 간단한 실습 목적으로 사용할 것이라면 작성할 필요가 없으므로 생략하겠습니다. 이제 왼쪽 메뉴를 눌러 사용자 인증 정보 창으로
이동한 뒤, 사용자 인증 정보 만들기 > Oauth 클라이언트 만들기 > 웹 애플리케이션을 선택합니다.

여기서 앱 이름을 입력하고 승인된 리디렉션 URI를 입력해야 합니다. Oauth 과정에서 구글 측에서 인증을 마치고 특정 URI로 리다이렉트를 거는데 이 URI가 여기서 작성한 승인된 리디렉션 URI에 포함되어 있어야 합니다. 여러 개 입력할 수 있습니다. 이제 만들기를 누르면 클라이언트 ID를 만드는 과정이 끝납니다. 이것을 다운로드 받아서 로컬 환경에 저장합니다.

2. 코드 작성하기

2-1. 라이브러리 설치하기

저는 여기서 django와 python3를 사용하겠습니다. 기본적인 django 세팅은 생략하도록 하겠습니다. REST api를 이용해 구현해도 되지만 구글에서는 python SDK를 제공하고 있으므로 그것을 사용하겠습니다.

pip install --upgrade google-auth
pip install google-auth-oauthlib 
pip install google-auth-httplib2

단순 로그인 과정 이외에 구글 api를 이용하려면 다음을 추가로 설치해야 합니다.

pip install --upgrade google-api-python-client

2-2. 전체 과정 살펴보기

이번에 사용하는 구글의 Oauth는 Authorization Code Grant Type 방식이며 요약하면 다음과 같습니다.
1. 리다이렉트할 uri, 사용자에게 앱에서 사용하도록 요청할 정보 범위 등을 포함한 flow 객체를 만듭니다.
2. 이 flow 객체가 만들어낸 구글 로그인 uri를 사용자에게 보냅니다.
3. 사용자가 위 구글 로그인 uri에서 요청 정보 범위, 앱 이름 등을 확인합니다.
4. 구글에서 이를 1에서 지정했던 uri로 리다이렉트하면서 정보 요청 코드를 보냅니다.
5. 이 코드를 통해 구글에 데이터를 요청합니다.
6. 구글에서 받아온 데이터를 서버 데이터와 조회해 로그인 처리를 합니다.
7. 구글 api 사용을 요청한 경우 6 과정에서 받은 access token과 refresh 토큰으로 api를 사용할 수 있습니다.

2-3. flow 작성하기

api 사용이 아닌 단순 로그인 과정만 해보도록 하겠습니다.
google_oauth.py

import google.oauth2.credentials
import google_auth_oauthlib.flow
from google.oauth2 import id_token
from google.auth.transport import requests

flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
    {구글 클라이언트 시크릿 파일 경로},
    scopes = ['openid',
        'https://www.googleapis.com/auth/userinfo.email',
        'https://www.googleapis.com/auth/userinfo.profile'
    ],
    state = '12345678910',
)

flow.redirect_uri = 'http://127.0.0.1:8000/'

authorization_url, state = flow.authorization_url(
    access_type='offline',
    include_granted_scopes='true')
    
    
def verify_id_token(credentials):
    idinfo = id_token.verify_oauth2_token(credentials.id_token, 
        requests.Request(),
        secret_keys.google_client_id
    )
    return idinfo

def verify_id_token_form_uri(uri):
    flow.fetch_token(authorization_response=uri)
    return verify_id_token(flow.credentials)

scope : 필수 입력 항목으로 사용자에게 요청할 정보 범위 입니다. 여기에 전체 목록이 나와있습니다.

state : 추천 입력 항목입니다. 구글이 리다이렉트하면서 동일한 값을 파라미터로 보내는 값이며 리다이렉트했을 때 이 권한 요청을 앱 서버에서 보낸 것이 맞는 지 확인할 수 있습니다.
access_type : 추천 입력 사용자가 로그아웃한 상태에서도(=브라우저를 종료한 상태)에서도 access_token을 쓸 수 있는지 없는지를 나타냅니다. 기본은 online으로 로그인 된 상태에서의 접근만이 허용됩니다.
그 외 여러 항목들이 있으며 자세한 것은 여기서 볼 수 있습니다.
이렇게 만들어진 authorization_url을 사용자에게 보내서 접속하게 합니다.

2-4. 응답 작성하기

views.py

def google_oauth_redirect(self, request):
	if request.GET.get('code'):
       		idinfo = google_oauth.verify_id_token_form_uri(
              		uri = request.build_absolute_uri())
           	request.session['sub'] = idinfo['sub']
           	request.session['name'] = idinfo['name']
           	return Reponse({로그인 완료 응답})
	elif request.GET.get('error'):
    		return Reponse({로그인 실패 응답})

리다이렉트 처리에서는 받아온 정보를 세션에 저장하면 일단 간단한 로그인 과정이 끝납니다. sub는 해당 유저의 고유한 식별자 입니다. 데이터베이스에 id로 사용할 수 있습니다. 실제 로그인 과정에서는 데이터베이스를 확인하고 추가하거나 불러오는 과정, state에 랜덤 해시를 생성해 확인하는 과정 등이 추가될 수 있습니다.

3. 참고

https://developers.google.com/identity/protocols/oauth2/web-server#creatingclient
https://ssungkang.tistory.com/entry/Django%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%9C%A0%EC%A7%80%ED%95%98%EA%B8%B0-%EC%BF%A0%ED%82%A4%EC%99%80-%EC%84%B8%EC%85%98
https://docs.djangoproject.com/en/3.0/ref/request-response/
https://opentutorials.org/course/3405
https://www.daleseo.com/google-oauth/

개인 공부 목적으로 작성하였으며 오류가 있을 시 댓글로 남겨주시면 감사하겠습니다.

2개의 댓글

comment-user-thumbnail
2020년 9월 25일

안녕하세요 궁금한게 있습니다. 하나의 구글로그인 버튼에 두개의 클라이언트 id를 연결해서 사용할 수 있나요??? 왜냐면
@aaa.com
@bbb.com

이렇게 두 회사의 이메일이 있는데
이 두회사의 구글로그인을 시켜주기위해 각각 클라이언트 ID를 만든 상황입니다.

답글 달기
comment-user-thumbnail
2020년 10월 5일

openid 스코프 왜오류날까요???ㅠ

답글 달기