Autorisierung und Integrität

Beim Erstellen einer öffentlich zugänglichen Anwendung ist es äußerst wichtig, die in Ihrem System gespeicherten Daten zu schützen. Bei LLMs ist besondere Sorgfalt erforderlich, um sicherzustellen, dass das Modell nur auf die Daten zugreift, auf die es zugreifen sollte, dass Toolaufrufe richtig auf den Nutzer beschränkt sind, der das LLM aufruft, und dass der Ablauf nur von geprüften Clientanwendungen aufgerufen wird.

Firebase Genkit bietet Mechanismen zum Verwalten von Autorisierungsrichtlinien und ‑kontexten. Für Abläufe, die in Cloud Functions for Firebase ausgeführt werden, müssen Entwickler eine Autorisierungsrichtlinie angeben oder andernfalls ausdrücklich bestätigen, dass keine vorhanden ist. Bei anderen Abläufen kann die Authentifizierung ebenfalls verwaltet und festgelegt werden, erfordert aber etwas mehr manuelle Integration.

Autorisierung für einfachen Ablauf

Für alle Abläufe kann in der Konfiguration eine authPolicy definiert werden. Eine Authentifizierungsrichtlinie ist eine Funktion, die prüft, ob bestimmte von Ihnen festgelegte Kriterien erfüllt sind, und eine Ausnahme auslöst, wenn ein Test fehlschlägt. Wenn dieses Feld festgelegt ist, wird es ausgeführt, bevor der Ablauf aufgerufen wird:

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

Wenn du diesen Ablauf ausführst, musst du ein Authentifizierungsobjekt mit withLocalAuthContext angeben, da sonst eine Fehlermeldung angezeigt wird:

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

Wenn du die Genkit-Entwicklungs-UI verwendest, kannst du das Auth-Objekt übergeben, indem du JSON auf dem Tab „Auth-JSON“ eingibst: {"uid": "abc-def"}.

Sie können den Authentifizierungskontext für den Workflow auch jederzeit innerhalb des Workflows abrufen, indem Sie getFlowAuth() aufrufen, auch in Funktionen, die vom Workflow aufgerufen werden:

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

Wenn Sie Abläufe mit den Genkit-Entwicklertools testen, können Sie dieses Authentifizierungsobjekt in der Benutzeroberfläche oder in der Befehlszeile mit dem Flag --auth angeben:

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

Cloud Functions for Firebase-Integration

Das Firebase-Plug-in bietet eine praktische Integration in Firebase Auth / Google Cloud Identity Platform sowie integrierte Firebase App Check-Unterstützung.

Autorisierung

Der vom Firebase-Plug-in bereitgestellte onFlow()-Wrapper funktioniert nativ mit den Cloud Functions for Firebase-Client-SDKs. Wenn Sie das SDK verwenden, wird der Firebase Auth-Header automatisch eingefügt, sofern Ihr App-Client auch das Firebase Auth SDK verwendet. Mit Firebase Auth können Sie Ihre mit onFlow() definierten Abläufe schützen:

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

Wenn du das Firebase Auth-Plug-in verwendest, wird user als DecodedIdToken zurückgegeben. Sie können dieses Objekt jederzeit über getFlowAuth() abrufen, wie oben beschrieben. Wenn Sie diesen Ablauf während der Entwicklung ausführen, übergeben Sie das Nutzerobjekt auf dieselbe Weise:

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

Standardmäßig muss der Auth-Header vom Firebase Auth-Plug-in vom Client gesendet werden. Wenn Sie jedoch nicht authentifizierten Zugriff mit einer speziellen Verarbeitung für authentifizierte Nutzer zulassen möchten (z. B. Upselling-Funktionen), können Sie die Richtlinie so konfigurieren:

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

Wenn Sie eine Cloud-Funktion für das Internet freigeben, ist es äußerst wichtig, dass Sie einen Autorisierungsmechanismus verwenden, um Ihre Daten und die Daten Ihrer Kunden zu schützen. Es gibt jedoch Fälle, in denen Sie eine Cloud-Funktion ohne codebasierte Autorisierungsüberprüfungen bereitstellen müssen. Beispielsweise ist Ihre Funktion nicht öffentlich aufrufbar, sondern wird durch Cloud IAM geschützt. Das Feld authPolicy ist bei der Verwendung von onFlow() immer erforderlich. Sie können der Bibliothek jedoch mithilfe der Funktion noAuth() mitteilen, dass Sie auf Autorisierungsüberprüfungen verzichten:

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

Clientintegrität

Die Authentifizierung allein trägt bereits wesentlich zum Schutz Ihrer App bei. Es ist aber auch wichtig, dafür zu sorgen, dass nur Ihre Client-Apps Ihre Funktionen aufrufen. Das Firebase-Plug-in für Genkit bietet erstklassigen Support für Firebase App Check. Fügen Sie einfach die folgenden Konfigurationsoptionen zu onFlow() hinzu:

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

Nicht Firebase-HTTP-Autorisierung

Wenn Sie Workflows in einem Serverkontext außerhalb von Cloud Functions for Firebase bereitstellen, sollten Sie neben den nativen Workflows auch eigene Autorisierungsüberprüfungen einrichten können. Es stehen zwei Optionen zur Verfügung:

  1. Verwende das gewünschte Server-Framework und leite den Authentifizierungskontext wie oben beschrieben über den Ablaufaufruf weiter.

  2. Verwenden Sie die integrierte startFlowsServer() und geben Sie die Express-Middleware in der Ablaufkonfiguration an:

    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
    

    Weitere Informationen zur Verwendung von Express finden Sie in der Anleitung für Cloud Run.

Hinweis: Wenn Sie (1) auswählen, wird die Konfigurationsoption middleware ignoriert, wenn der Ablauf direkt aufgerufen wird.