認可と完全性

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

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

基本フロー承認

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

import { defineFlow, runFlow } from '@genkit-ai/flow';

export const selfSummaryFlow = 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) => { ... });

このフローを実行する際は、withLocalAuthContext を使用して auth オブジェクトを指定する必要があります。そうしないと、エラーが発生します。

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

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

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

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

また、フローによって呼び出される関数を含め、getFlowAuth() を呼び出すことによって、フロー内でいつでもフローの認証コンテキストを取得できます。

import { getFlowAuth, defineFlow } from '@genkit-ai/flow';

async function readDatabase(uid: string) {
  if (getFlowAuth().admin) {
    // Do something special if the user is an admin:
    ...
  } else {
    // Otherwise, use the `uid` variable to retrieve the relevant document
    ...
  }
}

export const selfSummaryFlow = 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 {firebaseAuth} from "@genkit-ai/firebase/auth";
import {onFlow} from "@genkit-ai/firebase/functions";

export const selfSummaryFlow = onFlow({
    name: "selfSummaryFlow",
    inputSchema: z.string(),
    outputSchema: z.string(),
    authPolicy: firebaseAuth((user) => {
      if (!user.email_verified && !user.admin) {
        throw new Error("Email not verified");
      }
    }),
  }, (subject) => {...})

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

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

デフォルトでは、Firebase Auth プラグインはクライアントから 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({
    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(),
  }, (subject) => {...})

クライアントの整合性

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

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

export const selfSummaryFlow = onFlow({
    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: ...,
  }, (subject) => {...})

Firebase 以外の HTTP 認証

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

  1. 任意のサーバー フレームワークを使用し、前述のように runFlow() を介して認証コンテキストを渡します。

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

    export const selfSummaryFlow = 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);
    
          // This is what will get passed to your authPolicy
          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) => { ... });
    
    startFlowsServer();  // This will register the middleware
    

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

(1)を選択した場合、middleware 構成オプションは runFlow() によって無視されることに注意してください。