Autorização e integridade

Ao criar qualquer aplicativo voltado para o público, é extremamente importante proteger os dados armazenados no sistema. Quando se trata de LLMs, é necessário necessário para garantir que o modelo acesse apenas os dados que deveria, a ferramenta chama têm o escopo apropriado para o usuário que invoca o LLM e o fluxo está sendo invocado apenas por aplicativos clientes verificados.

O Firebase Genkit fornece mecanismos para gerenciar políticas de autorização e contextos de negócios diferentes. Nos fluxos em execução no Cloud Functions para Firebase, os desenvolvedores necessário fornecer uma política de autenticação ou confirmar explicitamente a falta de um. Para fluxos que não são do Functions, a autenticação também pode ser gerenciada e configurada, mas exige um pouco mais de integração manual.

Autorização de fluxo básica

Todos os fluxos podem definir um authPolicy na configuração. Uma política de autenticação é uma função que testa se determinados critérios (definidos por você) são atendidos e gera uma exceção se algum teste falhar. Se este campo for definido, ele será executado antes que o fluxo seja invocado:

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

Ao executar esse fluxo, é necessário fornecer um objeto de autenticação usando withLocalAuthContext. Caso contrário, receber um erro:

// 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' },
  }
);

Ao executar com a interface de usuário de desenvolvimento do Genkit, você pode passar o objeto Auth ao inserir o JSON no campo "Auth JSON" guia: {"uid": "abc-def"}.

Também é possível recuperar o contexto de autenticação do fluxo a qualquer momento dentro dele. chamando getFlowAuth(), inclusive em funções invocadas pelo fluxo:

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);
  });

Ao testar fluxos com as ferramentas de desenvolvimento do Genkit, você pode especificar essa autenticação na interface ou na linha de comando com a sinalização --auth:

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

Integração do Cloud Functions para Firebase

O plug-in do Firebase oferece uma integração conveniente com o Firebase Auth / Google Cloud Identity Platform, bem como suporte integrado ao Firebase App Check.

Autorização

O wrapper onFlow() fornecido pelo plug-in do Firebase funciona nativamente com o Cloud Functions para Firebase SDKs do cliente. Ao usar o SDK, o cabeçalho do Firebase Auth será incluído automaticamente como desde que o cliente do app também use SDK do Firebase Auth. É possível usar o Firebase Auth para proteger seus fluxos definidos com 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) => {...})

Ao usar o plug-in do Firebase Auth, user será retornado como um DeencodedIdToken (em inglês). Você pode recuperar esse objeto a qualquer momento usando getFlowAuth(), conforme indicado. acima. Ao executar esse fluxo durante o desenvolvimento, você passaria o objeto de usuário da mesma forma:

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

Por padrão, o plug-in Firebase Auth exige que o cabeçalho Auth seja enviado pelo cliente, mas nos casos em que você quiser permitir o acesso não autenticado com recursos para usuários autenticados (por exemplo, recursos avançados) configure a política assim:

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

Sempre que você expõe uma função do Cloud à Internet, ela é essencialmente é importante usar algum tipo de mecanismo de autorização para proteger seus dados e os dados dos seus clientes. Dito isso, há momentos em que você precisa para implantar uma função do Cloud sem verificações de autorização baseadas em código (por exemplo, a função não é chamável, mas é protegida por Cloud IAM). A O campo authPolicy sempre é obrigatório ao usar onFlow(), mas é possível indique à biblioteca que você está abrindo mão das verificações de autorização usando o Função 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) => {...})

Integridade do cliente

A autenticação, por si só, ajuda muito a proteger seu app. Mas também é importante garantir que apenas seus apps clientes chamem suas funções. A O plug-in do Firebase para genkit inclui suporte de primeira classe para Firebase App Check: Basta adicionar as seguintes opções de configuração para seu 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) => {...})

Autorização HTTP não relacionada ao Firebase

Ao implantar fluxos em um contexto de servidor fora do Cloud Functions para Firebase, convém ter uma maneira de configurar suas próprias verificações de autorização junto com os fluxos nativos. Você tem duas opções:

  1. Use o framework de servidor que quiser e transmita o contexto de autenticação pelo runFlow(), conforme mencionado acima.

  2. Use o startFlowsServer() integrado e forneça o middleware Express no configuração do fluxo:

    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
    

    Para mais informações sobre o uso do Express, acesse a página do Cloud Run instruções.

Se você usar (1), a opção de configuração middleware ser ignorado por runFlow().