การให้สิทธิ์และความสมบูรณ์

เมื่อสร้างแอปพลิเคชันที่เปิดเผยต่อสาธารณะ การปกป้องข้อมูลที่จัดเก็บไว้ในระบบของคุณเป็นสิ่งสำคัญอย่างยิ่ง เมื่อพูดถึง LLM ต้องมีความขยันเป็นพิเศษเพื่อให้มั่นใจว่าโมเดลจะเข้าถึงได้เฉพาะข้อมูลที่ควรจะเป็น การเรียกเครื่องมือกำหนดขอบเขตที่เหมาะสมไปที่ผู้ใช้ที่เรียกใช้ LLM และการเรียกใช้โฟลว์จะทำโดยแอปพลิเคชันไคลเอ็นต์ที่ได้รับการยืนยันเท่านั้น

Firebase Genkit มีกลไกในการจัดการนโยบายและบริบทการให้สิทธิ์ สำหรับโฟลว์ที่ทำงานบน Cloud Functions สำหรับ Firebase นักพัฒนาซอฟต์แวร์จะต้องระบุนโยบายการตรวจสอบสิทธิ์หรือรับทราบอย่างชัดแจ้งว่าไม่มีนโยบายการตรวจสอบสิทธิ์ดังกล่าว สำหรับขั้นตอนที่ไม่มีฟังก์ชัน คุณสามารถจัดการและตั้งค่าการตรวจสอบสิทธิ์ได้เช่นกัน แต่ต้องมีการผสานรวมด้วยตนเองเพิ่มเติมเล็กน้อย

การให้สิทธิ์โฟลว์พื้นฐาน

กระบวนการทั้งหมดสามารถกำหนด authPolicy ในการกำหนดค่า นโยบายการตรวจสอบสิทธิ์เป็นฟังก์ชันที่จะทดสอบว่าตรงกับเกณฑ์บางอย่าง (ซึ่งคุณกำหนด) หรือไม่ และจะส่งข้อยกเว้นหากการทดสอบไม่สำเร็จ หากตั้งค่าช่องนี้ไว้ ระบบจะดำเนินการก่อนที่เรียกใช้โฟลว์:

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

ขณะดำเนินการตามขั้นตอนนี้ คุณต้องระบุออบเจ็กต์การตรวจสอบสิทธิ์โดยใช้ withLocalAuthContext ไม่เช่นนั้นคุณจะพบข้อผิดพลาด

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

เมื่อเรียกใช้ด้วย Genkit Development UI คุณส่งออบเจ็กต์ Auth ได้โดยป้อน JSON ในแท็บ "Auth JSON": {"uid": "abc-def"}

นอกจากนี้ คุณยังเรียกข้อมูลบริบทการตรวจสอบสิทธิ์สำหรับโฟลว์ได้ทุกเมื่อภายในโฟลว์โดยเรียกใช้ getFlowAuth() รวมถึงในฟังก์ชันที่เรียกใช้โดยโฟลว์ด้วย ซึ่งมีรายละเอียดดังนี้

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

เมื่อทดสอบโฟลว์ด้วยเครื่องมือ Dev ของ Genkit คุณจะระบุออบเจ็กต์การตรวจสอบสิทธิ์นี้ใน UI หรือในบรรทัดคำสั่งได้ด้วยแฟล็ก --auth ดังนี้

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

การผสานรวม Cloud Functions สำหรับ Firebase

ปลั๊กอิน Firebase มีการผสานรวมกับ Firebase Auth / Google Cloud Identity Platform รวมถึงรองรับ Firebase App Check ในตัว

การให้สิทธิ์

Wrapper onFlow() ที่ได้จากปลั๊กอิน Firebase จะทำงานกับ Cloud Functions for Firebase SDK ของไคลเอ็นต์โดยค่าเริ่มต้น เมื่อใช้ SDK ระบบจะรวมส่วนหัว Firebase Auth โดยอัตโนมัติตราบใดที่ไคลเอ็นต์แอปใช้ Firebase Auth SDK ด้วย คุณใช้ Firebase Auth เพื่อปกป้องขั้นตอนที่คุณกำหนดด้วย 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) => {...})

