LoginSignup
0
3

More than 1 year has passed since last update.

Flaskを使用したFacebook ログイン 連携用API作成 メモ

Last updated at Posted at 2021-04-19

作成するAPI

認可リクエスト作成API

  • Facebookへの認可エンドポイントへアクセスするためのURLをパラメータをつけて生成・返却する。

    • stateはAPI呼び出し時に生成し、レスポンスとして返却する。
    • client_idなど固定の属性値は環境変数から取得する。
    • リクエスト例
    POST /api/facebook/auth_request/create HTTP/1.1
    Host: localhost:5000
    
    • レスポンス例
    {
        "authorization_request_url": "https://www.facebook.com/v10.0/dialog/oauth?client_id=AAABBBBCCC&scope=email&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2Fcallback&state=ABCDE12345&response_type=code",
        "state": "ABCDE12345"
    }
    

トークンリクエスト+ユーザー情報取得API

  • 認可レスポンスとして受け取った認可コード(code)を指定して、トークンリクエスト+ユーザー情報取得を行う。

    • 今回は簡略化のため認可リクエストパラメータのDB保存まで実装しておらず、検証処理は行っていないが、stateパラメータの検証は必ず行うこと。
    • リクエスト
    POST /api/facebook/auth_request/complete HTTP/1.1
    Host: localhost:5000
    Content-Type: application/json
    Content-Length: 363
    
    {
        "code":"XXX"
    }
    
  • レスポンス例

    {
        "id": "1234567890",
        "name": "山田太郎"
    }

プロジェクト構成

facebook_oidc
└─ docker-compose.yml
└─ facebook.env
│
└─ be
   └─ Dockerfile
   └─ requirements.txt
   │
   └─app
      └─ app.py
      │
      └─ api
          └─ __init__.py
          │
          └─views
             └─  facebook.py

実装

  • docker-compose.yml

