본문 바로가기

WebDev

django facebook login

django facebook login


이 글에서는 페이스북 로그인을 위한 그 어떤 모듈, django 서드파티 앱도 사용하지 않고, HTTP 요청만을 사용

렌더링하는 웹 페이지가 단 하나도 없는, 오로지 JSON API만을 담당하도록 만들어진 django-rest-framework기반 RestfulAPI 서버가 페이스북 로그인을 처리하는 방법을 다룬다.


disclaimer


이 글은 편리하고 대중적인 페이스북 로그인을 설명하지 않는다. 이 글에서 설명할 방법은 사서 고생하는 방법이다. 내용 또한 좀 길다



고생길의 시작


일단 최종 결과부터 보고 시작하자. 나는 작동 안 되는 것을 글로 쓰지 않는다.

GET /login_facebook?code=AQBUWDHTb1Zn1tULKnlNGdq49KETeAXYQk-pIT4-UaRJMEM6nvrIgc8CTiM4BdfI_XcVwzLl2eurmsJHDpx8AgE8UDhRufXABX2TmTxX45GKHUZQjdgdy8vZicx6w144qyNOGhla6yYdvfYBVTqE_aXymXJkhs3_fkwz8pklrUdl1vmDm9Px6UWg7EFDpNjlEEO6N5qZu5wFaY_lqAO6NqCAYrghNQ-fst7xxd-LPYR59KtofYDCRFspvA7jm6pKLVr0uWLQ0BoB0EqBhVK2-wrZAkZVlTfEd8q6NTve5bJ6CKceTcH-ErlL87lSG-m8U5jMpCVp8sYgMnbRg611T-r5
HTTP 200 OK
Allow: OPTIONS, POST, GET
Content-Type: application/json
Vary: Accept

{
"inspected": {
"scopes": [
"user_friends",
"email",
"public_profile"
],
"is_valid": true,
"app_id": "563193193877116",
"issued_at": 1454668306,
"expires_at": 1459852306,
"application": "phople",
"user_id": "438863309656541"
},
"is_valid": true,
"user_information": {
"timezone": 9,
"locale": "ko_KR",
"gender": "male",
"picture": {
"data": {
"url": "https://scontent.xx.fbcdn.net/hprofile-xfa1/v/t1.0-1/c15.0.50.50/p50x50/10354686_10150004552801856_220367501106153455_n.jpg?oh=31a919a3d795bc64ad02d0a1b462300e&oe=5736B82F",
"is_silhouette": true
}
},
"age_range": {
"min": 21
},
"last_name": "Lee",
"updated_time": "2015-07-11T17:39:44+0000",
"id": "438863309656541",
"verified": true,
"first_name": "JunHui",
"link": "https://www.facebook.com/app_scoped_user_id/438863309656541/",
"name": "JunHui Lee",
"email": "ohenwkgdj@gmail.com"
},
"access_token_response": {
"token_type": "bearer",
"access_token": "-비밀이지롱-",
"expires_in": 5153820
},
"access_token": "-비밀이지롱-",
"user_id": "438863309656541"
}


핵심은 user_id[각주:1]access_token[각주:2]이다. 자동 회원가입 처리를 위한 profile 정보나, 이름, email값도 소중한 것을 다 안다.

하지만 나를 힘들게 하는 것은 user_id와 access_token이었다. 특히 로그인한 유저의 user_id를 획득하기 위해 굉장히 성가신 일을 해야 했다.


일의 흐름은 다음과 같다:

  1. 로그인 팝업 띄우고 로그인, 연동 동의 처리하기 유일한 frontend 영역이다. https://www.facebook.com/dialog/oauth?client_id={페이스북앱아이디}&response_type=code&scope=user_friends,public_profile,email&redirect_uri={웹서버 처리 주소}[각주:3]
  2. access token 획득하기 1의 결과로 나온 'code'라는 값이 필요하다 
  3. user_id 획득하기 2의 결과로 나온 access token이 필요하다
  4. 유저 정보 얻기 최종장이다. access token과 user_id가 필요하다
  5. 결과를 클라이언트에게 반환하기

