認可と完全性

一般公開するアプリケーションを構築する場合は、システムに保存されているデータを保護することが非常に重要です。LLM の場合、モデルが必要なデータにのみアクセスし、ツール呼び出しが LLM を呼び出すユーザーに適切にスコープ設定され、フローが無効なクライアント アプリケーションによってのみ呼び出されるように、特別な注意が必要です。

Firebase Genkit には、承認ポリシーとコンテキストを管理するためのメカニズムが用意されています。Cloud Functions for Firebase で実行されるフローの場合、デベロッパーは認証ポリシーを指定するか、ポリシーがないことを明示的に認識する必要があります。Functions 以外のフローでも認証を管理して設定できますが、手動での統合が少し必要になります。

基本的なフロー認可

すべてのフローは、構成で authPolicy を定義できます。認証ポリシーは、(ユーザーが定義した)特定の条件が満たされているかどうかをテストし、テストが失敗した場合に例外をスローする関数です。このフィールドが設定されている場合、フロー呼び出しの前に実行されます。

import { genkit, z } from 'genkit';

const ai = genkit({ ... });

export const selfSummaryFlow = ai.defineFlow(
  {
    name: 'selfSummaryFlow',
    inputSchema: z.object({ uid: z.string() }),
    outputSchema: z.string(),
    authPolicy: (auth, input) => {
      if (!auth) {
        throw new Error('Authorization required.');
      }
      if (input.uid !== auth.uid) {
        throw new Error('You may only summarize your own profile data.');
      }
    },
  },
  async (input) => {
    // Flow logic here...
  }
);

このフローを実行するときに、withLocalAuthContext を使用して認証オブジェクトを指定する必要があります。指定しない場合、エラーが発生します。

// Error: Authorization required.
await selfSummaryFlow({ uid: 'abc-def' });

// Error: You may only summarize your own profile data.
await selfSummaryFlow(
  { uid: 'abc-def' },
  {
    withLocalAuthContext: { uid: 'hij-klm' },
  }
);

// Success
await selfSummaryFlow(
  { uid: 'abc-def' },
  {
    withLocalAuthContext: { uid: 'abc-def' },
  }
);

Genkit 開発 UI で実行する場合は、[Auth JSON] タブに JSON を入力して Auth オブジェクトを渡すことができます。{"uid": "abc-def"}

getFlowAuth() を呼び出すことで、フロー内の任意の時点でフロー認証コンテキストを取得することもできます。これは、フローによって呼び出される関数でも行えます。

import { genkit, z } from 'genkit';

const ai = genkit({ ... });;

async function readDatabase(uid: string) {
  const auth = ai.getAuthContext();
  if (auth?.admin) {
    // Do something special if the user is an admin
  } else {
    // Otherwise, use the `uid` variable to retrieve the relevant document
  }
}

export const selfSummaryFlow = ai.defineFlow(
  {
    name: 'selfSummaryFlow',
    inputSchema: z.object({ uid: z.string() }),
    outputSchema: z.string(),
    authPolicy: ...
  },
  async (input) => {
    await readDatabase(input.uid);
  }
);

Genkit デベロッパー ツールでフローテストを行う場合は、この認証オブジェクトを UI で指定するか、--auth フラグを使用してコマンドラインで指定できます。

genkit flow:run selfSummaryFlow '{"uid": "abc-def"}' --auth '{"uid": "abc-def"}'

Cloud Functions for Firebase との統合

Firebase プラグインを使用すると、Firebase Auth / Google Cloud Identity Platform との便利な統合と、Firebase App Check の組み込みサポートを利用できます。

承認

Firebase プラグインによって提供される onFlow() ラッパーは、Cloud Functions for Firebase のクライアント SDK とネイティブに連携します。SDK を使用する場合、アプリ クライアントも Firebase Auth SDK を使用している限り、Firebase Auth ヘッダーが自動的に含まれます。Firebase Auth を使用して、onFlow() で定義されたフローを保護できます。

import { genkit } from 'genkit';
import { firebaseAuth } from '@genkit-ai/firebase';
import { onFlow } from '@genkit-ai/firebase/functions';

const ai = genkit({ ... });;