※起動時に環境変数ファイルfacebook.envを読み込む。

  version: "3"
  services:
    be:
      container_name: be
      build: ./be
      env_file: facebook.env
      volumes:
        - ./be/app:/app
      ports:
        - "5000:5000"
      command: flask run --host 0.0.0.0 --port 5000
      tty: true
  • facebook.env

    • 環境変数ファイル。アプリケーション登録時に発行・設定した値を指定する。
  FACEBOOK_CLIENT_ID=YOUR_CLIENT_ID
  FACEBOOK_CLIENT_SECRET=YOUR_CLIENT_SECRET
  FACEBOOK_SCOPE_LIST=YOUR_SCOPE_LIST
  FACEBOOK_REDIRECT_URI=YOUR_REDIRECT_URI
  FACEBOOK_AUTHORIZATION_ENDPOINT=https://www.facebook.com/v10.0/dialog/oauth
  FACEBOOK_TOKEN_ENDPOINT=https://graph.facebook.com/v10.0/oauth/access_token
  FACEBOOK_APP_TOKEN_ENDPOINT=https://graph.facebook.com/oauth/access_token
  FACEBOOK_TOKEN_VALIDATION_ENDPOINT=https://graph.facebook.com/debug_token
  FACEBOOK_USER_INFO_ENDPOINT=https://graph.facebook.com/v10.0/
  • be/requirements.txt

    • Pythonライブラリ一式
  Flask
  Flask-Cors
  • be/Dockerfile
  FROM python:3.8

  RUN mkdir /app
  ADD requirements.txt /app

  ENV PYTHONUNBUFFERED 1
  EXPOSE 5000

  WORKDIR /app
  RUN pip3 install -r requirements.txt
  • be/app/app.py
  from api import app

  if __name__ == '__main__':
      app.run()
  • be/api/__init__.py
  from flask import Flask
  from .views.facebook import facebook_router
  from flask_cors import CORS


  def create_app():

      app = Flask(__name__)

      CORS(app, supports_credentials=True)

      app.register_blueprint(facebook_router, url_prefix='/api')

      return app


  app = create_app()

  • be/api/views/facebook.py

    • コントローラー
    • ※エラーハンドリング皆無
    import hashlib
    from flask import Flask, Blueprint, request
    import urllib.parse as parse
    import urllib.request as req
    import urllib.error as error
    import json
    import os
    from pprint import pprint
    
    # Routing Settings
    facebook_router = Blueprint('facebook_router', __name__)
    
    # Client Param
    client_id = os.getenv('FACEBOOK_CLIENT_ID')
    client_secret = os.getenv('FACEBOOK_CLIENT_SECRET')
    redirect_uri = os.getenv('FACEBOOK_REDIRECT_URI')
    scope_list = os.getenv('FACEBOOK_SCOPE_LIST')
    
    # Facebook Endpoint
    authorization_endpoint = os.getenv('FACEBOOK_AUTHORIZATION_ENDPOINT')
    token_endpoint = os.getenv('FACEBOOK_TOKEN_ENDPOINT')
    app_token_endpoint = os.getenv('FACEBOOK_APP_TOKEN_ENDPOINT')
    token_validation_endpoint = os.getenv('FACEBOOK_TOKEN_VALIDATION_ENDPOINT')
    user_info_endpoint = os.getenv('FACEBOOK_USER_INFO_ENDPOINT')
    
    app = Flask(__name__)
    
    # Create Authorization Request Endpoint
    @facebook_router.route("/facebook/auth_request/create",  methods=['POST'])
    def create():
        # Generate state
        state = hashlib.sha256(os.urandom(32)).hexdigest()
        # Create Authz Request URL
        auth_request_url = authorization_endpoint+'?{}'.format(parse.urlencode({
            'client_id': client_id,
            'scope': scope_list,
            'redirect_uri': redirect_uri,
            'state': state,
            'response_type': 'code'
        }))
        res_body = {
            "authorization_request_url": auth_request_url,
            "state": state
        }
        return json.loads(json.dumps(res_body))
    
    # Token Request And Get User Info Endpoint
    @facebook_router.route("/facebook/auth_request/complete", methods=['POST'])
    def complete():
        # Parse Req Body
        jsonData = json.dumps(request.json)
        req_body = json.loads(jsonData)
        # Exchange Authorization code for Access Token
        token_req_url = token_endpoint+'?{}'.format(parse.urlencode({
            'client_id': client_id,
            'client_secret': client_secret,
            'redirect_uri': redirect_uri,
            'code': req_body["code"]
        }))
        try:
            token_req = req.Request(token_req_url, method='GET')
            with req.urlopen(token_req) as token_res:
                token_res_body = token_res.read()
        except error.HTTPError as err:
            err_str = str(err.code) + ':' + err.reason + ':' + str(err.read())
            pprint(err_str)
        except error.URLError as err:
            err_str = err.reason
            pprint(err_str)
        access_token = json.loads(token_res_body)['access_token']
    
        # Generate App Access Token
        # https://developers.facebook.com/docs/facebook-login/access-tokens/#apptokens
        app_token_req_url = app_token_endpoint+'?{}'.format(parse.urlencode({
            'client_id': client_id,
            'client_secret': client_secret,
            'grant_type': 'client_credentials'
        }))
        try:
            app_token_req = req.Request(app_token_req_url, method='GET')
            with req.urlopen(app_token_req) as app_token_res:
                app_token_res_body = app_token_res.read()
        except error.HTTPError as err:
            err_str = str(err.code) + ':' + err.reason + ':' + str(err.read())
        except error.URLError as err:
            err_str = err.reason
        app_token = json.loads(app_token_res_body)['access_token']
    
        # Validate Access Token
        token_validation_req_url = token_validation_endpoint+'?{}'.format(parse.urlencode({
            'input_token': access_token,
            'access_token': app_token
        }))
        try:
            token_validation_req = req.Request(
                token_validation_req_url, method='GET')
            with req.urlopen(token_validation_req) as token_validation_res:
                token_validation_res_body = token_validation_res.read()
        except error.HTTPError as err:
            err_str = str(err.code) + ':' + err.reason + ':' + str(err.read())
            pprint(err_str)
        except error.URLError as err:
            err_str = err.reason
            pprint(err_str)
        # Get User Info
        # https://developers.facebook.com/docs/graph-api/reference/user
        user_request_url = user_info_endpoint+'/' + \
        json.loads(token_validation_res_body)['data']['user_id']+'?fields=email&access_token='+access_token
    
          user_req = req.Request(user_request_url, method='GET')
        with req.urlopen(user_req) as user_res:
            user_res_body = user_res.read()
    
        return json.loads(user_res_body)
    
    

起動

docker-compose up -d

参考情報

0
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3