그냥 CheatSheet나 내놔!를 원한다면 맨 아래로




1. 로그인 팝업 띄우고 로그인, 연동 동의 처리하기


다음과 같이 요청을 보내면 된다

https://www.facebook.com/dialog/oauth?client_id=563193193877116&response_type=code&scope=user_friends,public_profile,email&redirect_uri=http://api.phople.us/login_facebook

빨간 색으로 처리된 부분을 자신의 페이스북 연동 앱에 맞게 수정하면 된다.


기본적으로 facebook app review를 받지 않았다면 scope를 사용해 얻을 수 있는 정보는 위 주소 예시에 나온 대로 3가지가 전부다. 더 받을 준비가 되었다면 scope에 더 많은 정보를 요청하게 해놓자.


요청 보내기

  • 개발중에는? 그냥 브라우저 띄우고 막바로 주소 넣어버리자
  • 프로덕션에서는? 페이지 열심히 넘기는 웹사이트를 만들었다면 문제없다. 만약 SPA(Single Page Application) 라면 윈도우를 새로 하나 띄워서 요청을 보내게 대충 자바스크립트 코드를 짜놓자[각주:4]. 페이스북 로그인 팝업 페이지를 보여줘야 하기 때문에 답이 없다.


이 과정의 결과로 code라는 값[각주:5]이 반환 redirect_uri을 향해 쿼리 파라미터 형태로 전달된다. 

if request.query_params.get('code'): 로 처리할 수 있다는 얘기.



2. access token 획득하기


다음과 같이 요청을 보내면 된다

https://graph.facebook.com/v2.3/oauth/access_token?client_id={appid}&redirect_uri={uri}&client_secret={appsecret}&code={code}


쿼리 파라미터에 대한 설명은 다음과 같다:

  • appid appid다. 1에서 사용한 그 녀석이다
  • uri 반드시 1에서 사용한 것과 값이 같아야 한다
  • appsecret 페이스북 개발자 페이지 가서 볼 수 있는 appsecret을 입력하면 된다.  당연한 얘기지만 이 값은 절대 하드코딩하지 말 것
  • code 1의 결과로 받았던 녀석이다. 넣어주자


3. user_id 획득하기


다음과 같이 요청을 보내면 된다

https://graph.facebook.com/debug_token?input_token={it}&access_token={at}

쿼리 파라미터에 대한 설명은 다음과 같다:

  • it 2의 결과로 나온 access token의 값을 넣어주자
  • at 우리 '앱'의 accesss token. 원래대로라면 발급 절차를 밟아야 하나, 모든 처리는 서버에서만 이루어지므로 편법을 쓰자. app-id|app-secret[각주:6]형태로 값을 넣어주면 된다.

이번 과정의 결과로는 뭐 이런 식의 JSON이 돌아온다

{

"data": {

              "app_id": "563193193877116",

              "application": "phople",

              "expires_at": 1454695200,

              "is_valid": true,

              "scopes": [

                 "user_friends",

                 "email",

                 "public_profile"

              ],

              "user_id": "438863309656541"

}

}

여겨볼건 is_validuser_id이다. is_valid가 영 좋지 않게 false라면 당장 처리를 중단하고 로그를 남기자. 해커일 수도 있다.


4. 유저 정보 얻기


다음과 같이 요청을 보내면 된다

https://graph.facebook.com/{uid}?fields=id,name,first_name,last_name,age_range,link,gender,locale,picture,timezone,updated_time,verified,email&access_token={access_token}


쿼리 파라미터에 대한 설명은 다음과 같다:

  • uid 3의 결과로 받은 user_id를 쓰자
  • access_token access_token을 쓰자

결과로는 뭐 이런 식의~~~

