Ao criar qualquer aplicativo voltado para o público, é extremamente importante proteger os dados armazenados em seu sistema. Quando se trata de LLMs, é necessário ter cuidado extra para garantir que o modelo acesse apenas os dados necessários, que as chamadas de ferramentas tenham o escopo correto para o usuário que invoca o LLM e que o fluxo seja invocado apenas por aplicativos clientes verificados.
O Firebase Genkit tem mecanismos para gerenciar contextos e políticas de autorização. Para fluxos em execução no Cloud Functions para Firebase, os desenvolvedores precisam fornecer uma política de autenticação ou reconhecer explicitamente a falta dela. Para fluxos não do Functions, a autenticação também pode ser gerenciada e configurada, mas requer um pouco mais de integração manual.
Autorização básica de fluxo
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, você precisa fornecer um objeto de autenticação usando withLocalAuthContext
. Caso contrário, você
vai 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 desenvolvimento do Genkit, você pode transmitir o objeto Auth
inserindo JSON na guia "JSON de autenticação": {"uid": "abc-def"}
.
Também é possível recuperar o contexto de autenticação do fluxo a qualquer momento
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 para desenvolvedores do Genkit, você pode especificar esse objeto de 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 com o Cloud Functions para Firebase
O plug-in do Firebase fornece uma integração conveniente com o Firebase Auth / Google Cloud Identity Platform, além de suporte integrado ao Firebase App Check.
Autorização
O wrapper onFlow()
fornecido pelo plug-in do Firebase funciona de maneira nativa com os
SDKs de cliente
do Cloud Functions para Firebase.
Ao usar o SDK, o cabeçalho do Firebase Auth será incluído automaticamente,
se o cliente do app também estiver usando o
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.
É possível recuperar esse objeto a qualquer momento usando getFlowAuth()
, conforme indicado
acima. Ao executar esse fluxo durante o desenvolvimento, você transmitiria o objeto do usuário
da mesma maneira:
genkit flow:run selfSummaryFlow '{"uid": "abc-def"}' --auth '{"admin": true}'
Por padrão, o plug-in do Firebase Auth exige que o cabeçalho de autenticação seja enviado pelo cliente, mas nos casos em que você quiser permitir o acesso não autenticado com processamento especial para usuários autenticados (como recursos de upsell), é possível configurar a política da seguinte maneira:
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 para uma Internet mais ampla, é de vital importância usar algum tipo de mecanismo de autorização para proteger os dados e os dados dos clientes. Dito isso, há momentos em que você precisa
implantar uma função do Cloud sem verificações de autorização baseadas em código (por exemplo,
sua função não é chamável globalmente, mas está protegida pelo
Cloud IAM). O
campo authPolicy
é sempre obrigatório ao usar onFlow()
, mas é possível
indicar à biblioteca que você está renunciando às verificações de autorização usando a
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 a proteger seu app. Mas também é
importante garantir que somente os apps clientes chamem as funções. O
plug-in do Firebase para genkit inclui suporte de primeira classe para o
Firebase App Check. Basta adicionar
as seguintes opções de configuração ao 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 Firebase
Ao implantar fluxos em um contexto de servidor fora do Cloud Functions para Firebase, é interessante ter uma maneira de configurar suas próprias verificações de autorização junto com os fluxos nativos. Você tem duas opções:
Use o framework de servidor que quiser e transmita o contexto de autenticação pelo
runFlow()
, conforme mencionado acima.Use o
startFlowsServer()
integrado e forneça o middleware Express na configuração de 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 como usar o Express, consulte as instruções do Cloud Run.
Se você escolher (1), a opção de configuração middleware
será
ignorada por runFlow()
.