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

Khi xây dựng 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 được lưu trữ trong hệ thống của bạn. Đối với các LLM, chúng tôi cần phải xem xét kỹ hơn cần thiết để đảm bảo rằng mô hình chỉ truy cập vào dữ liệu mà mô hình đó cần, sẽ gọi công cụ có phạm vi phù hợp với người dùng đang gọi LLM và quy trình đang được gọi chỉ do các ứng dụng khách đã xác minh.

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 quy trình chạy trên Cloud Functions cho Firebase, nhà phát triển cần cần phải cung cấp chính sách xác thực hoặc thừa nhận rõ ràng việc thiếu một. Đố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 tính năng xác thực, nhưng bắt buộc phải tích hợp thủ công hơn một chút.

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

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

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 luồng 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ẽ phải 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ể truyền đối tượng Xác thực bằng cách nhập JSON trong trường "Xác thực 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(), kể 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ông cụ cho nhà phát triển Genkit, bạn có thể chỉ định thông tin xác thực này trong giao diện người dùng hoặc trên dòng lệnh có 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 giúp tích hợp thuận tiện với tính năng Xác thực Firebase / Google Cloud Identity Platform cũng như tính năng hỗ trợ tích hợp sẵn của tính năng Kiểm tra ứng dụng Firebase.

Ủ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 Cloud Functions cho Firebase SDK ứng dụng. Khi sử dụng SDK, tiêu đề Xác thực Firebase sẽ tự động được đưa vào dưới dạng miễn là ứng dụng khách của bạn cũng sử dụng SDK xác thực của Firebase. Bạn có thể sử dụng tính năng Xác thực Firebase để bảo vệ quy trình đượ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 DecodeIdToken. Bạn luôn có thể truy xuất đối tượng này bất cứ lúc nào qua getFlowAuth() như đã ghi chú ở 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ợ Xác thực Firebase yêu cầu phải gửi tiêu đề xác thực bằng khách hàng, 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 cho người dùng đã xác thực (chẳng hạn như bán thêm các tính năng), 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 cung cấp một Chức năng đám mây trên Internet, điều quan trọng là bạn cần 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. Như đã nói, đôi khi bạn cần để triển khai một Hàm đám mây mà không cần kiểm tra uỷ quyền dựa trên mã (ví dụ: Hàm của bạn không thể gọi được mà được bảo vệ bằng IAM Cloud). Chiến lược phát hành đĩa đơn Trường authPolicy là trường 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 đang bỏ qua kiểm tra ủy 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

Việc tự xác thực sẽ giúp bảo vệ ứng dụng của bạn rất hiệu quả. Tuy nhiên, cần đảm bảo rằng chỉ các ứng dụng khách mới gọi các hàm của bạn. Chiến lược phát hành đĩa đơn Trình bổ trợ Firebase cho bộ công cụ tạo bao gồm tính năng hỗ trợ hạng nhất cho Kiểm tra ứng dụng Firebase. Chỉ cần thêm các tuỳ chọn cấu hình sau đối với onFlow() của bạn:

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 luồng cho ngữ cảnh máy chủ bên ngoài Chức năng đám mây cho Firebase, bạn sẽ muốn có một cách để thiết lập hoạt động kiểm tra khoản 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 thích và chuyển ngữ cảnh xác thực thông 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 Cloud Run .

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