SkyWay Beta用の認証JWT(SkyWay Auth Token)を生成するサーバーをGoogle Apps Scriptで動かす

はじめに

2月21日に、新しいSkyWayのβ版であるSkyWay Betaがリリースされました。
SkyWay Betaでは、JWT形式のトークン、SkyWay Auth Tokenを使用して、細かい権限管理ができるようになっています。
一方で、ドキュメントには

SkyWay Auth Tokenを利用するためには、これを払い出すアプリケーションサーバーを構築する必要があります。
(中略)
なお、サーバを構築せずにフロントエンドでSkyWay Auth Tokenを生成した場合、シークレットキーをエンドユーザーが取得できるため、誰でも任意の Channel や Room に入ることができる等のセキュリティ上の問題が発生します。

とあるように、SkyWay Auth Tokenを生成するサーバーを建てて、アプリケーションをセキュアにする必要があります。
beta.skyway.ntt.com

今回は、Google Apps Script(以下GAS)を使用して、手軽にSkyWay Auth Tokenを生成するサーバーを建てる方法を紹介します。

※以下の記事でも説明したように筆者はSkyWayの中の人ですが、本記事で紹介する方法はあくまでも一個人として試してみた記録です。
sublimer.hatenablog.com

環境など

  • Node.js v14.16.0
  • TypeScript 4.5.5
  • @skyway-sdk/token 0.2.0-beta.6

実装

今回作ったもののGitHubリポジトリはこちらです。
github.com

SkyWay Auth Tokenを生成するためのライブラリは、npmから以下のコマンドでインストールできます。

$ npm i @skyway-sdk/token

SkyWay Auth Tokenを生成するコードは以下のようになります。

import { SkyWayAuthToken } from '@skyway-sdk/token';

declare var global: any;

global.doGet = (e: GoogleAppsScript.Events.DoGet) => {
    const res = ContentService.createTextOutput();
    res.setMimeType(ContentService.MimeType.JSON);
    const roomName = e.parameter.roomName || '*';
    const memberName = e.parameter.memberName || '*';

    const ps = PropertiesService.getScriptProperties();
    const appId = ps.getProperty('SKYWAY_APP_ID');
    const secretKey = ps.getProperty('SKYWAY_SECRET_KEY');

    if (appId === null || secretKey === null || appId === undefined || secretKey === undefined || appId === '' || secretKey === '') {
        res.setContent(JSON.stringify({ error: 'appId or secretKey is empty.' }));
        return res;
    }

    const token = new SkyWayAuthToken({
        jti: Utilities.getUuid(),
        iat: Math.floor(Date.now() / 1000),
        exp: Math.floor(Date.now() / 1000) + 60 * 60,
        scope: {
            app: {
                id: appId,
                turn: true,
                actions: ['read'],
                channels: [
                    {
                        id: '*',
                        name: roomName,
                        actions: ['write'],
                        members: [
                            {
                                id: '*',
                                name: memberName,
                                actions: ['write'],
                                publication: {
                                    actions: ['write'],
                                },
                                subscription: {
                                    actions: ['write'],
                                },
                            },
                        ],
                        sfuBots: [
                            {
                                actions: ['write'],
                                forwardings: [
                                    {
                                        actions: ['write']
                                    }
                                ]
                            }
                        ]
                    },
                ],
            },
        },
    });
    const tokenString = token.encode(secretKey);

    const obj = {
        token: tokenString
    };
    res.setContent(JSON.stringify(obj));
    return res;
};

GETリクエストのクエリパラメーターに指定されたroomName、memberName用の有効期限1時間のSkyWay Auth Tokenを生成し、エンコード済みの文字列をレスポンスとして返します。
クエリパラメーターを指定しない場合は、任意のroomName、memberNameを表す * を設定したSkyWay Auth Tokenを生成します。

少しハマったポイントとしては、GASでUUIDの生成をする際は Utilities.getUuid() を使用する必要がある点です。
SkyWay Betaのドキュメントでは、@skyway-sdk/tokenがexportしているuuidV4()を使用していますが、これはGASでは動かないので、GASが提供しているメソッドを使用します。

SkyWay Auth Tokenの生成に必要なAppIdとSecretKeyは、GASのPropertiesServiceから取得しています。
Class PropertiesService  |  Apps Script  |  Google Developers
なお、新しいGASのエディタではプロパティーの設定ができなくなってしまったようなので、以下のようなスクリプトを初回に手動実行してプロパティーの設定を行っています。

function myFunction() {
  const sp=PropertiesService.getScriptProperties();
  sp.setProperty('SKYWAY_APP_ID','<AppId>');
  sp.setProperty('SKYWAY_SECRET_KEY','<SecretKey>');
}

ビルドとデプロイ

TypeScriptのビルドはWebpackで行います。
そのままビルドしてもGASでは動かないため、gas-webpack-pluginを使用します。
github.com
以下のようなwebpack.config.jsを書きビルドすると、 ./dist/main.js にGAS用のJSファイルが生成されます。
後はこれをGASにデプロイすれば、SkyWay Auth Tokenを生成するAPIサーバーとして動かすことができます。
今回のコードでは、claspを使用してコマンド一発でデプロイまで行うようにしています。
github.com
claspを使ってデプロイする場合、初回だけはWebからデプロイする必要があるようです。
あとは、 https://script.google.com/macros/s//exec?roomName=room&memberName=member のようなURLでGETリクエストを送ると、SkyWay Auth Tokenがレスポンスとして返されます。
注意点として、GASはリクエストが来るとリダイレクトが必ず行われるので、リダイレクト先にリクエストを送る必要があります。ブラウザのfetch APIであれば勝手にリダイレクトしてくれるので問題ないですが、例えばcurlでリクエストを送る際は -L オプションを付ける必要があります。

For security reasons, content returned by the Content service isn't served from script.google.com, but instead redirected to a one-time URL at script.googleusercontent.com.

developers.google.com

おわりに

今回はSkyWay Auth Tokenを生成するシンプルなサーバーをGASで動かしてみました。
実際にアプリケーションを作る場合は、リクエストの内容を元にアカウント認証を行う必要があります。
例えばFirebase Authenticationと連携させれば、容易にアカウントの認証機能を追加できます。

SkyWay Betaは提供中は無料で利用でき、これまでのSkyWayよりも高機能になっているので、これを活用して新しいWebアプリケーションを作ってみるのも面白そうです。