export const selfSummaryFlow = onFlow(
  ai,
  {
    name: 'selfSummaryFlow',
    inputSchema: z.string(),
    outputSchema: z.string(),
    authPolicy: firebaseAuth((user) => {
      if (!user.email_verified && !user.admin) {
        throw new Error('Email not verified');
      }
    }),
  },
  async (input) => {
        // Flow logic here...
  }
);

Firebase Auth プラグインを使用する場合、userDecodedIdToken として返されます。このオブジェクトは、上記のように getFlowAuth() を使用していつでも取得できます。開発中にこのフローを実行する場合は、ユーザー オブジェクトを同じように渡します。

genkit flow:run selfSummaryFlow '{"uid": "abc-def"}' --auth '{"admin": true}'

デフォルトでは、Firebase Auth プラグインでは、クライアントから認証ヘッダーを送信する必要がありますが、認証済みユーザーに対して特別な処理(アップセル機能など)で未認証のアクセスを許可する場合は、ポリシーを次のように構成できます。

authPolicy: firebaseAuth((user) => {
  if (user && !user.email_verified) {
    throw new Error("Logged in users must have verified emails");
  }
}, {required: false}),

Cloud Functions をインターネットに公開する場合は、なんらかの認可メカニズムを使用して、自分のデータとユーザーのデータを保護することが不可欠です。ただし、コードベースの認可チェックなしで Cloud Functions の関数をデプロイする必要がある場合があります(関数が世界中で呼び出せるものではなく、Cloud IAM で保護されている場合など)。onFlow() を使用する場合は、authPolicy フィールドが常に必要ですが、noAuth() 関数を使用して、承認チェックをスキップすることをライブラリに指示できます。

import { onFlow, noAuth } from "@genkit-ai/firebase/functions";

export const selfSummaryFlow = onFlow(
  ai,
  {
    name: "selfSummaryFlow",
    inputSchema: z.string(),
    outputSchema: z.string(),
    // WARNING: Only do this if you have some other gatekeeping in place, like
    // Cloud IAM!
    authPolicy: noAuth(),
  },
  async (input) => {
        // Flow logic here...
  }
);

クライアントの整合性

認証だけでもアプリを保護できますが、クライアント アプリのみが関数を呼び出すようにすることも重要です。genkit 用の Firebase プラグインには、Firebase App Check のファーストクラス サポートが含まれています。次の構成オプションを onFlow() に追加するだけです。

import { onFlow } from "@genkit-ai/firebase/functions";

export const selfSummaryFlow = onFlow(
  ai,
  {
    name: "selfSummaryFlow",
    inputSchema: z.string(),
    outputSchema: z.string(),

    // These two fields for app check. The consumeAppCheckToken option is for
    // replay protection, and requires additional client configuration. See the
    // App Check docs.
    enforceAppCheck: true,
    consumeAppCheckToken: true,

    authPolicy: ...,
  },
  async (input) => {
        // Flow logic here...
  }
);

Firebase 以外の HTTP 認可

Cloud Functions for Firebase の外部のサーバー コンテキストにフローをデプロイする場合は、ネイティブ フローとともに独自の認可チェックを設定できる方法が必要です。次の 2 つのオプションがあります。

  1. 任意のサーバー フレームワークを使用し、上記のようにフロー呼び出しを介して認証コンテキストを渡します。

  2. 組み込みの startFlowsServer() を使用し、フロー構成で Express ミドルウェアを指定します。

    import { genkit, z } from 'genkit';
    
    const ai = genkit({ ... });;
    
    export const selfSummaryFlow = ai.defineFlow(
      {
        name: 'selfSummaryFlow',
        inputSchema: z.object({ uid: z.string() }),
        outputSchema: z.string(),
        middleware: [
          (req, res, next) => {
            const token = req.headers['authorization'];
            const user = yourVerificationLibrary(token);
    
            // Pass auth information to the flow
            req.auth = user;
            next();
          }
        ],
        authPolicy: (auth, input) => {
          if (!auth) {
            throw new Error('Authorization required.');
          }
          if (input.uid !== auth.uid) {
            throw new Error('You may only summarize your own profile data.');
          }
        }
      },
      async (input) => {
        // Flow logic here...
      }
    );
    
    ai.startFlowServer({
      flows: [selfSummaryFlow],
    });  // Registers the middleware
    

    Express の使用方法については、Cloud Run の手順をご覧ください。

(1)を選択した場合、フローから直接呼び出されると、middleware 構成オプションは無視されます。