前提条件
- NodeJSとAstroがインストールされていること
- Agoraの開発者アカウント
プロジェクトのセットアップ
まず新しいAstroプロジェクトを作成します。
$ npm create astro@latest
セットアッププロセスでは、空のテンプレートを使う以外は、デフォルトのオプションに従います。 こうすることで、プロジェクトに不要なものを導入しないようにします。
次に、トークンを生成するためにAgora Tokenパッケージをインストールします。
$ npm i agora-token
トークンを生成するには、Agora App ID と App Certificate を使う必要があります。 アプリ証明書は "秘密"キーであり、公開されないようにする必要があります。 このデータは .envファイルに保存するのが一般的です。 Astro プロジェクトを生成するとき、.env は既に .gitignore にリストされています。 .env に、プロジェクトの APP_ID と APP_CERTIFICATE を追加します。
トークンとは
トークンは、ユーザがアプリケーションの一部にアクセスできるかどうかを検証する認証の一形態です。 この場合、トークンはビデオ通話内でユーザとその権限を認証します。 トークンは HMAC-SHA256 暗号アルゴリズムに基づいて生成されます。これは認証のための業界標準であり、ユーザーの身元を確認するコードを与えてくれます。 このトークンはagora-tokenパッケージを使ってNodeで生成できます。
エンドポイントの作成
Astro でエンドポイントを作成するには、Javascript または Typescript ファイルを pages ディレクトリに追加します。 ファイル名には、データ型の拡張子を付けます。 つまり、ファイル名は <name>.<return-type>.ts のようになります。 JavascriptとTypescriptの拡張子は、ビルド処理中に削除されます。
トークンの生成に使用するファイル・パスは、pages/api/token.json.tsです。 このエンドポイントは JSON データを返すので、ファイル名は .json.ts で終わります。 すべてのエンドポイントを api フォルダ内に置き、フロントエンドのルートから分離することを推奨しています。 Typescript 拡張は削除され、pages ディレクトリは URL に表示されないので、最終的なエンドポイントは api/token.json になります。
エンドポイントの定義
エンドポイントには入力情報が必要なので、リクエストボディとして入力を渡せるように POST リクエストを使用します。 Astroでは、POST関数をエクスポートしてResponseオブジェクトを返すことで定義できます。
import type { APIContext } from "astro";
export async function POST({ request }: APIContext) {
const { channel, role, uid, expireTime } = await request.json()
return new Response(JSON.stringify({
token: "token"
}), { status: 200 })
}
レスポンスヘルパー関数
utils/sendResponse.tsファイルを作成することで、Responseオブジェクトを抽象化し、エンドポイントを簡素化し、レスポンス間の統一性を確保します。
このファイル内で、バックエンドを使用する際にCORSの問題を回避するために、ヘッダーを設定する必要があります。
- 「"Access-Control-Allow-Origin": "*"」 すべてのドメインがサーバーにアクセスできるようにするもので、APIや開発中に役立ちます。
- 「"Access-Control-Allow-Methods": "POST, OPTIONS "」POSTメソッドとOPTIONSメソッドのみが許可されることを指定し、他のサイトがサーバとどのようにやり取りできるかを制御します。
これらのヘッダは、すべてのドメインを許可することで、APIが本番環境と開発環境でうまく動作することを可能にします。 本番環境では、許可するオリジンのみを制限する必要があります。
次に、定義したヘッダーを使用してレスポンスを作成する関数を2つ定義します。
- sendBadRequest - リクエストが不正であった理由とともに400のステータスを返す
- sendSuccessfulResponse - データとともに200のステータスを返す
const headers = new Headers({
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, OPTIONS",
});
export const sendBadRequest = (reason: string) => {
return new Response(reason, { status: 400, headers });
}
export const sendSuccessfulResponse = (data: any) => {
return new Response(JSON.stringify(data), { status: 200, headers });
}
認証
トークン生成機能には、channel、role、uid、expireTimeが必要です。 これらは、トークンに正しい認証と権限が含まれていることを確認するために使用されます。 正確なデータを渡すために、channel、uid、expireTimeが空でないことを確認してください。 もし空だった場合は、ヘルパー関数を使用して Bad Request を返します。
ロールは、トークンを要求するユーザがどのようなアクセス権限を持つかを決定します。
- publisher - チャネルにデータを公開できる。
- subscriber - データを受信するだけで、データを公開することはできない。
ロールに有効な入力はこの2つだけです。 これらの入力の一つがリクエストボディにある場合、agoraRole 変数を agora-token パッケージ内で定義済みの enum に代入することができます。 リクエストにこれらのオプションが含まれていない場合、ヘルパー関数を使って Bad Request を返します。
import agoraToken from "agora-token";
export async function POST({ request }: APIContext) {
const { channel, role, uid, expireTime } = await request.json()
if (!channel) {
return sendBadRequest("channel is required")
}
if (!uid) {
return sendBadRequest("uid is required")
}
if (!expireTime) {
return sendBadRequest("expireTime is required")
}
let agoraRole;
if (role === 'publisher') {
agoraRole = agoraToken.RtcRole.PUBLISHER;
} else if (role === 'subscriber') {
agoraRole = agoraToken.RtcRole.SUBSCRIBER
} else {
return sendBadRequest("role is incorrect")
}
return new Response(JSON.stringify({
token: "token"
}), { status: 200 })
}
トークン生成
トークンを生成するために、handleGenerateToken関数を別に作成します。 これにより、POSTリクエストとResponseロジックがトークン生成ロジックから分離されます。
これにより、次の2つの利点が得られます。
- コードが読みやすくなる
- 同じバックエンドにさらに機能を追加する場合(クラウド録画など)、ネットワークコールをする必要がなくなり、トークンを直接生成できるようになる
トークンを生成するには、agora-token パッケージの buildTokenWithUid 関数を使用します。 トークンと権限の有効期限フィールドに expireTime を渡します。
権限の有効期限は、ユーザがチャネル内でどれだけの期間権限を保持できるかを定義し、トークンの有効期限は、ユーザがチャネルにどれだけの期間留まることができるかを定義します。 権限の有効期限が切れた場合、ユーザーは通話を聞くことはできるが、配信することはできなくなります。 トークンの有効期限が切れると、チャネルへのアクセスは完全に失われます。 この例では、トークンと権限の有効期限を同じ期間に設定します。
トークンが生成されたら、ヘルパー関数を使って成功レスポンスを送信します。
const APP_ID = import.meta.env.APP_ID;
const APP_CERTIFICATE = import.meta.env.APP_CERTIFICATE;
export async function POST({ request }: APIContext) {
// ...
const token = await handleGenerateToken({ channel, role: agoraRole, uid, expireTime })
return sendSuccessfulResponse({ token })
}
export async function handleGenerateToken({ channel, role, uid, expireTime }: { channel: string, role: number, uid: string, expireTime: number }) {
return agoraToken.RtcTokenBuilder.buildTokenWithUid(APP_ID, APP_CERTIFICATE, channel, uid, role, expireTime, expireTime);
}
バックエンドの実行
バックエンドを実行します。
$ npm run dev
cURL を使って、channel、role、uid、expireTime をボディに含む POST リクエストをターミナルに送ることができます。
curl -X POST http://localhost:4321/api/token.json \
-H "Content-Type: application/json" \
-d '{
"uid": "20",
"channel": "test",
"role": "publisher",
"expireTime": "3600"
}'
POSTリクエストに情報が欠けている場合、どの情報が欠けているかを示すBad Requestレスポンスが返されます。 必要な情報をすべて送信すると、次のような正常な応答が返されます。
{"token":"<a long string of letters and numbers>"}
これで、アプリケーションの開発に使用できる完全に動作するトークン生成システムが完成しました。 このトークン生成システムは、主に開発目的での使用を想定しています。 本番環境で構築する場合は、ユーザー認証やその他のセキュリティ対策を追加して、通話に参加するのは本来の参加者だけであることを確認する必要があります。
Astroはコンテンツが多いサイトをサポートするために作られましたが、バックエンドのプロジェクトでも同じように機能します。 Astroは、フルスタックウェブサイトのための優れたソリューションです。
Agoraの開発者アカウントを作る(無料トライアル)
Agoraの無料トライアルは利用時間10,000分/月で、開発期間中はずっと無料となっています。(リリース後から料金が発生)Agoraは株式会社ブイキューブが日本代理店となっており、料金など不明点は日本語での対応が可能です。
Agoraの開発者アカウントを作る>
Agoraの導入について、お気軽にお問い合わせ下さい。