승인 및 무결성

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

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

기본 흐름 승인

모든 흐름은 구성에서 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 개발 UI로 실행하는 경우 'Auth JSON' 탭에 JSON({"uid": "abc-def"})을 입력하여 Auth 객체를 전달할 수 있습니다.

흐름에서 호출된 함수를 포함하여 흐름 내에서 언제든지 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 개발 도구로 흐름을 테스트할 때는 UI에서 또는 --auth 플래그를 사용하여 명령줄에서 이 인증 객체를 지정할 수 있습니다.

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

Firebase용 Cloud Functions 통합

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

승인

Firebase 플러그인이 제공하는 onFlow() 래퍼는 Firebase용 Cloud Functions 클라이언트 SDK와 기본적으로 호환됩니다. SDK를 사용할 때 앱 클라이언트가 Firebase Auth SDK도 사용하는 한 Firebase Auth 헤더가 자동으로 포함됩니다. Firebase 인증을 사용하여 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 인증 플러그인을 사용하면 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(
  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...
  }
);

클라이언트 무결성

인증만으로도 앱을 보호하는 데 큰 도움이 됩니다. 하지만 클라이언트 앱만 함수를 호출하도록 하는 것도 중요합니다. genkit용 Firebase 플러그인에는 Firebase 앱 체크에 대한 최고의 지원이 포함되어 있습니다. 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...
  }
);

Firebase 외 HTTP 승인

Firebase용 Cloud Functions 외부의 서버 컨텍스트에 흐름을 배포할 때는 네이티브 흐름과 함께 자체 인증 검사를 설정하는 방법이 필요합니다. 다음과 같은 옵션을 선택할 수 있습니다.

  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 구성 옵션이 무시됩니다.