Авторизация и целостность

При создании любого общедоступного приложения чрезвычайно важно защитить данные, хранящиеся в вашей системе. Когда дело доходит до LLM, необходимо проявлять особую осмотрительность, чтобы гарантировать, что модель обращается только к тем данным, которые ей нужны, вызовы инструментов правильно привязаны к пользователю, вызывающему LLM, а поток вызывается только проверенными клиентскими приложениями.

Firebase Genkit предоставляет механизмы для управления политиками и контекстами авторизации. Для потоков, выполняемых в облачных функциях для Firebase, разработчики должны предоставить политику аутентификации или явно признать ее отсутствие. Для потоков, не связанных с функциями, аутентификацией также можно управлять и настраивать, но для этого требуется немного больше ручной интеграции.

Базовая авторизация потока

Все потоки могут определить authPolicy в своей конфигурации. Политика аутентификации — это функция, которая проверяет соответствие определенным критериям (определенным вами) и выдает исключение, если какой-либо тест не пройден. Если это поле установлено, оно выполняется до вызова потока:

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

При выполнении этого потока вы должны предоставить объект аутентификации, используя withLocalAuthContext , иначе вы получите сообщение об ошибке:

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

При работе с пользовательским интерфейсом разработки Genkit вы можете передать объект Auth, введя JSON на вкладке «Auth JSON»: {"uid": "abc-def"} .

Вы также можете получить контекст аутентификации для потока в любой момент внутри потока, вызвав getFlowAuth() , в том числе в функциях, вызываемых потоком:

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

При тестировании потоков с помощью инструментов разработки Genkit вы можете указать этот объект аутентификации в пользовательском интерфейсе или в командной строке с помощью флага --auth :

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

Облачные функции для интеграции с Firebase

Плагин Firebase обеспечивает удобную интеграцию с Firebase Auth/Google Cloud Identity Platform, а также встроенную поддержку проверки приложений Firebase.

Авторизация

Оболочка onFlow() предоставляемая плагином Firebase, изначально работает с облачными функциями для клиентских SDK Firebase. При использовании SDK заголовок Firebase Auth будет автоматически включен, если ваш клиент приложения также использует Firebase Auth SDK . Вы можете использовать Firebase Auth для защиты ваших потоков, определенных с помощью 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...
  }
);

При использовании плагина Firebase Auth user будет возвращен как DecodedIdToken . Вы всегда можете получить этот объект в любое время с помощью getFlowAuth() как указано выше. При запуске этого потока во время разработки вы должны передать объект пользователя таким же образом:

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

По умолчанию плагин Firebase Auth требует, чтобы заголовок аутентификации был отправлен клиентом, но в тех случаях, когда вы хотите разрешить неаутентифицированный доступ со специальной обработкой для аутентифицированных пользователей (например, функции дополнительной продажи), вы можете настроить политику следующим образом:

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

Всякий раз, когда вы предоставляете облачную функцию доступу в Интернет, крайне важно использовать какой-то механизм авторизации для защиты ваших данных и данных ваших клиентов. С учетом сказанного, бывают случаи, когда вам необходимо развернуть облачную функцию без проверок авторизации на основе кода (например, ваша функция не может вызываться по всему миру, но вместо этого защищена Cloud IAM ). Поле authPolicy всегда является обязательным при использовании onFlow() , но вы можете указать библиотеке, что отказываетесь от проверок авторизации, используя функцию 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...
  }
);

Целостность клиента

Аутентификация сама по себе имеет большое значение для защиты вашего приложения. Но также важно убедиться, что только ваши клиентские приложения вызывают ваши функции. Плагин Firebase для genkit включает первоклассную поддержку Firebase App Check . Просто добавьте следующие параметры конфигурации в 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...
  }
);

HTTP-авторизация, отличная от Firebase

При развертывании потоков в контексте сервера за пределами Cloud Functions for Firebase вам понадобится возможность настроить собственные проверки авторизации наряду с собственными потоками. У вас есть два варианта:

  1. Используйте любую серверную структуру, которая вам нравится, и передайте контекст аутентификации через вызов потока, как указано выше.

  2. Используйте встроенный startFlowsServer() и укажите промежуточное ПО Express в конфигурации потока:

    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
    

    Дополнительную информацию об использовании Express см. в инструкциях Cloud Run .

Обратите внимание: если вы выберете (1), опция конфигурации middleware будет игнорироваться при непосредственном вызове потока.