{

   "id": "438863309656541",

   "name": "JunHui Lee",

   "first_name": "JunHui",

   "last_name": "Lee",

   "age_range": {

      "min": 21

   },

   "link": "https://www.facebook.com/app_scoped_user_id/438863309656541/",

   "gender": "male",

   "locale": "ko_KR",

   "picture": {

      "data": {

         "is_silhouette": true,

         "url": "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-xfa1/v/t1.0-1/c15.0.50.50/p50x50/10354686_10150004552801856_220367501106153455_n.jpg?oh=31a919a3d795bc64ad02d0a1b462300e&oe=5736B82F&__gda__=1463225095_484ea4441f278e6b8895ab53a1be91a0"

      }

   },

   "timezone": 9,

   "updated_time": "2015-07-11T17:39:44+0000",

   "verified": true,

   "email": "ohenwkgdj\u0040gmail.com"

}


이 결과를 frontend로 넘겨주면 끝이다.




CHEAT-SHEET


이 모든걸 담아낸 나의 코드를 예제로 들었다.[각주:7][각주:8]

@api_view(['GET', 'POST'])
def login_facebook(request):
"""
login via facebook
:param request: request
:return: user information
"""
appid, appsecret = read_credential('facebook', 'APP_ID'), read_credential('facebook', 'APP_SECRET')
app_access_token = '{}|{}'.format(appid, appsecret)
# error case
if request.query_params.get('error'):
return Response('login rejected by user', status=status.HTTP_403_FORBIDDEN)
elif request.query_params.get('code'):
# step1: retrieving 'access token'
uri = 'https://graph.facebook.com/v2.3/oauth/access_token?client_id={appid}&redirect_uri={uri}&client_secret={appsecret}&code={code}'
code = request.query_params.get('code')
uri = uri.format(appid=appid, uri='http://api.phople.us/login_facebook', appsecret=appsecret, code=code)
access_token_response = nap.url.Url(uri).get().json()
access_token = access_token_response.get('access_token')

# step2: retrieving user information by using access token
# 2.1 inspect access token.
inspected = nap.url.Url('https://graph.facebook.com/debug_token'). \
get(params={'input_token': access_token, 'access_token': app_access_token}).json().get('data')
is_valid, user_id = inspected.get('is_valid', False), inspected.get('user_id', -1)
# 2.2 retrieving user information
uri = 'https://graph.facebook.com/{uid}?fields=id,name,first_name,last_name,age_range,link,gender,locale,picture,timezone,updated_time,verified,email&access_token={access_token}'
uri = uri.format(uid=user_id, access_token=access_token)
user_information = nap.url.Url(uri).get().json()
return Response(data=user_information)


  1. 페이스북 사용자의 진짜 id이다. facebook app마다 값이 달라진다는 점을 주의하자. [본문으로]
  2. 페이스북 사용자의 권한을 사용하는데 필요한 값이다. 이 값이 있다면 유저가 허락해준 권한을 내맘대로 이용할 수 있다. 주로 이메일 주소 확인하기, 상세 유저정보 보기에 활용된다. [본문으로]
  3. 내가 사용한 값은: https://www.facebook.com/dialog/oauth?client_id=563193193877116&response_type=code&scope=user_friends,public_profile,email&redirect_uri=http://api.phople.us/login_facebook [본문으로]
  4. 이 부분은 프론트엔드 개발자에게 맡기..자... 귀찮다.... [본문으로]
  5. 로그인을 표현하는 녀석으로, 일회용 암호값이다. [본문으로]
  6. 예를 들어, 13513131351331|38ddA392eur2e8183913835TUA38135kkxxA3591581373311 [본문으로]
  7. 필요한 파이썬3 모듈은 django, djangorestframework, nap이다. [본문으로]
  8. read_credential은 파일로 따로 뺀 값을 추출하기 위해 만든 함수다. 자신의 환경에 맞게 적절히 만들어두자. [본문으로]

'WebDev' 카테고리의 다른 글

Web SQL  (0) 2016.02.15
django custom user  (0) 2016.02.07
Python으로 현재 컴퓨터가 EC2인지 확인하기  (0) 2016.01.26
Pynamodb Basic Tutorial  (0) 2016.01.25
DRF file upload (파일 업로드)  (0) 2016.01.23