Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

lcobucci/jwt 4系でZoom API用トークン作成 (Claimの日付系フォーマットを変える)

[PHP] Zoom APIを利用してミーティングを作成するで、Zoom APIを利用してミーティングを作成する方法が詳しく記載されていますが、lcobucci/jwtの4系ではZoom API用のJWTトークンの作成方法が変わっています。
4系でのトークン作成について簡単に説明します。

TL;DR

lcobucci/jwt4系は、デフォルトのClaimFormatterが日付系をマイクロ秒ありUnixタイムスタンプにフォーマットするので、Zoom APIが401を返してくる。日付をマイクロ秒なしUNIXタイムスタンプにフォーマットしてトークン作成することで正常にZoom APIにアクセスできる。

lobucci/jwtインストール

composer require lobucci/jwt

トークン作成

Configurationインスタンスを作る

3系ではBuilderを使ってトークン作成していましたが、4系ではまずConfigurationインスタンスを作成します。

use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;

$apiSecret = '[ZOOM API SECRET]';
$configuration = Configuration::forSymmetricSigner(
            new Sha256(), // 暗号化ハッシュ関数
            InMemory::plainText($apiSecret) // キー
        );

Zoomは、HMAC SHA256を利用しているので、ハッシュ関数には Lcobucci\JWT\Signer\Hmac\Sha256を、キーはZoom API管理画面から平文で取得できるのでLcobucci\JWT\Signer\Key\InMemory::plainText()でセットします。

Builderからトークン作成、そして失敗

トークン作成用のBuilderは$configuration->builder()で取得できるので、これを利用します。

※このコードでは動作しません!!!

$apiKey = '[ZOOM API KEY]';
$now = new \DateTimeImmutable();
$token = $configuration->builder()
    ->issuedBy($apiKey)
    ->issuedAt($now)
    ->expiresAt($now->modify('+1 hour'))
    ->getToken($configuration->signer(), $configuration->signingKey());

作成日時や有効期限といった日付系はUNIXタイムからDateTimeImmutable型に変更になっています。
Builderのそれぞれのメソッドに渡す値につぃては以下の通りです。

メソッド名 引数
issuedBy APIキー
issuedAt トークン作成日
expiredAt 有効期限

値をセットして、getToken($configuration->signer(), $configuration->signingKey())でトークンを作成しますが、このトークンではZoom APIにアクセスしても401 Unauthorizedが返ってきます。

作成したトークンでAPIにアクセスできない原因は日付系フォーマット

4系のBuilderで生成されたトークンのClaim部分とZoom APIの管理画面から生成した値を比べてみました。

Zoom管理画面から生成したClaim部分
{
  "aud": null,
  "iss": "APIキー",
  "exp": 1610972172,
  "iat": 1610966772
}
4系lcobucci/jwtで生成したClaim部分
{
  "iss": "APIキー",
  "iat": "1611019091.181495",
  "exp": "1611022691.181495"
}

4系では、日付系のフォーマットにマイクロ秒が付随しているのに対し、Zoom管理画面から生成したものにはマイクロ秒が付随していません。(audはなくても問題なし)

lcobucci/jwtのコードを追ってみると...

ChainedFormatter.php
public static function default(): self
    {
        return new self(new UnifyAudience(), new MicrosecondBasedDateConversion()); // <- ここ
    }
MicrosecondBasedDateConversion
    private function convertDate(DateTimeImmutable $date)
    {
        $seconds      = $date->format('U');
        $microseconds = $date->format('u');

        if ((int) $microseconds === 0) {
            return (int) $seconds;
        }

        return $seconds . '.' . $microseconds; // <- ここ
    }

というようになっています。lcobucci/jwtではLcobucci\JWT\ClaimsFormatteインターフェイスを実装したフォーマッタークラスがあり、これを利用してClaimをフォーマットしていますが、デフォルトでは日付はマイクロ秒つきのUnixタイムスタンプになるフォーマッターが使われてしまいます。これが原因でした。

日付をマイクロ秒なしUNIXタイムスタンプにフォーマットしてトークン作成する

マイクロ秒なしUNIXタイムスタンプ用のフォーマッターを作る

まず、マイクロ秒なしにするためには独自のフォーマッターを作ります。

UnixTimestampFormatter.php
<?php

use Lcobucci\JWT\ClaimsFormatter;
use Lcobucci\JWT\Token\RegisteredClaims;

class UnixTimestampFormatter implements ClaimsFormatter
{
    /** @inheritdoc  */
    public function formatClaims(array $claims): array
    {
        foreach (RegisteredClaims::DATE_CLAIMS as $claim) {
            if (! array_key_exists($claim, $claims)) {
                continue;
            }

            assert($claims[$claim] instanceof \DateTimeImmutable);
            $claims[$claim] = $claims[$claim]->getTimestamp();
        }

        return $claims;
    }
}

独自フォーマッターを使ってトークンを作成する

作成したフォーマッターは$configuration->builder()に引数で渡せます。独自フォーマッターを渡せば、あとは前述の通りでZoom APIにアクセスすることができるトークンを作成できます。Configurationインスタンス作成を含めた、一連のコードは以下の通りです。

use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;
use UnixTimestampFormatter;

$apiKey = '[ZOOM API KEY]';
$apiSecret = '[ZOOM API SECRET]';
$now = new \DateTimeImmutable();

// Configurationインスタンス作成
$configuration = Configuration::forSymmetricSigner(
            new Sha256(), // 暗号化ハッシュ関数
            InMemory::plainText($apiSecret) // キー
        );

// トークン作成
$token = $configuration->builder(new UnixTimestampFormatter())
    ->issuedBy($apiKey)
    ->issuedAt($now)
    ->expiresAt($now->modify('+1 hour'))
    ->getToken($configuration->signer(), $configuration->signingKey());

トークン作成後は、[PHP] Zoom APIを利用してミーティングを作成すると同様です。

ippey_s
大阪・枚方のフリーランスWebデベロッパー。主にSymfonyやLaravelなどのPHPフレームワークを使った開発やCraftCMSやWordPressなどのCMSを使ったサイト制作をしています。
https://unplat.info
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away