Studyplus Engineering Blog

スタディプラスの開発者が発信するブログ

Zoom Marketplaceにアプリを公開するまで

こんにちは、Studyplus for School事業部エンジニアの島田です。

今回は先日リリースしたStudyplus for School(以下FS) のZoomとの連携機能と、そのリリースに至るまでのプロセスを紹介させていただきます。

prtimes.jp

導入の背景

FSを利用している塾・予備校の多くでコロナ禍によりオンライン指導が急速に進んでいます。その中でZoomを導入しているケースが多くありました。オンライン指導をするにあたってミーティング予定を作成するためにZoomを起動する手間を省き、FSからZoomのミーティングの予定をシームレスに登録出来る事が望まれていました。

以上から、開発機能の要件としては

  1. FSからZoomのミーティング予定を登録出来る
  2. ミーティング予定を登録すると、既存の生徒メッセージ機能で予定の内容が生徒へ送信される

となりました。

Zoom OAuth Appについて

Zoomと連携する方法はいくつかありますが、今回の実現方法はZoom Outh App を作成し、FSからZoomのAPI を利用することにしました。

OAuthアプリの作成

アプリの作成については、こちらのページの手順どおりに進めていくだけになります。

基本的にZoomはドキュメントが充実していたので、アプリ作成については大きくつまづくことはありませんでした。

ここではFSのアプリを作成するための主だった設定を抜粋します。

  • App Type: User-managed app
    • 今回は、認証したユーザーに対するミーティング設定なのでこちらとなります。
  • Intent to publish: Yes
    • FSを介して学習塾が利用するためには(App Marketplaceへの)公開が必要となります。
  • Scopes: meeting:write user:read
    • こちらを参考にします。今回はユーザー情報取得とミーティング作成に必要な項目となります。

Rubyでの実装について

上記で作成したアプリケーションのClient IDとClient Secretを利用したRubyの実装サンプルを紹介します。

今回はこのサンプルや本実装でもZoomAPIの処理部分で、それ用のgemは以下の理由で利用しませんでした。

  • ZoomAPIのgemは存在するが、メンテナンスされているgemが少ない
  • 今回、FSで実現するためのAPIは少ないのでgemを利用することの方が今後を考えるとリスク、コストがあると判断

利用するAPIについて

FSでは今回は以下のAPIを利用しました。

APIを利用して、

  • 連携
    1. Zoomとの連携の最初にOAuthでトークンを取得
    2. /me はどのZoomのどのアカウントと紐づいているかをユーザーページで確認するために利用
  • ミーティング作成
    1. FSの生徒メッセージ画面にてミーティング予定を入力
    2. 入力されたミーティング予定でZoomのミーティング作成APIを呼び出す
    3. 作成された予定を生徒にメッセージする といったフローになります。

f:id:yo-shimada:20201105163448j:plain

実装サンプル

RubyでのOAuthを利用したAPI呼び出しを確認するための最小の実装を紹介します。

  1. OAuthトークンを取得する
  2. 上記で取得したトークンを利用して、API(/users/me)でユーザー情報を取得する

概略コード

require "net/http"
require "base64"

class ZoomController < ApplicationController
  CLIENT_ID = ENV["ZOOM_CLIENT_ID"]
  CLIENT_SECRET = ENV["ZOOM_CLIENT_SECRET"]
  REDIRECT_URI = "http://localhost:3000/zoom/oauth"

  def oauth
    uri = URI.parse("https://zoom.us/oauth/token")
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = uri.scheme === "https"

    query = {
      grant_type: "authorization_code",
      code: params["code"],
      redirect_uri: REDIRECT_URI,
    }

    credential = Base64.strict_encode64("#{CLIENT_ID}:#{CLIENT_SECRET}")
    headers = { "authorization" => "Basic #{credential}" }
    response = http.post(uri.path, query.to_param, headers)

    if response.code == "200"
      body = JSON.parse(response.body)
      # access_token, refresh_tokenを永続化する
      # body
      # => {"access_token"=> "xxx",
      #     "token_type"=>"bearer",
      #     "refresh_token"=> "xxx",
      #     "expires_in"=>3599,
      #     "scope"=>"meeting:read meeting:write user:read user_profile"}
      Rails.logger.debug("#{body.inspect}")

      # 試しに自分の情報を取得する
      profile = users_me(body["access_token"])
      render plain: profile
      return
    end

    render plain: "NG"
  end

  private

  def users_me(token)
    uri = URI.parse("https://api.zoom.us/v2/users/me")
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = uri.scheme === "https"

    headers = { "authorization" => "Bearer #{token}" }
    response = http.get(uri.path, headers)

    body = JSON.parse(response.body)
    "#{body["last_name"]} #{body["first_name"]}, #{body["email"]}"
  end
