使用生成的 Admin SDK

借助 Firebase Data Connect Admin SDK,您可以从可信的环境(例如 Cloud Functions、自定义后端或您自己的工作站)调用查询和突变。与为客户端应用生成 SDK 的方式非常相似,您可以在设计部署到 Data Connect 服务的架构、查询和突变时,并行生成自定义 Admin SDK。然后,将此 SDK 中的方法集成到后端逻辑或管理脚本中。

正如我们在其他地方提到的,请务必注意,Data Connect 查询和突变不是由客户端在请求时提交的。相反,在部署时,数据连接操作会像 Cloud Functions 一样存储在服务器上。这意味着,每当您部署对查询和突变的更改时,还需要重新生成 Admin SDK 并重新部署依赖于这些 SDK 的任何服务。

准备工作

生成 Admin SDK

创建 Data Connect 架构、查询和 mutation 后,您可以生成相应的 Admin SDK:

  1. 打开或创建 connector.yaml 文件,然后添加 adminNodeSdk 定义:

    connectorId: default
    generate:
      adminNodeSdk:
        outputDir: ../../dataconnect-generated/admin-generated
        package: "@dataconnect/admin-generated"
        packageJsonDir: ../..
    

    connector.yaml 文件通常与包含查询和突变定义的 GraphQL (.gql) 文件位于同一目录中。如果您已生成客户端 SDK,则此文件已创建。

  2. 生成 SDK。

    如果您安装了 Data Connect VS Code 扩展程序,该扩展程序将始终保持生成的 SDK 为最新版本。

    否则,请使用 Firebase CLI:

    firebase dataconnect:sdk:generate

    或者,如需在更新 gql 文件时自动重新生成 SDK,请执行以下操作:

    firebase dataconnect:sdk:generate --watch

通过 Admin SDK 执行操作

生成的 Admin SDK 包含与 gql 定义对应的接口和函数,您可以使用这些接口和函数对数据库执行操作。例如,假设您为歌曲数据库生成了一个 SDK,其中包含一个查询 getSongs

import { initializeApp } from "firebase-admin/app";
import { getSongs } from "@dataconnect/admin-generated";

const adminApp = initializeApp();

const songs = await getSongs(
  { limit: 4 },
  { impersonate: { unauthenticated: true } }
);

或者,如需指定连接器配置,请执行以下操作:

import { initializeApp } from "firebase-admin/app";
import { getDataConnect } from "firebase-admin/data-connect";
import {
  connectorConfig,
  getSongs,
} from "@dataconnect/admin-generated";

const adminApp = initializeApp();
const adminDc = getDataConnect(connectorConfig);

const songs = await getSongs(
  adminDc,
  { limit: 4 },
  { impersonate: { unauthenticated: true } }
);

模拟未经过身份验证的用户

Admin SDK 旨在从可信环境中运行,因此可以不受限制地访问您的数据库。

使用 Admin SDK 运行公开操作时,应避免使用完整的管理员权限(遵循最低权限原则)。您应以模拟用户(请参阅下一部分)或模拟未经身份验证的用户的身份运行该操作。未经身份验证的用户只能运行标记为 PUBLIC 的操作。

在上面的示例中,getSongs 查询是以未经身份验证的用户身份执行的。

模拟用户

您还可以通过在 impersonate 选项中传递部分或全部 Firebase Authentication 令牌来代表特定用户执行操作;至少,您必须在 sub 声明中指定用户的用户 ID。(此值与您可以在 Data Connect GraphQL 操作中引用的 auth.uid 服务器值相同。)

当您模拟用户时,只有在您提供的用户数据通过 GraphQL 定义中指定的身份验证检查后,操作才会成功。

如果您从可公开访问的端点调用生成的 SDK,则至关重要的是,该端点必须要求进行身份验证,并且您必须先验证身份验证令牌的完整性,然后才能使用该令牌来模拟用户。

使用可调用函数 Cloud Functions 时,系统会自动验证身份验证令牌,您可以按以下示例所示的方式使用该令牌:

import { HttpsError, onCall } from "firebase-functions/https";

export const callableExample = onCall(async (req) => {
    const authClaims = req.auth?.token;
    if (!authClaims) {
        throw new HttpsError("unauthenticated", "Unauthorized");
    }

    const favoriteSongs = await getMyFavoriteSongs(
        undefined,
        { impersonate: { authClaims } }
    );

    // ...
});

否则,请使用 Admin SDKverifyIdToken 方法来验证和解码身份验证令牌。例如,假设您的端点实现为纯 HTTP 函数,并且您已使用 authorization 标头将 Firebase Authentication 令牌传递给端点(这是标准做法):

import { getAuth } from "firebase-admin/auth";
import { onRequest } from "firebase-functions/https";

const auth = getAuth();

export const httpExample = onRequest(async (req, res) => {
    const token = req.header("authorization")?.replace(/^bearer\s+/i, "");
    if (!token) {
        res.sendStatus(401);
        return;
    }
    let authClaims;
    try {
        authClaims = await auth.verifyIdToken(token);
    } catch {
        res.sendStatus(401);
        return;
    }

    const favoriteSongs = await getMyFavoriteSongs(
        undefined,
        { impersonate: { authClaims } }
    );

    // ...
});

只有在从安全且非公开访问的环境中执行真正意义上的管理任务(例如数据迁移)时,您才应指定并非来自可验证来源的用户 ID:

// Never do this if end users can initiate execution of the code!
const favoriteSongs = await getMyFavoriteSongs(
  undefined,
  { impersonate: { authClaims } }
);

以不受限的访问权限运行

如果您要执行需要管理员级权限的操作,请从调用中省略 impersonate 参数:

await upsertSong(adminDc, {
  title: songTitle_one,
  instrumentsUsed: [Instrument.VOCAL],
});

以这种方式调用的操作具有对数据库的完整访问权限。如果您有仅用于管理目的的查询或突变,则应使用 @auth(level: NO_ACCESS) 指令定义它们。这样做可确保只有管理员级调用者才能执行这些操作。