授权和完整性

在构建任何面向公众的应用时,保护系统中存储的数据非常重要。对于 LLM,您需要格外小心,以确保模型仅访问应访问的数据,工具调用应正确限定为调用 LLM 的用户,并且只有经过验证的客户端应用才能调用该流程。

Firebase Genkit 提供了用于管理授权政策和上下文的机制。在 Firebase 上运行的流程可以使用身份验证政策回调(或辅助程序)。或者,Firebase 还会向该流程提供身份验证上下文,以便其执行自己的检查。对于非函数流程,您可以通过中间件管理和设置身份验证。

在流程内授权

流程可以通过两种方式检查授权:请求绑定(例如 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 Authentication)、context.app(Firebase App Check)和 context.instanceIdToken(Firebase Cloud Messaging)。手动调用流程时,您可以手动添加自己的身份验证上下文。

// 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 开发界面运行时,您可以在“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 开发者工具测试流程时,您可以在界面中指定此身份验证对象,也可以在命令行中使用 --context 标志指定此对象:

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

授权使用 Cloud Functions for Firebase

Cloud Functions for Firebase SDK 支持 Genkit,包括与 Firebase Auth / Google Cloud Identity Platform 集成,以及内置的 Firebase App Check 支持。

用户身份验证

Firebase Functions 库提供的 onCallGenkit() 封装容器内置了对 Cloud Functions for Firebase 客户端 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 会作为一个对象返回,其中包含一个 uid(用户 ID)和一个 tokenDecodedIdToken)。如前所述,您可以随时使用 ai.currentContext() 检索此对象。在开发期间运行此流程时,您可以通过相同的方式传递用户对象:

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

每当您向更广泛的互联网公开 Cloud Functions 函数时,都必须使用某种授权机制来保护您的数据和客户的数据,这一点至关重要。尽管如此,有时您需要部署不进行基于代码的授权检查的 Cloud Functions 函数(例如,您的函数不是可供所有人调用的,而是受 Cloud IAM 保护)。借助 Cloud Functions for Firebase,您可以使用 invoker 属性(用于控制 IAM 访问权限)来执行此操作。特殊值 'private' 会将函数保留为默认 IAM 设置,这意味着只有具有 Cloud Run Invoker 角色的调用方才能执行该函数。您可以改为提供应被授予调用此确切函数权限的用户或服务账号的电子邮件地址。

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),插件也可能会公开这些 ContextProvider。例如,@genkit-ai/firebase/context 插件会公开一个上下文提供程序,用于验证 Firebase Auth 凭据并将其填充到上下文中。

使用以下代码(可能会出现在各种应用中):

// 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 会提取 Web 框架,因此这些工具也可在 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 说明。