AI Connect/Integration/マルチテナントパターン

マルチテナントパターン

VinkiusについてAIに質問

アプリケーションスコープのクライアントを 1 つにし、リクエスト入力の前にスコープを解決し、認証済みユーザーに対してのみケイパビリティを返し、テスト可能な分離境界を築きます。

1 つの Vinkius クライアントがアプリケーションの全ユーザーに対応します。分離はクライアントのインスタンスではなく、externalId の導き方から生まれます。このページは、SDK をマルチテナントのバックエンドに組み込むためのセキュリティチェックリストです。

Vinkius は実際のユーザーを一切知りません。 プラットフォームが知っているのは、あなたの Application とバックエンドが渡す不透明な識別子だけです。メールアドレス、名前、プロフィールはあなたのシステムの中にとどまり、Vinkius が見るのはコードが解決したユーザースコープだけです。

アプリケーションスコープのクライアントを 1 つ作成する

typescript
// lib/vinkius.ts — server only
import { Vinkius } from '@vinkius/connect';

export const vinkius = new Vinkius({
  appId: process.env.VINKIUS_APP_ID!,
  apiKey: process.env.VINKIUS_APP_KEY!,
});

クライアントはアプリケーションの認証情報を保持し、ユーザーごとの状態は保持しません。ユーザーごとにクライアントを作成してはいけません。ユーザーのスコープはルートを通じて運ばれます。

リクエスト入力を読む前にスコープを解決する

externalId を最初に認証済みセッションに結び付けます。認可の境界はセッション参照です。App ID とキーが認可するのはアプリケーションであり、ブラウザのユーザーではありません:

typescript
async function handleCapabilities(request: Request) {
  const session = await requireSession(request); // your auth code
  const capabilities = await vinkius.user(session.userId).capabilities();
  // ...
}

認証済みユーザーのケイパビリティのみを返す

クライアントが必要とするフィールドだけを射影し、それ以外は返しません。ケイパビリティオブジェクトは、セッションで解決されたユーザーに既にスコープされています:

typescript
return Response.json(
  capabilities.map(({ name, description, inputSchema }) => ({
    name,
    description,
    inputSchema,
  })),
);

コネクタ設定は別途認可する

認証情報の書き込みは特権的な操作です。connect()credentials.set() を呼び出す前に、そのセッションがそのユーザーのコネクタを管理する権限を持つことを確認してください:

typescript
const github = vinkius.user(session.userId).connector('github');
await github.connect();
await github.credentials.set({ GITHUB_TOKEN: submittedToken });

同じスコープで読み込んだケイパビリティのみを実行する

ケイパビリティはそれを生成した接続ルートを保持しています。クライアントから受け取ったケイパビリティ名を、別のスコープで解決されたハンドルに対して実行してはいけません。認証済みリクエストの中でケイパビリティセットを読み込み、そこからディスパッチしてください:

typescript
const capabilities = await vinkius.user(session.userId).capabilities();
const capability = capabilities.findCapability(requestedName);
if (!capability) {
  return Response.json({ error: 'not available for this user' }, { status: 404 });
}
const result = await capability.execute(args, { idempotencyKey });

メタデータは機密性のないアプリケーションコンテキストにのみ使用する

user.ensure(metadata) はユーザーの upsert を行い、プラン階層やラベルなどのアプリケーションコンテキストを保存します。認証情報の保管場所ではありません。シークレットは書き込み専用のコネクタ認証情報に保管してください。

分離境界をテストする

同じコネクタに接続した 2 人のユーザーが、互いの状態を見てはなりません。最小限のテスト形:

typescript
const alice = vinkius.user('alice_123');
const bob = vinkius.user('bob_456');

await alice.connector('github').connect();
const bobConnectors = await bob.connectors();

// bob's list must not contain alice's connection

このテストは、専用の App ID とキーを持つステージング環境に対して実行してください。環境の分離ルールについては Authentication and scope を参照してください。

次のステップ