生成された Admin SDK を使用する

Firebase Data Connect Admin SDK を使用すると、Cloud Functions、カスタム バックエンド、独自のワークステーションなどの信頼できる環境からクエリとミューテーションを呼び出すことができます。クライアント アプリの SDK を生成するのと同様に、Data Connect サービスにデプロイするスキーマ、クエリ、ミューテーションを設計するのと並行して、カスタム管理 SDK を生成できます。次に、この SDK のメソッドをバックエンド ロジックまたは管理スクリプトに統合します。

別の場所でも説明したように、Data Connect のクエリとミューテーションはリクエスト時にクライアントによって送信されないことに注意してください。代わりに、デプロイ時に Data Connect オペレーションは Cloud Functions のようにサーバーに保存されます。つまり、クエリとミューテーションの変更をデプロイするたびに、管理 SDK を再生成し、それらに依存するサービスを再デプロイする必要があります。

始める前に

Admin SDK を生成する

Data Connect のスキーマ、クエリ、ミューテーションを作成したら、対応する管理 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) ディレクティブを使用して定義する必要があります。これにより、管理者レベルの呼び出し元のみがこれらのオペレーションを実行できるようになります。