【Laravel×ZOOM API】JSON Web Tokensを自分で作成して実装してみた（JWTトークン編）

# ZOOM API実装方法には種類が２つ（JWTとOAuth）
zoom APIを使った実装における認証方法にはJWTとOAuthが候補としてあがります。

OAuthを用いた認証の方が資格情報・操作権限も付与するのでかなり厳重な認証システムが必要となり実装工数が少し上がります。

またOAuthで実装したzoom APIはマーケットプレイスに出して公開することができます。

# JWT認証を選択した理由
今回、ZOOM APIを実装するにあたりJWT認証を選択した理由は以下３つです。
①実装したい要件がZOOMミーティング作成のみで比較的シンプルな内容だった
②実装工数を削減したかった
③ZOOMマーケットプレイスを通じて公開する必要はなかった

# 実装工数
1.5人日ぐらいでした。

**機能実装のみだけであればコピペで早く実装できたかと思いますが、JWTトークンの構成の仕組みを理解した上で実装したかったため時間が掛かってしまいました。**

またHttpをつかったリソースへのアクセスやHTTPプロトコルのコンテキストまで深掘りかなり脇道に外れつつも『仕組みを理解すること』に重点を置きながら進めていったので工数としてはそれなりになりました。
#実装内容
今回の記事ではZOOMアカウントの登録とAPIKEY生成はスコープ外であるため割愛させていただきます。。公式ドキュメントや他Qiita様にも掲載があるのでそちらをご参照いただければ幸いです。

