권한 부여 및 무결성

공개용 애플리케이션을 빌드할 때는 시스템에 저장된 데이터를 보호하는 것이 매우 중요합니다. LLM의 경우 모델이 필요한 데이터에만 액세스하고, 도구 호출 범위가 LLM을 호출하는 사용자로 적절하게 지정되며, 확인된 클라이언트 애플리케이션에 의해서만 흐름이 호출되는지 확인하기 위해 더욱 주의를 기울여야 합니다.

Firebase Genkit는 승인 정책 및 컨텍스트 관리를 위한 메커니즘을 제공합니다. Firebase용 Cloud Functions에서 실행되는 흐름의 경우 개발자가 인증 정책을 제공하거나 인증 정책이 없음을 명시적으로 확인해야 합니다. 비 함수 흐름의 경우 인증도 관리하고 설정할 수 있지만 수동 통합이 좀 더 필요합니다.

기본 흐름 승인

모든 흐름은 구성에서 authPolicy를 정의할 수 있습니다. 인증 정책은 개발자가 정의한 특정 기준이 충족되는지 테스트하고 테스트가 실패하면 예외를 발생시키는 함수입니다. 이 필드가 설정되면 흐름이 호출되기 전에 실행됩니다.

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

이 흐름을 실행할 때 withLocalAuthContext를 사용하여 인증 객체를 제공해야 합니다. 그러지 않으면 오류가 발생합니다.

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

Genkit Development UI를 사용하여 실행할 때 'Auth JSON' 탭에 JSON({"uid": "abc-def"})을 입력하여 인증 객체를 전달할 수 있습니다.

흐름에서 호출된 함수를 포함하여 흐름 내에서 언제든지 getFlowAuth()를 호출하여 흐름의 인증 컨텍스트를 검색할 수도 있습니다.

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

Genkit 개발 도구로 흐름을 테스트할 때 UI 또는 명령줄에서 --auth 플래그를 사용하여 이 인증 객체를 지정할 수 있습니다.

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

Firebase용 Cloud Functions 통합

Firebase 플러그인은 Firebase 인증 / Google Cloud Identity Platform과 기본 제공되는 Firebase 앱 체크 지원과의 편리한 통합을 제공합니다.

승인

Firebase 플러그인에서 제공하는 onFlow() 래퍼는 기본적으로 Firebase용 Cloud Functions 클라이언트 SDK와 함께 작동합니다. SDK를 사용하는 경우 앱 클라이언트에서도 Firebase 인증 SDK를 사용하는 한 Firebase 인증 헤더가 자동으로 포함됩니다. Firebase 인증을 사용하여 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) => {...})

Firebase Auth 플러그인을 사용하면 userDecodedIdToken으로 반환됩니다. 위에서 설명한 대로 언제든지 getFlowAuth()를 통해 이 객체를 가져올 수 있습니다. 개발 중에 이 흐름을 실행할 때는 같은 방식으로 사용자 객체를 전달합니다.

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

기본적으로 Firebase 인증 플러그인을 사용하려면 클라이언트에서 인증 헤더를 전송해야 합니다. 하지만 인증된 사용자에 대한 특수 처리 (예: 기능 업셀링)를 통해 인증되지 않은 액세스를 허용하려는 경우 다음과 같이 정책을 구성할 수 있습니다.

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

Cloud 함수를 더 넓은 인터넷에 노출할 때마다 일종의 승인 메커니즘을 사용하여 데이터와 고객 데이터를 보호하는 것이 매우 중요합니다. 따라서 코드 기반 승인 검사 없이 Cloud 함수를 배포해야 하는 경우가 있습니다 (예: 함수는 누구나 호출할 수 없지만 대신 Cloud IAM으로 보호됩니다). onFlow()를 사용할 때는 authPolicy 필드가 항상 필요하지만 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) => {...})

클라이언트 무결성

자체 인증은 앱 보호에 큰 도움이 됩니다. 하지만 클라이언트 앱만 함수를 호출하도록 하는 것도 중요합니다. Genkit용 Firebase 플러그인에는 Firebase 앱 체크에 대한 최고 수준의 지원이 포함되어 있습니다. 다음 구성 옵션을 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) => {...})

Firebase 이외의 HTTP 승인

Firebase용 Cloud Functions 외부의 서버 컨텍스트에 흐름을 배포할 때 기본 흐름과 함께 자체 승인 확인을 설정할 방법이 있어야 합니다. 다음과 같은 옵션을 선택할 수 있습니다.

  1. 원하는 서버 프레임워크를 사용하고 위에서 언급한 대로 runFlow()를 통해 인증 컨텍스트를 전달합니다.

  2. 기본 제공되는 startFlowsServer()를 사용하고 흐름 구성에서 Express 미들웨어를 제공합니다.

    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
    

    Express 사용에 대한 자세한 내용은 Cloud Run 안내를 참조하세요.

(1)을 선택하면 middleware 구성 옵션이 runFlow()에 의해 무시됩니다.