LoginSignup
3
2

More than 1 year has passed since last update.

Flaskを使用したZoom OAuth 連携用API作成 メモ

Last updated at Posted at 2021-07-03
  • Flask を使用してZoom OAuth連携をサポートするためのAPI作成方法についての個人用メモ。
    • Google連携用APIを試した時と同様に、Docker起動できる形で部分的にAPI化した。

作成するAPI

①認可リクエスト作成API

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

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

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

  • 認可レスポンスとしてzoomからのリダイレクトで受け取った認可コードを指定し、トークンリクエストとユーザー情報取得を行う。

    • 今回は簡略化のため認可リクエストパラメータのDB保存まで実装しておらず、検証処理は行っていない。実運用では検証処理を入れることを推奨。
    • リクエスト例
    POST /api/zoom/auth_request/complete HTTP/1.1
    Host: localhost:5000
    Content-Type: application/json
    Content-Length: 52
    
    {
        "code":"..."
    }
    
    • レスポンス例(ユーザー情報)
    {
        "account_id": "...",
        "cms_user_id": "",
        "created_at": "2021-07-03T08:32:58Z",
        "dept": "",
        "email": "hogehoge@example.com",
        "first_name": "...",
        "group_ids": [],
        "host_key": "...",
        "id": "...",
        "im_group_ids": [],
        "jid": "....",
        "job_title": "",
        "language": "jp-JP",
        "last_login_time": "2021-07-03T08:32:58Z",
        "last_name": "...",
        "location": "",
        "login_types": [
            1,
            100
        ],
        "personal_meeting_url": "...",
        "phone_country": "",
        "phone_number": "",
        "pic_url": "...",
        "pmi": ...,
        "role_id": "0",
        "role_name": "Owner",
        "status": "active",
        "timezone": "",
        "type": 1,
        "use_pmi": false,
        "verified": 0
    }
    

事前作業

プロジェクト構成

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

実装

  • docker-compose.yml

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

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

    • 環境変数ファイル。アプリケーション登録時に発行・設定した値を指定する。
  CLIENT_ID=YOUR_CLIENT_ID
  CLIENT_SECRET=YOUR_CLIENT_SECRET
  REDIRECT_URI=http:YOUR_CALLBACK_URL
  AUTHORIZATION_ENDPOINT=https://zoom.us/oauth/authorize
  TOKEN_ENDPOINT=https://zoom.us/oauth/token
  USERINFO_ENDPOINT=https://zoom.us/v2/users/me
  • 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.zoom import zoom_router
  from flask_cors import CORS


  def create_app():

      app = Flask(__name__)

      CORS(app, supports_credentials=True)

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

      return app


  app = create_app()

  • be/api/views/zoom.py

    • コントローラー
    • ※エラーハンドリング皆無
from flask import Flask, Blueprint, request
import hashlib
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
zoom_router = Blueprint('zoom_router', __name__)

# Client Param
client_id = os.getenv('CLIENT_ID')
client_secret = os.getenv('CLIENT_SECRET')
redirect_uri = os.getenv('REDIRECT_URI')

# Zoom Endpoint
authorization_endpoint = os.getenv('AUTHORIZATION_ENDPOINT')
token_endpoint = os.getenv('TOKEN_ENDPOINT')
userinfo_endpoint = os.getenv('USERINFO_ENDPOINT')

app = Flask(__name__)


# Create Authorization Request Endpoint
@zoom_router.route("/zoom/auth_request/create",  methods=['POST'])
def create():

    state = hashlib.sha256(os.urandom(32)).hexdigest()
    # Create Authz Request URL
    # https://zoom.us/oauth/authorize?response_type=code&client_id=...&redirect_uri=...&state=...
    auth_request_url = authorization_endpoint+'?{}'.format(parse.urlencode({
        'client_id': client_id,
        '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
@zoom_router.route("/zoom/auth_request/complete", methods=['POST'])
def complete():
    # Parse Req Body
    jsonData = json.dumps(request.json)
    req_body = json.loads(jsonData)
    # Token Request
    token_req_body = parse.urlencode({
        'code': req_body["code"],
        'client_id': client_id,
        'client_secret': client_secret,
        'redirect_uri': redirect_uri,
        'grant_type': 'authorization_code'
    }).encode('utf-8')
    token_req = req.Request(token_endpoint)
    err_str = ''
    try:
        with req.urlopen(token_req, data=token_req_body) as f:
            token_res = f.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)['access_token']
    # Get User Info
    # https://marketplace.zoom.us/docs/api-reference/zoom-api/users/user
    headers = {
        'Authorization': 'Bearer ' + access_token
    }
    user_info_req = req.Request(
        userinfo_endpoint, headers=headers, method='GET')
    try:
        with req.urlopen(user_info_req) as user_info_res:
            user_info_res_body = user_info_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)
    return json.loads(user_info_res_body)

起動

docker-compose up -d

参考情報

3
2
2

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
3
2