Quando crei un'applicazione rivolta al pubblico, è estremamente importante proteggere i dati memorizzati nel sistema. Per quanto riguarda i modelli LLM, è necessaria un'attenzione aggiuntiva per garantire che il modello acceda solo ai dati che deve, che le chiamate allo strumento siano opportunamente limitate all'utente che richiama il modello LLM e che il flusso venga richiamato solo da applicazioni client verificate.
Firebase Genkit fornisce meccanismi per la gestione dei criteri e dei contesti di autorizzazione. Per i flussi in esecuzione su Cloud Functions for Firebase, gli sviluppatori sono verplichti a fornire un criterio di autenticazione o a riconoscere esplicitamente la mancanza di uno. Per i flussi non Functions, l'autenticazione può essere gestita e impostata, ma richiede un'integrazione un po' più manuale.
Autorizzazione del flusso di base
Tutti i flussi possono definire un authPolicy
nella configurazione. Un criterio di autenticazione è una funzione che verifica se sono soddisfatti determinati criteri (definiti da te) e genera un'eccezione in caso di errore durante il test.
Se questo campo è impostato, viene eseguito prima dell'invocazione del flusso:
import { genkit, z } from 'genkit';
const ai = genkit({ ... });
export const selfSummaryFlow = ai.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) => {
// Flow logic here...
}
);
Quando esegui questo flusso, devi fornire un oggetto auth utilizzando withLocalAuthContext
, altrimenti verrà visualizzato un errore:
// Error: Authorization required.
await selfSummaryFlow({ uid: 'abc-def' });
// Error: You may only summarize your own profile data.
await selfSummaryFlow(
{ uid: 'abc-def' },
{
withLocalAuthContext: { uid: 'hij-klm' },
}
);
// Success
await selfSummaryFlow(
{ uid: 'abc-def' },
{
withLocalAuthContext: { uid: 'abc-def' },
}
);
Quando esegui l'app con l'interfaccia utente di sviluppo di Genkit, puoi passare l'oggetto Auth inserendo il JSON nella scheda "JSON Auth": {"uid": "abc-def"}
.
Puoi anche recuperare il contesto di autenticazione per il flusso in qualsiasi momento all'interno del flusso chiamando getFlowAuth()
, anche nelle funzioni richiamate dal flusso:
import { genkit, z } from 'genkit';
const ai = genkit({ ... });;
async function readDatabase(uid: string) {
const auth = ai.getAuthContext();
if (auth?.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);
}
);
Quando testi i flussi con gli strumenti per sviluppatori Genkit, puoi specificare questo oggetto auth
nell'interfaccia utente o nella riga di comando con il flag --auth
:
genkit flow:run selfSummaryFlow '{"uid": "abc-def"}' --auth '{"uid": "abc-def"}'
Integrazione di Cloud Functions for Firebase
Il plug-in Firebase offre un'integrazione comoda con Firebase Auth / Google Cloud Identity Platform, nonché il supporto integrato di Firebase App Check.
Autorizzazione
Il wrapper onFlow()
fornito dal plug-in Firebase funziona in modo nativo con gli SDK client di Cloud Functions for Firebase.
Quando utilizzi l'SDK, l'intestazione Firebase Auth verrà inclusa automaticamente se il client dell'app utilizza anche l'SDK Firebase Auth.
Puoi utilizzare Firebase Auth per proteggere i flussi definiti con onFlow()
:
import { genkit } from 'genkit';
import { firebaseAuth } from '@genkit-ai/firebase';
import { onFlow } from '@genkit-ai/firebase/functions';
const ai = genkit({ ... });;
export const selfSummaryFlow = onFlow(
ai,
{
name: 'selfSummaryFlow',
inputSchema: z.string(),
outputSchema: z.string(),
authPolicy: firebaseAuth((user) => {
if (!user.email_verified && !user.admin) {
throw new Error('Email not verified');
}
}),
},
async (input) => {
// Flow logic here...
}
);
Quando utilizzi il plug-in Firebase Auth, user
verrà restituito come
DecodedIdToken.
Puoi sempre recuperare questo oggetto in qualsiasi momento tramite getFlowAuth()
come indicato sopra. Quando esegui questo flusso durante lo sviluppo, devi passare l'oggetto utente nello stesso modo:
genkit flow:run selfSummaryFlow '{"uid": "abc-def"}' --auth '{"admin": true}'
Per impostazione predefinita, il plug-in Firebase Auth richiede che l'intestazione di autenticazione venga inviata dal client, ma nei casi in cui vuoi consentire l'accesso non autenticato con un'elaborazione speciale per gli utenti autenticati (ad esempio le funzionalità di upselling), puoi configurare il criterio nel seguente modo:
authPolicy: firebaseAuth((user) => {
if (user && !user.email_verified) {
throw new Error("Logged in users must have verified emails");
}
}, {required: false}),
Ogni volta che esponi una funzione cloud all'intera rete di internet, è di vitale importanza utilizzare una sorta di meccanismo di autorizzazione per proteggere i tuoi dati e quelli dei tuoi clienti. Detto questo, a volte è necessario eseguire il deployment di una funzione Cloud senza controlli di autorizzazione basati sul codice (ad esempio, la funzione non è richiamabile da tutti, ma è protetta da Cloud IAM). Il campo authPolicy
è sempre obbligatorio quando utilizzi onFlow()
, ma puoi indicare alla libreria che rinunci ai controlli di autorizzazione utilizzando la funzione noAuth()
:
import { onFlow, noAuth } from "@genkit-ai/firebase/functions";
export const selfSummaryFlow = onFlow(
ai,
{
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(),
},
async (input) => {
// Flow logic here...
}
);
Integrità del cliente
L'autenticazione da sola contribuisce notevolmente a proteggere la tua app. È anche importante assicurarsi che solo le tue app client chiamino le tue funzioni. Il plug-in Firebase per Genkit include il supporto di primo livello per Firebase App Check. Basta aggiungere
le seguenti opzioni di configurazione a onFlow()
:
import { onFlow } from "@genkit-ai/firebase/functions";
export const selfSummaryFlow = onFlow(
ai,
{
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: ...,
},
async (input) => {
// Flow logic here...
}
);
Autorizzazione HTTP non Firebase
Quando esegui il deployment dei flussi in un contesto del server esterno a Cloud Functions per Firebase, devi avere un modo per configurare i tuoi controlli di autorizzazione insieme ai flussi nativi. Sono disponibili due opzioni:
Utilizza il framework del server che preferisci e passa il contesto di autenticazione tramite la chiamata di flusso come indicato sopra.
Utilizza
startFlowsServer()
integrato e fornisci il middleware Express nella configurazione del flusso:import { genkit, z } from 'genkit'; const ai = genkit({ ... });; export const selfSummaryFlow = ai.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); // Pass auth information to the flow 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) => { // Flow logic here... } ); ai.startFlowServer({ flows: [selfSummaryFlow], }); // Registers the middleware
Per ulteriori informazioni sull'utilizzo di Express, consulta le istruzioni di Cloud Run.
Tieni presente che, se scegli l'opzione (1), l'opzione di configurazione middleware
verrà ignorata quando il flusso viene invocato direttamente.