授權與完整性

建構任何面向大眾的應用程式時,請務必保護系統中儲存的資料。在 LLM 方面,您必須特別小心,確保模型只會存取應有的資料、工具呼叫的範圍正確地指向叫用 LLM 的使用者,且只有經過驗證的用戶端應用程式會叫用流程。

Firebase Genkit 提供管理授權政策和情境的機制。在 Firebase 上執行的流程可以使用驗證政策回呼 (或輔助程式)。或者,Firebase 也會在流程中提供驗證內容,讓流程自行執行檢查。對於非 Functions 流程,您可以透過中介軟體管理及設定驗證。

在流程中授權

流程可以透過兩種方式檢查授權:要求繫結 (例如 Cloud Functions for Firebase 的 onCallGenkitexpress) 可以強制執行授權,或者這些架構可以將授權政策傳遞至流程本身,讓流程可以存取在流程中管理的授權資訊。

import { genkit, z, UserFacingError } from 'genkit';

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

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

在這種情況下,要求繫結會填入 context.auth。舉例來說,onCallGenkit 會自動填入 context.auth (Firebase 驗證)、context.app (Firebase App Check) 和 context.instanceIdToken (Firebase 雲端通訊)。手動呼叫流程時,您可以手動新增自己的驗證內容。

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

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

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

使用 Genkit 開發人員 UI 執行時,您可以在「Auth JSON」分頁中輸入 JSON 來傳遞 Auth 物件:{"uid": "abc-def"}

您也可以在流程中的任何時間點呼叫 ai.currentContext(),藉此擷取流程的授權結構定義,包括在流程所叫用的函式中:

import { genkit, z } from 'genkit';

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

async function readDatabase(uid: string) {
  const auth = ai.currentContext()?.auth;
  // Note: the shape of context.auth depends on the provider. onCallGenkit puts
  // claims information in auth.token
  if (auth?.token?.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 中指定此驗證物件,也可以在指令列中使用 --context 標記指定:

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

使用 Cloud Functions for Firebase 授權

Firebase SDK 專用的 Cloud Functions 支援 Genkit,包括與 Firebase 驗證 / Google Cloud Identity Platform 整合,以及內建的 Firebase App Check 支援。

使用者驗證

Firebase Functions 程式庫提供的 onCallGenkit() 包裝函式已內建支援 Firebase 專用 Cloud Functions 用戶端 SDK。使用這些 SDK 時,只要應用程式用戶端也使用 Firebase Auth SDK,系統就會自動納入 Firebase Auth 標頭。您可以使用 Firebase Auth 保護使用 onCallGenkit() 定義的流程:

import { genkit } from 'genkit';
import { onCallGenkit } from 'firebase-functions/https';

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

const selfSummaryFlow = ai.defineFlow({
  name: 'selfSummaryFlow',
  inputSchema: z.string(),
  outputSchema: z.string(),
}, async (input) => {
  // Flow logic here...
});

export const selfSummary = onCallGenkit({
  authPolicy: (auth) => auth?.token?.['email_verified'] && auth?.token?.['admin'],
}, selfSummaryFlow);

使用 onCallGenkit 時,context.auth 會以物件形式傳回,其中包含用於使用者 ID 的 uid,以及 DecodedIdTokentoken。如先前所述,您隨時可以使用 ai.currentContext() 擷取這個物件。在開發期間執行此流程時,您會以相同方式傳遞使用者物件:

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

每當您將 Cloud Function 公開給更廣大的網際網路時,請務必使用某種授權機制來保護您的資料和客戶資料。不過,有時您需要部署不含程式碼授權檢查的 Cloud 函式 (例如,您的函式不是公開可呼叫,而是由 Cloud IAM 保護)。您可以使用 invoker 屬性 (用於控管 IAM 存取權) 執行這項操作。特殊值 'private' 會將函式設為預設的 IAM 設定,也就是說只有具備 Cloud Run 叫用者角色的呼叫端才能執行該函式。您可以改為提供使用者或服務帳戶的電子郵件地址,以便授予呼叫此函式的精確權限。

import { onCallGenkit } from 'firebase-functions/https'

const selfSummaryFlow = ai.defineFlow({
  name: 'selfSummaryFlow',
  inputSchema: z.string(),
  outputSchema: z.string(),
}, async (input) => {
  // Flow logic here...
});

export const selfSummary = onCallGenkit({
  invoker: 'private',
}, selfSummaryFlow);

用戶端完整性

驗證功能本身就能有效保護應用程式,但您也必須確保只有用戶端應用程式才會呼叫您的函式。Genkit 的 Firebase 外掛程式包含 Firebase App Check 的一流支援功能。方法是將下列設定選項新增至 onCallGenkit()

import { onCallGenkit } from 'firebase-functions/https';

const selfSummaryFlow = ai.defineFlow({
  name: 'selfSummaryFlow',
  inputSchema: z.string(),
  outputSchema: z.string(),
}, async (input) => {
  // Flow logic here...
});

export const selfSummary = onCallGenkit({
  // 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: ...,
}, selfSummaryFlow);

非 Firebase HTTP 授權

將流程部署至 Cloud Functions for Firebase 以外的伺服器情境時,您需要設法在內建流程旁邊設定自己的授權檢查。

使用 ContextProvider 填入 auth 等內容值,並提供宣告式政策或政策回呼。Genkit SDK 提供 ContextProvider (例如 apiKey),外掛程式也可能會公開這些元素。舉例來說,@genkit-ai/firebase/context 外掛程式會公開內容提供者,用於驗證 Firebase 驗證憑證,並將憑證填入內容。

使用以下程式碼,這類程式碼可能會出現在各種應用程式中:

// Express app with a simple API key
import { genkit, z } from 'genkit';

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

export const selfSummaryFlow = ai.defineFlow(
  {
    name: 'selfSummaryFlow',
    inputSchema: z.object({ uid: z.string() }),
    outputSchema: z.string(),
  },
  async (input) => {
    // Flow logic here...
  }
);

您可以透過以下方式保護簡單的「流程伺服器」Express 應用程式:

import { apiKey } from "genkit";
import { startFlowServer, withContext } from "@genkit-ai/express";

startFlowServer({
  flows: [
    withContext(selfSummaryFlow, apiKey(process.env.REQUIRED_API_KEY))
  ],
});

或者,您也可以使用相同的工具建立自訂 Express 應用程式:

import { apiKey } from "genkit";
import * as express from "express";
import { expressHandler } from "@genkit-ai/express;

const app = express();
// Capture but don't validate the API key (or its absence)
app.post('/summary', expressHandler(selfSummaryFlow, { contextProvider: apiKey()}))

app.listen(process.env.PORT, () => {
  console.log(`Listening on port ${process.env.PORT}`);
})

ContextProvider 會抽離網路架構,因此這些工具也適用於 Next.js 等其他架構。以下是使用 Next.js 建構的 Firebase 應用程式範例。

import { appRoute } from "@genkit-ai/express";
import { firebaseContext } from "@genkit-ai/firebase";

export const POST = appRoute(selfSummaryFlow, { contextProvider: firebaseContext })

如要進一步瞭解如何使用 Express,請參閱 Cloud Run 操作說明。