end

アプリの申請について

実装が完了したら、いよいよZoomのApp Marketplaceに公開するためアプリの申請をします。申請フローついてはこちらにまとまっています。

チェックリストを元に、レビューのプロセスに必要なページの用意やアプリの設定をします。

大まかにまとめると、

  • マーケットプレイスへ公開するために必要なアプリ情報をZoomのアプリ設定画面から設定する
  • アプリを利用するために必要なドキュメントを整備する
  • Zoomがアプリをレビューするために必要な環境を整える
  • セキュリティアンケートに回答する

となります。

中でも手間がかかったのは、「ドキュメントの整備」になります。 Zoomマーケットプレイスでは英語のアプリ情報のみがサポート(レビュー)対象となっています。そのためアプリ情報に関連する全てのコンテンツ(利用規約、プライバシーポリシー、Zoom連携機能のサポートページ)とアプリ情報に利用する画像や説明文のテキストを英語に翻訳する必要がありました。ページによっては新規に英語版のページを用意することにもなりました。 アプリが特定の地域・国のみでの利用を前提としている場合にも、このコンテンツは英語である必要があります。また、特定の地域のみで利用する場合にはそれをアプリ情報の長文説明に記載する必要があります。

アプリ申請の却下について

アプリ申請が却下される場合には、およそ2~3営業日ほどでメールが来ます。メールには却下理由が丁寧に指摘されたGoogleドキュメントが添付されています。

レビューはこのレビューのプロセス の順番で実施されているようで、途中で不備が見つかるとそこでレビューを終わらせて指摘をしてくるようです。

機能・ユーザビリティテスト

このフェーズで指摘された点は、主に上記で説明した英語化をする箇所の認識の漏れ等がありドキュメント整備の不備によるものが多かったです。

英語化する箇所が多かったものの、難易度としてはそこまで難しいものではありませんでした。

セキュリティテスト

このフェーズでは、セキュリティに関してのアンケートへの回答とサービスの脆弱性チェックがされます。

そこで指摘された内容が、

  • OAuthトークンの暗号化
  • TLS 1.1のバージョンアップ
  • LocalStorageに機密情報を保存しない。機密データを保存する必要がある場合は、httponlysecure の属性を設定したセッションクッキーの利用をする

といった内容になり、かなり細かくチェックを受けました。

この中で対応が大変だったのは、LocalStorageの指摘になります。これを改善するためにFSのログイン方法を大きく変更する必要があり、その為の修正におよそ1ヶ月ほどの時間を要する事になりました。

最後に

  • Zoomのアプリ設定画面やドキュメントはしっかりしていて開発はスムーズに出来た
  • 申請後のZoomからのフィードバックのスピードが早く、開発が間延びする事が少なく助かった
  • 開発以外での英語化対応に時間をとられた
  • セキュリティテストは、下手な脆弱性診断を受けるよりよっぽど良かったのではないか
  • 認証方法の変更については指摘の課題感は以前からあったが優先して対応するタイミングがなかったので、このタイミングで出来た事は良かった(Zoom連携機能のリリースは遅れたが)

以上の事を経て、無事Zoomマーケットプレイスに公開されました。

marketplace.zoom.us