Ủy quyền và tính toàn vẹn

Khi tạo bất kỳ ứng dụng công khai nào, điều cực kỳ quan trọng là phải bảo vệ dữ liệu lưu trữ trong hệ thống của bạn. Đối với các LLM, bạn cần thêm chuyên sâu để đảm bảo 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ụ sẽ được xác định phạm vi phù hợp với người dùng gọi LLM và quy trình này chỉ được gọi bằng các ứng dụng khách đã xác minh.

Firebase Genkit cung cấp các cơ chế để quản lý ngữ cảnh và chính sách uỷ quyền. Đối với các quy trình 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 xác nhận rõ ràng việc không có chính sách này. Đối với các quy trình không phải Hàm, bạn cũng có thể quản lý và thiết lập quy trình xác thực, nhưng yêu cầu tích hợp thủ công hơn một chút.

Cấp phép luồng cơ bản

Tất cả các flow đề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 liệu có đáp ứng một số tiêu chí nhất định (do bạn xác định) hay không và trả về 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, thì trường này sẽ được thực thi trước khi gọi luồng:

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

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

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

Khi chạy bằng giao diện người dùng Phát triển Genkit, bạn có thể chuyển đối tượng Xác thực bằng cách nhập JSON vào thẻ "JSON xác thực": {"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 được luồng gọi:

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

Khi kiểm thử quy trình bằng các công cụ dành cho nhà 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"}'

Chức năng đám mây cho quá trình tích hợp Firebase

Trình bổ trợ Firebase giúp bạn tích hợp thuận tiện với tính năng Xác thực Firebase / Nền tảng Google Cloud Identity cũng như tính năng hỗ trợ Kiểm tra ứng dụng Firebase tích hợp sẵn.

Uỷ quyền

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

Khi sử dụng trình bổ trợ Xác thực Firebase, user sẽ được trả về dưới dạng một DecodedIdToken. Bạn luôn có thể truy xuất đối tượng này bất cứ lúc nào thông qua getFlowAuth() như đã nêu ở trên. Khi chạy quy trình 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ợ Xác thực Firebase 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 quyền truy cập chưa được xác thực bằng cách xử lý đặc biệt dành cho người dùng đã xác thực (chẳng hạn như bán thêm tính năng), 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 cung cấp một Hàm đám mây trên Internet rộng hơn, điều cực kỳ quan trọng là bạn phải sử dụng một số loại 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, đôi khi bạn cần triển khai Hàm đám mây mà không cần kiểm tra việc uỷ 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 mà được bảo vệ bởi Cloud IAM). Trường authPolicy luôn là trường bắt buộc khi sử dụng onFlow(). Tuy nhiên, bạn có thể cho thư viện biết rằng bạn sẽ bỏ qua quá trình 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({
    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) => {...})

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

Bản thân xác thực sẽ giúp bảo vệ ứng dụng của bạn rất hiệu quả. Nhưng điều quan trọng là phải đảm bảo rằng chỉ các ứng dụng khách mới gọi các chức năng của bạn. Trình bổ trợ Firebase cho genkit bao gồm tính năng hỗ trợ hàng đầu cho tính năng 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({
    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) => {...})

Uỷ quyền HTTP không phải Firebase

Khi triển khai các luồng đế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 các quy trình kiểm tra uỷ quyền của riêng bạn cùng với các luồng 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à chuyển ngữ cảnh xác thực qua runFlow() 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:

    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
    

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

Xin lưu ý rằng nếu bạn sử dụng (1), middleware tuỳ chọn cấu hình sẽ bị runFlow() bỏ qua.