승인 및 무결성

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

Firebase Genkit은 승인 정책 및 컨텍스트를 관리하기 위한 메커니즘을 제공합니다. Firebase에서 실행되는 흐름은 인증 정책 콜백 (또는 도우미)을 사용할 수 있습니다. 또는 Firebase는 자체 검사를 실행할 수 있는 흐름에 인증 컨텍스트를 제공합니다. Functions 이외의 흐름의 경우 미들웨어를 통해 인증을 관리하고 설정할 수 있습니다.

흐름 내에서 승인

흐름은 두 가지 방법으로 승인을 확인할 수 있습니다.요청 바인딩(예: Firebase용 Cloud Functions의 경우 onCallGenkit 또는 express)이 승인을 시행하거나 이러한 프레임워크가 흐름 자체에 인증 정책을 전달할 수 있습니다. 이 경우 흐름은 흐름 내에서 관리되는 인증 정보에 액세스할 수 있습니다.

import { genkit, z, UserFacingError } from 'genkit';

const ai = genkit({ ... });

export const selfSummaryFlow = ai.defineFlow( {
  name: 'selfSummaryFlow',
  inputSchema: z.object({ uid: z.string() }),
  outputSchema: z.string(),
}, async (input, { context }) => {
  if (!context.auth) {
    throw new UserFacingErrorError('UNAUTHENTICATED', 'Unauthenticated');
  }
  if (input.uid !== context.auth.uid) {
    throw new UserFacingError('PERMISSION_DENIED', 'You may only summarize your own profile data.');
  }
  // Flow logic here...
});

이 경우 context.auth를 채우는 것은 요청 바인딩에 따라 다릅니다. 예를 들어 onCallGenkitcontext.auth(Firebase 인증), context.app (Firebase 앱 체크), context.instanceIdToken (Firebase 클라우드 메시징)을 자동으로 채웁니다. 흐름을 수동으로 호출할 때 자체 인증 컨텍스트를 수동으로 추가할 수 있습니다.

// Error: Authorization required.
await selfSummaryFlow({ uid: 'abc-def' });

// Error: You may only summarize your own profile data.
await selfSummaryFlow.run(
  { uid: 'abc-def' },
  {
    context: { auth: { uid: 'hij-klm' } },
  }
);

// Success
await selfSummaryFlow(
  { uid: 'abc-def' },
  {
    context: { auth: { uid: 'abc-def' } },
  }
);

Genkit 개발 UI로 실행하는 경우 'Auth JSON' 탭에 JSON({"uid": "abc-def"})을 입력하여 Auth 객체를 전달할 수 있습니다.

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

import { genkit, z } from 'genkit';

const ai = genkit({ ... });;

