, ,
2016.02.06 04:08

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
django facebook login  (0) 2016.02.06
Python으로 현재 컴퓨터가 EC2인지 확인하기  (0) 2016.01.26
Pynamodb Basic Tutorial  (0) 2016.01.25
DRF file upload (파일 업로드)  (0) 2016.01.23

+ Recent posts