メインコンテンツまでスキップ

認証と呼び出し

ワーカープラグインでアクセストークンを検証する方法と、プラグインからワーカープラグインを呼び出す方法を説明します。

検証はワーカープラグインの責務​

ディスパッチャはアクセストークンを検証しません。 Authorizationヘッダーはそのままワーカープラグインに渡されます。 トークンを持たないリクエストも転送されます。

検証を省略したルートは、URLを知っている誰からでも呼び出せます。 利用者に属するデータを読み書きするルートでは、必ずトークンを検証してください。

ルームにはログインせずに参加できるため、ワーカープラグインから見た呼び出し元は次の3種類です。

呼び出し元見分け方
未ログイントークンがない
匿名の参加者トークンはあるがsubクレームがない
ログイン済みの利用者subクレームがある

どの呼び出し元を許可するかは、ワーカープラグインが決めます。

トークンを検証する​

simple-api-with-token-verificationテンプレートは、joseを使って次の順に検証します。

  1. Authorization: Bearerヘッダーからトークンを取り出す
  2. PLUGIN_TOKEN_JWKS_URLから取得した公開鍵で署名を検証する
  3. issクレームがPLUGIN_TOKEN_ISSUERと一致することを確認する
  4. expクレームで有効期限を確認する
  5. subクレームから利用者を特定する

PLUGIN_TOKEN_ISSUERとPLUGIN_TOKEN_JWKS_URLはプラットフォームが注入します。 詳しくはZIPと設定を参照してください。

警告

署名の検証だけでは不十分です。 issを確認しないと、別の認証基盤が発行したトークンも受け付けてしまいます。

テンプレートの認証ミドルウェアは、ルートごとに適用します。

import { authenticated } from './middleware/authenticated'

app.use('/items/*', authenticated)

app.get('/items', (ctx) => {
const { sub, claims } = ctx.get('identity')
// ...
})

ミドルウェアを適用していないルートは、誰でも呼び出せます。 新しいルートを追加したときは、ミドルウェアの適用を忘れないでください。

テンプレートは検証に失敗した場合、次のステータスを返します。

ステータス理由状況
401missing bearer tokenトークンがない
401invalid token署名、iss、有効期限のいずれかが不正、またはexpがない
401token has no subsubクレームがない
503token verification unavailableJWKSを取得できず、トークンを判定できない

JWKSを取得できない失敗はサーバー側の問題で、再試行すると成功します。 クライアントに再ログインを促さないよう、401と区別して503を返しています。

データの境界はトークンから決める​

ルームなど、アクセスできるデータの範囲を決める値は、検証済みのトークンのクレームから読んでください。 パスやクエリなど、呼び出し元が指定した値を信用すると、他のルームのデータにアクセスされるおそれがあります。

備考

現在、アクセストークンにはルームを表すクレームが含まれていません。 ルームをクエリなどで受け取る場合、呼び出し元はルームを偽れます。 ルームごとのデータに機密情報を保存しないでください。

プラグインから呼び出す​

プラグインからは、ルームと同じオリジンの/ext/{組織ID}/以下を呼び出します。 ルームは組織のサブドメインで動作するため、ワーカープラグインと同じオリジンになります。

GET https://{組織ID}.metatell.app/ext/{組織ID}/posts
Authorization: Bearer {アクセストークン}
備考

現在、Plugin SDKには、組織ID、接続先、アクセストークンを取得するAPIがありません。 ルームが組織のサブドメイン({組織ID}.metatell.app)で開かれている場合は、ホスト名の先頭から組織IDを取得できます。 カスタムドメインで開かれている場合、ホスト名から組織IDは取得できません。組織IDをプラグインのコードに定数として埋め込むなど、別の方法で渡してください。 SDKにAPIが追加されたら、このページで案内します。

ワーカープラグインが登録されていない場合、リクエストは404(worker plugin is not provisioned)になります。 プラグインはこの場合もエラーで停止せず、機能を無効にするなどして動作を続けてください。

WebSocket​

ワーカープラグインはWebSocketを受け付けられます。 ブラウザのWebSocketはハンドシェイクでヘッダーを指定できないため、トークンはクエリのaccess_tokenで渡します。

const url = new URL(`/ext/${organizationId}/api/ws`, window.location.origin)
url.protocol = url.protocol.replace('http', 'ws')
url.searchParams.set('access_token', token)
const socket = new WebSocket(url)

ワーカープラグインでは、次の点に注意してください。

  • WebSocketのパスは/api/以下に置いてください。ZIPにassets/がある場合、/api/以外のパスはWorkerに届かず、接続できません。
  • トークンはAuthorizationヘッダーではなく、クエリのaccess_tokenから読みます。
  • WebSocketの接続はDurable Objectsで受けます。Workerからは、RPCではなくstub.fetch()でリクエストを転送します。
  • Durable ObjectsではHibernation API(ctx.acceptWebSocket())を使います。使わない場合は、アイドル中の接続にも課金が続きます。
  • トークンがURLに含まれるため、ログなどに残りやすくなります。URLやトークンをログに出力しないでください。
app.get('/api/ws', async (ctx) => {
// access_token クエリのトークンを検証する
const id = ctx.env.ROOM_STORE.idFromName(roomId)
return ctx.env.ROOM_STORE.get(id).fetch(ctx.req.raw)
})

ワーカープラグインを削除しても、確立済みのWebSocketの接続は切断されません。