【GAS×ZOOM API】初学者にも優しくJWT認証の方法を解説してみる

こんにちは！GoogleAppsScriptを使った業務効率化に夢中の初心者プログラマー、太郎です。

今日はGoogleAppsScript(GAS)でZOOM APIを使用するために必要な**JWT認証の方法**を詳しく書いていきたいと思います！

記事を書くにあたって、この辺りの記事を大いに参考にさせていただきました。毎度毎度、先達の方々には感謝です。

* [How to Create Zoom Meetings with Google Script](https://www.labnol.org/code/zoom-meetings-200628)←英語だけど最も分かりやすく親切！

ただ、やっぱり、プログラミング歴数ヶ月、かつ外部APIを触ったことがほとんどない初心者にとってはどの記事も難しく、、、読み解くことに非常に苦労したんですね。。。。
なので！！！本記事では、、、初心者目線から、初心者の方になるべく分かりやすいように記事を書いていきたいと思います。

この記事の想定読者レベルはこんな感じです！

* JavaScriptの文法はある程度抑えている
* GASはある程度いじったことがある
* 外部APIとかほとんど使ったことないし、認証の仕組みもよく分からない。けど使いたい！！！

玄人の方からすれば、説明が一部間違っているところもあるかもしれませんが、ご了承ください。
また結論から知りたい方は、[まとめ](#まとめ-gasでzoomのjwt認証を行う方法)をご覧ください。
##そもそもJWT認証とは？
**JWT認証**とは、Json Web Tokenの略です。
簡単に言ってしまえば、**外部APIを安全に使用するための手段の一つ**です。外部APIを使用するときは、部外者に漏れたら悪用されかねない大事なパスワードを扱ったりするので、セキュリティに気を付けないといけないんですね。
そこで、トークン(Token)という一時的なパスワードを生成します。APIの利用にはそのパスワードが必要になるんですね。

ほら、銀行口座をネットで使うときも**『ワンタイムトークン』**って使いませんか？あれと同じ感じです。有効期限の短い一時的なパスワードを発行しなければ、外部APIは利用ができないんです。

##まずはZOOMに登録し、JWTappを作る
まずはZOOMのアカウントを作りましょう。アカウントの作成方法関しては説明省くので各自でお願いします。。
アカウントを作ったら、[ZOOMのマーケットプレイス](https://marketplace.zoom.us/)にアクセスします。
![スクリーンショット (97).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108046/592396c5-144e-d5b0-733b-fbf69edfe7c5.png)

右上の**『Develop』**をクリックし、ドロップダウンメニューから**『Build App』**を選択。
![スクリーンショット (98).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108046/2b6c2677-1e5d-4327-8a3a-3a659a669e35.png)

以下のような画面に飛ぶので、JWTアプリをCreateしましょう。（アプリの名前や所属を聞かれますが、適当に答えて大丈夫です。私はすでに作成しているのでCreateボタンが出ません）
![スクリーンショット (96).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108046/0876d255-c4cb-811f-0ba0-1b5c60c22700.png)
アプリが作成出来たら、こんな画面に飛びます。
![スクリーンショット (99)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108046/efd3ad07-e92e-13b0-7f3f-3d77f7e5da80.jpeg)
この**API Key**と**API Secret**はこの後の認証で使いますので、ページはそのまま開いておきましょう。
これで準備は完了です！
##最終ゴールはJWT Tokenを作ること
先に何を目指すのかをお伝えしておきます！最終ゴールは**JWT Token**を作ることです！
冒頭にもお伝えしましたが、これは、**認証に必要な一時的なパスワード**です。
先の画面を下にスクロールして『View JWT Token』を押してみてみてください。
![スクリーンショット (100)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108046/ff8e3ff6-371e-1d3d-fabc-e25406208e79.jpeg)
よく分からない文字列が生成されてますよね？これがJWT Tokenです！この文字列を自分自身で作成できなければ、APIが使えません(この画面でいちいち発行して使用することもできるんでしょうが、めんどいですよね)。そのためにまず、JWT Tokenの構造を紐解いていきましょう（というかJWT TokenってToken被ってない？？？）。
##JWT Tokenの構造
JWT Tokenの構造はこんな感じ。
`ヘッダー.ペイロード.署名`

**ヘッダー**はこういうオブジェクトです(この部分は理解しきっていないため詳しくは[こちらの記事](https://techblog.yahoo.co.jp/advent-calendar-2017/jwt/)参照)。

```javascript
header = {
  alg: "HS256",
  typ: "JWT"
}
```
`alg`はアルゴリズムのこと。`HS256`はHMAC SHA-256のアルゴリズムを使って、なんか上手いこと暗号化するよ！という意味です(たぶん)。

**ペイロード**はAPI_Keyと有効期限(expiry)の情報の入ったオブジェクトです。

```javascript
{
    iss: ZOOM_API_KEY, // ZOOMのAPIKEY
    exp: Date.now() + 1800 //有効期限、単位は秒
  };
```
上記の場合は、発行から1800秒(30分)有効にしてます。ちなみに、1970年1月1日午前0時0分0秒を基準としたUNIX時間です。

**署名**はまたちょっと複雑で、これ自体が`ヘッダー.ペイロード.APIシークレット`と、これまでのヘッダーとペイロードに加えAPIシークレットをつないだ文字列になっています。

さて、以上の3つ、ヘッダーとペイロードと署名がつながった文字列が、JWT Tokenです。

実物はこんな感じ！！！

```javascript
const JWT_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6ImtvaGVpIE9rYW1vdG8iLCJleHAiOjIzNDc1ODM3Mjg0NjV9.ka68fYi-fNErqIJrG-37J_aSK5Zux-EpJmm8XLSuGGU";
```
**はっ！！！？？？ナニコレ単なる文字列じゃん！**
初心者プログラマーにとっての発狂ポイントその１ですね。

こういう形を想像してたのに・・・

```javascript
const JWT_TOKEN = "{ alg: "HS256", typ: "JWT"}.{ iss: ZOOM_API_KEY, exp: Date.now() + 1800}.{署名情報何かしら}";
```
実は、ヘッダーとペイロードは64進法に変換し、署名はHMACSHA256で暗号化した後に、さらに64進法に変換する必要があるんですね。その結果さっきの文字列ができます。分かりやすいように.(ドット)で改行してあげると、、、

```javascript
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. // ヘッダーを64進法で変換
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6ImtvaGVpIE9rYW1vdG8iLCJleHAiOjIzNDc1ODM3Mjg0NjV9. // ペイロードを64進法で変換
ka68fYi-fNErqIJrG-37J_aSK5Zux-EpJmm8XLSuGGU //署名を暗号化した後64進法で変換
```
こういうことです。少しは理解できましたでしょうか・・・？
##トークンを作成する処理
さて、構造が分かったところでコード化するための具体的な処理を書いていきましょう。まずは、ヘッダーとペイロードから。

```javascript
const jsonHeader = JSON.stringify(header); // headerはJavaScriptオブジェクトなので、JSON形式の文字列に変更
const encodedHeader = Utilities.base64Encode(jsonHeader); // 64進法に変換
const jsonPayload = JSON.stringify(payload); // 同上
const encodedPayload = Utilities.base64Encode(jsonPayload); // 同上
```
署名に関してはもう少し複雑で、ZOOM APIの秘密鍵も必要になります。

```javascript
const signatureHMAC = Utilities.computeHmacSha256Signature(`${encodedHeader}.${encodedPayload}`, ZOOM_API_SECRET)); // エンコードしたヘッダーとペイロードをHMACで符号化
const encodedSignature = Utilities.base64Encode(signatureHMAC);
```
そして、すべてを合わせたものがJWT Tokenなので、最終的にはこうなります。

```javascript
const JWT_TOKEN = `${encodedHeader}.${encodedPayload}.${encodedSignature}`;
```
これで、JWT Tokenが作成できました！
##まとめ-GASでZOOMのJWT認証を行う方法
それでは最後に実際のコードを紹介します。関数の戻り値として、Tokenを受け取る形式になっています。

```javascript
function getZoomAccessToken() {
  const ZOOM_API_KEY = '**************'; // ご自身のAPI KEYを入力
  const ZOOM_API_SECRET = '******************'; // ご自身のAPI SECRETを入力

  const header = { alg: 'HS256', typ: 'JWT' };
  const payload = {
    iss: ZOOM_API_KEY,
    exp: Date.now() + 1800, // 時間設定はご自由に
  };

  // ヘッダーとペイロードをコード化
  const jsonHeader = JSON.stringify(header);
  const encodedHeader = Utilities.base64Encode(jsonHeader);
  const jsonPayload = JSON.stringify(payload);
  const encodedPayload = Utilities.base64Encode(jsonPayload);

  // 署名を符号化した後にコード化
  const signatureHMAC = Utilities.computeHmacSha256Signature(`${encodedHeader}.${encodedPayload}`, ZOOM_API_SECRET));
  const encodedSignature = Utilities.base64Encode(signatureHMAC);

  // 戻り値にTokenを設定
  return `${encodedHeader}.${encodedPayload}.${encodedSignature}`;
}
```
`API KEY`と`API SECRET`はこちらで確認してください！
![スクリーンショット (99)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108046/3c9b6a49-6566-1c28-fd6a-ebec2c8ba840.jpeg)

以上、参考になれば嬉しいです。ZOOMのAPIの使い方ってググってもほとんど出てこないですね。。。今まさに実装したい機能があるのにできずに困っています。。。

今後も初学者なりに、悪戦苦闘した結果を共有していきたいと思います！

ちなみに、[公式ドキュメントのJWT認証の解説](https://marketplace.zoom.us/docs/guides/auth/jwt)もまあまあ読み解きやすいので、余裕がある方は覗いてみてください～～～。