※公式ドキュメントはこちら：[Zoom Developer Platform](https://marketplace.zoom.us/docs/guides)


```php:ZoomMakeController.php
<?php

namespace App\Http\Controllers;
use App\Http\Controllers\Controller;

class ZoomController extends Controller
{
    //⓪文字列をURL-Safe Base64でエンコード
    public static function urlsafe_base64_encode($str)
    {
       return str_replace(array('+', '/', '='), array('-', '_', ''), base64_encode($str));
    }

    //①ZOOM APIを使ったMeeting作成
     public function makeZoomMeeting()
     {
        //②リクエストURL、API KEY関連を設定
        $zoom_url = 'https://api.zoom.us/v2/users/メールアドレス　 or Zoom userID/meetings';
        $zoom_api_key = API Key;
        $zoom_api_secret = API Secret;

        //③JWTトークン作成
        $expiration = time() + 20;
        $header = self::urlsafe_base64_encode('{"alg":"HS256","typ":"JWT"}');
        $payload = self::urlsafe_base64_encode('{"iss":"' . $zoom_api_key . '","exp":"' . $expiration . '"}');
        $signature = self::urlsafe_base64_encode(hash_hmac('sha256', "$header.$payload", $zoom_api_secret, TRUE));
        $token = "$header.$payload.$signature";

        //④ポストする作成用の会議データを連想配列で生成
        $data_to_zoom_api = array(
         'type' => "1",
         'topic' => "会議室タイトル",
        );

        //⑤コンテキストのオプションを生成
        $options = array(
            'http' => array(
            'method'=> 'POST',
            'header'=> array(
                'Content-type: application/json',
                'Authorization: Bearer' . $token,
            ),
            'content' => json_encode($data_to_zoom_api)
            )
        );

        //⑥ストリームを使用してコンテキストを作成
        $context = stream_context_create($options);

        //⑦ZOOM APIにアクセスしてして結果を取得
        $json_result = file_get_contents($zoom_url, false, $context);

        //⑧JSON形式で返ってきた結果を配列で取得
        $json_result = json_decode($json_result, true);

        //⑨参加用URLを取得して表示
        $join_url = $json_result['join_url'];

        //⑩結果を表示
        echo $join_url;
    }
}
```

## ①ZOOM APIを使ったMeeting作成
まずはcontrollerを作成します。今回はAPIを使用してミーティング参加用のURLを表示するという非常にシンプルな機能です。実際は様々な内容（パスワード生成等や会議の日時指定）等があるかと思いますがこちらでは割愛いたします。
## ②リクエストURL、API KEY関連を設定
APIにリクエストを投げるためのURLとAPI KEYの設定を行います。URLには公式ドキュメントによるとuserIDとメールアドレスどちらでもいけるそうです。上記の例では簡略化のため直書きしていますが、実際はconfig()関数等を用いて直接書かないようにしました。
> "The user ID or email address of the user. For user-level apps, pass `me` as the value for userId.",

## ③JWTトークン作成
前置きがかなり長くなってしまいましたがここからが本題になります。
上述の通り、今回は認証方法をJWTを選択いたしました。JWTとはJSON Web Tokensの略称であり、異なる３つの要素、具体的には『headder・payload・signature』の3つのコンポーネントで構成されています。

```
ex：aaaaa.bbbbb.ccccc
```
上記の実装では３つのコンポーネントをそれぞれ作成、さらに※Base64でエンコードし連結させてJWTトークンを生成しています。

### Base64でエンコード：
データを64文字の文字（a～z、A～Z、0～9、+、/）と「=」で一定の規則に基づきデータを変換します。今回の例では上記のurlsafe_base64_encodeメソッドがこのエンコードを担います。

>     public static function urlsafe_base64_encode($str)
    {
       return str_replace(array('+', '/', '='), array('-', '_', ''), base64_encode($str));
    }

そもそも何故base64を用いるかというと、HTTPヘッダは特殊記号を使うことができないため、Base64にエンコードした文字列が使用されているそうです。


最終的に.（ドット）で連結させてJWTトークンを作成します。

```php
$token = "$header.$payload.$signature";
```

### header
ヘッダーには、署名アルゴリズムとトークンのタイプの仕様を記載します。電子署名がどういったアルゴリズムで作られているか伝えるのに利用されるようです。Zoom APIは署名の作成、検証に共通の鍵を利用します（HS256）を使用します。またtypトークンタイプ：JWTを指定します。

```php
$header = self::urlsafe_base64_encode('{"alg":"HS256","typ":"JWT"}');
```


### payload
ペイロードにはクレームと呼ばれる認証対象となる情報（ユーザーと必要なメタデータ等）が含まれます。また、発行には発行者（iss）と有効期限（exp）の記載が必要になります。ZoomAPIではissには取得したAPIキーを、有効期限（exp）は任意の値を指定します。

```php
///有効期限を記載
$expiration = time() + 20;

$payload = self::urlsafe_base64_encode('{"iss":"' . $zoom_api_key . '","exp":"' . $expiration . '"}');

```

### signature
トークンが改ざんされていないか検証するために利用されます。
ヘッダーとペイロードをエンコードし、HMACSHA256アルゴリズム内にAPIシークレットを含めて、パッケージ全体に安全に署名します。hash_hmac（）は引数で指定した秘密鍵を用いてハッシュ値を生成する関数になります。

```php
$signature = self::urlsafe_base64_encode(hash_hmac('sha256', "$header.$payload", $zoom_api_secret, TRUE));
```
# まとめ
今回初めてJWT認証を使ったAPIの実装を経験しました。プログラミング学習中の身であるためトークン生成の仕組み等を理解するのはかなり大変でしたがとても良い経験になったと思います。

実際はJWT認証のセキュリティ上の注意点（algの改竄）等も鑑みながら実装可否を判断する必要があると思いました。

今回はJWTをメインで記載いたしましたが次回は④以降のリクエスト処理について学んだことをまとめてみようと思います。

[【Laravel×ZOOM API】JSON Web Tokensを自分で作成して実装してみた（APIリクエスト編）](https://qiita.com/joe-main/items/2cc678640ecd0d6dd0d0)

# 参考させて頂いた内容
[Zoom Developer Platform](https://marketplace.zoom.us/docs/guides)
[JWT.io](https://jwt.io/)
[Zoom APIを試してみる JWT編](https://qiita.com/nanbuwks/items/ed74a76a0f294c0bf4ed)
[JWT(JSON Web Token)の「仕組み」と「注意点」](https://www.wakuwakubank.com/posts/523-it-jwt/)
[Base64とは](https://medium-company.com/base64/)
[共通鍵暗号方式とは](https://medium-company.com/%E5%85%B1%E9%80%9A%E9%8D%B5%E6%9A%97%E5%8F%B7%E6%96%B9%E5%BC%8F/)