เมื่อใช้ปลั๊กอิน Firebase Auth ระบบจะแสดงผล user เป็น Deและเพิ่มIdToken คุณเรียกออบเจ็กต์นี้ได้ทุกเมื่อผ่าน getFlowAuth() ตามที่ระบุไว้ข้างต้น เมื่อเรียกใช้ขั้นตอนนี้ระหว่างการพัฒนา คุณจะส่งออบเจ็กต์ผู้ใช้ด้วยวิธีเดียวกัน

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

โดยค่าเริ่มต้น ปลั๊กอิน Firebase Auth กำหนดให้ไคลเอ็นต์ส่งส่วนหัวการตรวจสอบสิทธิ์ แต่ในกรณีที่คุณต้องการอนุญาตการเข้าถึงที่ไม่ผ่านการตรวจสอบสิทธิ์โดยมีการจัดการพิเศษสำหรับผู้ใช้ที่ตรวจสอบสิทธิ์แล้ว (เช่น ฟีเจอร์การขายอัปเซล) คุณสามารถกำหนดค่านโยบายได้ดังนี้

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

เมื่อใดก็ตามที่คุณเปิดเผย Cloud Function กับอินเทอร์เน็ตในวงกว้าง คุณจำเป็นต้องใช้กลไกการให้สิทธิ์บางอย่างเพื่อปกป้องข้อมูลของคุณและข้อมูลของลูกค้า อย่างไรก็ตาม มีบางครั้งที่คุณต้องทำให้ Cloud Function ใช้งานได้โดยไม่ต้องมีการตรวจสอบการให้สิทธิ์ตามโค้ด (เช่น ฟังก์ชันของคุณเรียกใช้ได้ทั่วโลก แต่ได้รับการปกป้องโดย Cloud IAM แทน) ต้องกรอกข้อมูลในช่อง authPolicy เสมอเมื่อใช้ onFlow() แต่คุณสามารถระบุให้ไลบรารีทราบว่าคุณกำลังปฏิเสธการตรวจสอบการให้สิทธิ์โดยใช้ฟังก์ชัน 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) => {...})

ความสมบูรณ์ของไคลเอ็นต์

การตรวจสอบสิทธิ์เพียงอย่างเดียวจะช่วยป้องกันแอปของคุณได้อย่างมาก แต่สิ่งสำคัญอีกอย่างคือ การตรวจสอบว่ามีเพียงแอปไคลเอ็นต์เท่านั้นที่เรียกใช้ฟังก์ชันของคุณได้ ปลั๊กอิน Firebase สำหรับ Genkit มีการรองรับระดับเสิร์ชสำหรับ Firebase App Check เพียงเพิ่มตัวเลือกการกำหนดค่าต่อไปนี้ใน 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) => {...})

การให้สิทธิ์ HTTP ที่ไม่ใช่ Firebase

เมื่อทำให้โฟลว์ใช้งานได้กับบริบทเซิร์ฟเวอร์นอก Cloud Functions for Firebase คุณจะต้องมีวิธีตั้งค่าการตรวจสอบการให้สิทธิ์ของคุณเองควบคู่ไปกับขั้นตอนดั้งเดิม คุณมีสองตัวเลือกดังนี้

  1. ใช้เฟรมเวิร์กของเซิร์ฟเวอร์ใดก็ได้ที่คุณชอบ และผ่านบริบทการตรวจสอบสิทธิ์ผ่าน runFlow() ตามที่ระบุไว้ข้างต้น

  2. ใช้ startFlowsServer() ในตัวและระบุมิดเดิลแวร์ Express ในการกำหนดค่าโฟลว์ ดังนี้

    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
    

    ดูข้อมูลเพิ่มเติมเกี่ยวกับการใช้ Express ได้ในวิธีการของ Cloud Run

โปรดทราบว่า หากคุณใช้ (1) runFlow() จะไม่สนใจตัวเลือกการกำหนดค่า middleware