async function readDatabase(uid: string) {
  const auth = ai.currentContext()?.auth;
  // Note: the shape of context.auth depends on the provider. onCallGenkit puts
  // claims information in auth.token
  if (auth?.token?.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에서 또는 --context 플래그를 사용하여 명령줄에서 이 인증 객체를 지정할 수 있습니다.

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

Firebase용 Cloud Functions를 사용하여 승인

Firebase용 Cloud Functions SDK는 Firebase Auth / Google Cloud Identity Platform 통합을 비롯한 Genkit과 내장 Firebase App Check 지원을 지원합니다.

사용자 인증

Firebase Functions 라이브러리에서 제공하는 onCallGenkit() 래퍼에는 Firebase용 Cloud Functions 클라이언트 SDK에 대한 기본 제공 지원이 포함되어 있습니다. 이러한 SDK를 사용하면 앱 클라이언트가 Firebase Auth SDK도 사용하는 한 Firebase Auth 헤더가 자동으로 포함됩니다. Firebase 인증을 사용하여 onCallGenkit()로 정의된 흐름을 보호할 수 있습니다.

import { genkit } from 'genkit';
import { onCallGenkit } from 'firebase-functions/https';

const ai = genkit({ ... });;

const selfSummaryFlow = ai.defineFlow({
  name: 'selfSummaryFlow',
  inputSchema: z.string(),
  outputSchema: z.string(),
}, async (input) => {
  // Flow logic here...
});

export const selfSummary = onCallGenkit({
  authPolicy: (auth) => auth?.token?.['email_verified'] && auth?.token?.['admin'],
}, selfSummaryFlow);

onCallGenkit를 사용하면 context.auth가 사용자 ID의 uidDecodedIdTokentoken이 있는 객체로 반환됩니다. 앞에서 언급한 대로 언제든지 ai.currentContext()를 사용하여 이 객체를 가져올 수 있습니다. 개발 중에 이 흐름을 실행할 때는 동일한 방식으로 사용자 객체를 전달합니다.

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

Cloud 함수를 더 광범위한 인터넷에 노출할 때마다 일종의 승인 메커니즘을 사용하여 내 데이터와 고객의 데이터를 보호하는 것이 매우 중요합니다. 하지만 코드 기반 승인 검사 없이 Cloud 함수를 배포해야 하는 경우도 있습니다 (예: 함수가 전 세계에서 호출할 수 없지만 Cloud IAM으로 보호됨). Firebase용 Cloud Functions를 사용하면 IAM 액세스를 제어하는 invoker 속성을 사용하여 이를 실행할 수 있습니다. 특수 값 'private'는 함수를 기본 IAM 설정으로 둡니다. 즉, Cloud Run 호출자 역할이 있는 호출자만 함수를 실행할 수 있습니다. 대신 이 함수를 호출할 권한이 부여되어야 하는 사용자 또는 서비스 계정의 이메일 주소를 제공할 수 있습니다.

import { onCallGenkit } from 'firebase-functions/https'

const selfSummaryFlow = ai.defineFlow({
  name: 'selfSummaryFlow',
  inputSchema: z.string(),
  outputSchema: z.string(),
}, async (input) => {
  // Flow logic here...
});

export const selfSummary = onCallGenkit({
  invoker: 'private',
}, selfSummaryFlow);

클라이언트 무결성

인증만으로도 앱을 보호하는 데 큰 도움이 됩니다. 하지만 클라이언트 앱만 함수를 호출하도록 하는 것도 중요합니다. genkit용 Firebase 플러그인에는 Firebase 앱 체크에 대한 최고의 지원이 포함되어 있습니다. onCallGenkit()에 다음 구성 옵션을 추가합니다.

import { onCallGenkit } from 'firebase-functions/https';

const selfSummaryFlow = ai.defineFlow({
  name: 'selfSummaryFlow',
  inputSchema: z.string(),
  outputSchema: z.string(),
}, async (input) => {
  // Flow logic here...
});

export const selfSummary = onCallGenkit({
  // 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: ...,
}, selfSummaryFlow);

Firebase 외 HTTP 승인

Firebase용 Cloud Functions 외부의 서버 컨텍스트에 흐름을 배포할 때는 기본 제공 흐름과 함께 자체 승인 검사를 설정하는 방법이 필요합니다.

ContextProvider를 사용하여 auth와 같은 컨텍스트 값을 채우고 선언적 정책 또는 정책 콜백을 제공합니다. Genkit SDK는 apiKey와 같은 ContextProvider를 제공하며 플러그인도 이를 노출할 수 있습니다. 예를 들어 @genkit-ai/firebase/context 플러그인은 Firebase 인증 사용자 인증 정보를 확인하고 컨텍스트에 채우는 컨텍스트 제공자를 노출합니다.

다음과 같은 코드가 다양한 애플리케이션에 표시될 수 있습니다.

// Express app with a simple API key
import { genkit, z } from 'genkit';

const ai = genkit({ ... });;

export const selfSummaryFlow = ai.defineFlow(
  {
    name: 'selfSummaryFlow',
    inputSchema: z.object({ uid: z.string() }),
    outputSchema: z.string(),
  },
  async (input) => {
    // Flow logic here...
  }
);

다음과 같이 작성하여 간단한 '흐름 서버' 익스프레스 앱을 보호할 수 있습니다.

import { apiKey } from "genkit";
import { startFlowServer, withContext } from "@genkit-ai/express";

startFlowServer({
  flows: [
    withContext(selfSummaryFlow, apiKey(process.env.REQUIRED_API_KEY))
  ],
});

또는 동일한 도구를 사용하여 맞춤 Express 애플리케이션을 빌드할 수도 있습니다.

import { apiKey } from "genkit";
import * as express from "express";
import { expressHandler } from "@genkit-ai/express;

const app = express();
// Capture but don't validate the API key (or its absence)
app.post('/summary', expressHandler(selfSummaryFlow, { contextProvider: apiKey()}))

app.listen(process.env.PORT, () => {
  console.log(`Listening on port ${process.env.PORT}`);
})

ContextProvider는 웹 프레임워크를 추상화하므로 이러한 도구는 Next.js와 같은 다른 프레임워크에서도 작동합니다. 다음은 Next.js를 기반으로 빌드된 Firebase 앱의 예입니다.

import { appRoute } from "@genkit-ai/express";
import { firebaseContext } from "@genkit-ai/firebase";

export const POST = appRoute(selfSummaryFlow, { contextProvider: firebaseContext })

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