Uỷ quyền và tính toàn vẹn

Khi xây dựng bất kỳ ứng dụng nào hướng đến công chúng, điều cực kỳ quan trọng là bạn phải bảo vệ dữ liệu được lưu trữ trong hệ thống. Đối với LLM, bạn cần phải cẩn thận hơn để đảm bảo rằng mô hình chỉ truy cập vào dữ liệu cần thiết, các lệnh gọi công cụ được đưa vào phạm vi thích hợp cho người dùng gọi LLM và luồng chỉ được các ứng dụng khách đã xác minh gọi.

Firebase Genkit cung cấp các cơ chế để quản lý chính sách uỷ quyền và ngữ cảnh. Đối với các luồng chạy trên Cloud Functions cho Firebase, nhà phát triển phải cung cấp chính sách xác thực hoặc phải xác nhận rõ ràng rằng họ không có chính sách xác thực. Đối với các luồng không phải Hàm, bạn cũng có thể quản lý và thiết lập quyền xác thực, nhưng cần tích hợp thêm một chút theo cách thủ công.

Uỷ quyền luồng cơ bản

Tất cả luồng đều có thể xác định authPolicy trong cấu hình của chúng. Chính sách xác thực là một hàm kiểm tra xem có đáp ứng một số tiêu chí nhất định (do bạn xác định) hay không và gửi một ngoại lệ nếu có bất kỳ kiểm thử nào không thành công. Nếu bạn đặt trường này, trường này sẽ được thực thi trước khi gọi luồng:

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

Khi thực thi quy trình này, bạn phải cung cấp một đối tượng xác thực bằng withLocalAuthContext, nếu không, bạn sẽ nhận được lỗi:

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

Khi chạy bằng giao diện người dùng phát triển Genkit, bạn có thể truyền đối tượng Auth bằng cách nhập JSON vào thẻ "Auth JSON": {"uid": "abc-def"}.

Bạn cũng có thể truy xuất ngữ cảnh xác thực cho luồng bất cứ lúc nào trong luồng bằng cách gọi getFlowAuth(), bao gồm cả trong các hàm do luồng gọi:

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

Khi kiểm thử luồng bằng các công cụ phát triển Genkit, bạn có thể chỉ định đối tượng xác thực này trong giao diện người dùng hoặc trên dòng lệnh bằng cờ --auth:

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

Tích hợp Cloud Functions cho Firebase

Trình bổ trợ Firebase cung cấp tính năng tích hợp thuận tiện với Firebase Auth / Nền tảng nhận dạng trên đám mây của Google cũng như tính năng hỗ trợ Tìm lỗi mã nguồn trong ứng dụng Firebase tích hợp sẵn.

Ủy quyền

Trình bao bọc onFlow() do trình bổ trợ Firebase cung cấp hoạt động nguyên gốc với SDK ứng dụng của Hàm trên đám mây cho Firebase. Khi sử dụng SDK, tiêu đề Firebase Auth sẽ tự động được đưa vào miễn là ứng dụng của bạn cũng đang sử dụng SDK Firebase Auth. Bạn có thể sử dụng Firebase Auth để bảo vệ các luồng được xác định bằng 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...
  }
);

Khi sử dụng trình bổ trợ Firebase Auth, user sẽ được trả về dưới dạng DecodedIdToken. Bạn có thể truy xuất đối tượng này bất cứ lúc nào thông qua getFlowAuth() như đã lưu ý ở trên. Khi chạy luồng này trong quá trình phát triển, bạn sẽ truyền đối tượng người dùng theo cách tương tự:

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

Theo mặc định, trình bổ trợ Firebase Auth yêu cầu ứng dụng gửi tiêu đề xác thực, nhưng trong trường hợp bạn muốn cho phép truy cập chưa xác thực bằng cách xử lý đặc biệt cho người dùng đã xác thực (ví dụ: các tính năng bán thêm), thì bạn có thể định cấu hình chính sách như sau:

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

Bất cứ khi nào bạn hiển thị một Hàm trên đám mây cho Internet rộng lớn hơn, bạn phải sử dụng một số cơ chế uỷ quyền để bảo vệ dữ liệu của mình và dữ liệu của khách hàng. Tuy nhiên, có những lúc bạn cần triển khai một Hàm trên đám mây không có quy trình kiểm tra quyền dựa trên mã (ví dụ: Hàm của bạn không thể gọi được trên toàn cầu nhưng được bảo vệ bằng Cloud IAM). Trường authPolicy luôn bắt buộc khi sử dụng onFlow(), nhưng bạn có thể cho thư viện biết rằng bạn sẽ bỏ qua các bước kiểm tra uỷ quyền bằng cách sử dụng hàm 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...
  }
);

Tính toàn vẹn của ứng dụng

Việc xác thực tự thân sẽ giúp bảo vệ ứng dụng của bạn rất nhiều. Tuy nhiên, bạn cũng cần đảm bảo rằng chỉ các ứng dụng khách của bạn mới gọi các hàm của bạn. Trình bổ trợ Firebase cho genkit có khả năng hỗ trợ hàng đầu cho Kiểm tra ứng dụng Firebase. Bạn chỉ cần thêm các tuỳ chọn cấu hình sau vào 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...
  }
);

Cấp quyền HTTP không phải Firebase

Khi triển khai flow đến ngữ cảnh máy chủ bên ngoài Cloud Functions cho Firebase, bạn nên có cách thiết lập quy trình kiểm tra uỷ quyền của riêng mình cùng với flow gốc. Bạn có hai tuỳ chọn:

  1. Sử dụng bất kỳ khung máy chủ nào bạn muốn và truyền ngữ cảnh xác thực thông qua lệnh gọi flow như đã lưu ý ở trên.

  2. Sử dụng startFlowsServer() tích hợp sẵn và cung cấp phần mềm trung gian Express trong cấu hình luồng:

    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
    

    Để biết thêm thông tin về cách sử dụng Express, hãy xem hướng dẫn về Cloud Run.

Xin lưu ý rằng nếu bạn chọn (1), tuỳ chọn cấu hình middleware sẽ bị bỏ qua khi luồng được gọi trực tiếp.