SQL Connect で Admin SDK を使用する

Firebase Admin SDK は、特権環境から Firebase を操作するために使用するサーバー ライブラリのセットであり、 Firebase SQL Connect サービスに対するクエリとミューテーションの実行、一括データ マネジメント 、権限昇格とユーザーのなりすまし認証情報を使用したその他のオペレーションなどの操作を行うことができます。

Admin SDK には、読み取り/書き込みモードと読み取り専用モードの両方でオペレーションを呼び出す API が用意されています。読み取り専用オペレーションを使用すると、データベース内のデータを変更できない管理機能を安心して実装できます。

Admin SDK の設定

サーバーで Firebase SQL Connect を使用して を使い始めるには、まず をインストールして設定する必要があります。Admin SDK

スクリプトで Admin SDK を初期化する

SDK を初期化するには、SQL Connect 拡張機能をインポートし、 プロジェクト サービス ID とロケーションを宣言します。


import { initializeApp } from 'firebase-admin/app';
import { getDataConnect } from 'firebase-admin/data-connect';

// If you'd like to use OAuth2 flows and other credentials to log in,
// visit https://firebase.google.com/docs/admin/setup#initialize-sdk
// for alternative ways to initialize the SDK.

const app = initializeApp();

const dataConnect = getDataConnect({
    serviceId: 'serviceId',
    location: 'us-west2'
});

Admin SDK で使用するクエリとミューテーションを設計する

Admin SDK は、次の考慮事項を踏まえて SQL Connect オペレーションを実行する場合に便利です。

SDK と @auth(level: NO_ACCESS) オペレーション ディレクティブについて

Admin SDK は権限を使用して動作するため、 クエリとミューテーションを、@auth ディレクティブを使用して設定されたアクセスレベル(NO_ACCESS レベルを含む)に関係なく、任意に実行できます。

クライアント オペレーションに加えて、管理スクリプトにインポートするために管理クエリとミューテーションを .gql ソースファイルに整理する場合は、認証アクセスレベルのない管理オペレーションをマークするか、NO_ACCESS として明示的に設定することをおすすめします。いずれにしても、これにより、クライアントやその他の特権のないコンテキストからこのようなオペレーションが実行されるのを防ぐことができます。

SQL Connect エミュレータで SDK を使用する

プロトタイプ環境とテスト環境では、ローカルデータに対してデータ シーディングなどのオペレーションを実行すると便利な場合があります。Admin SDK を使用すると、ローカル フローの認証と認可を無視できるため、ワークフローを簡素化できます。 (ユーザーのなりすましを使用して、オペレーションの認証と認可の構成に準拠することもできます)。

環境変数 DATA_CONNECT_EMULATOR_HOST が設定されている場合、Firebase Admin SDK は SQL Connect エミュレータに自動的に接続します。

export DATA_CONNECT_EMULATOR_HOST="127.0.0.1:9399"

詳しくは以下をご覧ください。

管理オペレーションを実行する

Admin SDK は、重要なデータに対する特権オペレーション用に提供されています。

Admin SDK には、次の 3 つの API セットが用意されています。

  • 生成された Admin SDK。クライアント SDK を生成するのと同じ方法で、gql 定義から生成されるタイプセーフな SDK です。
  • 任意の GraphQL オペレーションを実行するための一般的なインターフェース。コードでクエリとミューテーションを実装し、読み取り / 書き込みの executeGraphql メソッドまたは読み取り専用の executeGraphqlRead メソッドに渡します。
  • 一括データ オペレーション用の特別なインターフェース。 汎用的な executeGraphql メソッドではなく、 ミューテーション オペレーション専用のメソッド(insertinsertManyupsertupsertMany)を公開します。

生成された SDK でデータを管理する

クライアント SDK を生成するのと同じ方法で、Admin SDKgql定義から生成します。

生成された 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` トークンの一部または すべてを渡すことで、特定のユーザーに代わってオペレーションを実行することもできます。少なくとも、サブクレームでユーザーのユーザー ID を指定する必要があります。(これは、 auth.uid サーバー値 GraphQL オペレーションで参照できる値と同じです)。SQL Connect

ユーザーのなりすましを行う場合、提供したユーザーデータが 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 SDK's verifyIdToken メソッドを使用して、認証トークンを検証してデコードします。たとえば、エンドポイントが プレーン 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 } }
);

無制限のアクセス権で実行する

管理者レベルの権限を必要とするオペレーションを実行する場合は、呼び出しからなりすましパラメータを省略します。

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

この方法で呼び出されたオペレーションは、データベースに完全にアクセスできます。管理目的でのみ使用するクエリまたはミューテーションがある場合は、@auth(level: NO_ACCESS) ディレクティブを使用して定義する必要があります。これにより、管理者レベルの呼び出し元のみがこれらのオペレーションを実行できるようになります。

executeGraphql メソッドでデータを管理する

gql ミューテーションまたはクエリを定義していない 1 回限りのオペレーションを実行する必要がある場合は、executeGraphql メソッドまたは読み取り専用の executeGraphqlRead メソッドを使用できます。

認証されていないユーザーのなりすまし

Admin SDK で公開オペレーションを実行する場合は、完全な管理者権限でオペレーションを実行しないようにしてください(最小権限の原則に従います)。代わりに、なりすましユーザー (次のセクションを参照)として、またはなりすまし 認証されていないユーザーとしてオペレーションを実行する必要があります。認証されていないユーザーは、PUBLIC とマークされたオペレーションのみを実行できます。

// Query to get posts, with authentication level PUBLIC
const queryGetPostsImpersonation = `
    query getPosts @auth(level: PUBLIC) {
        posts {
          description
        }
    }`;

// Attempt to access data as an unauthenticated user
const optionsUnauthenticated: GraphqlOptions<undefined> = {
    impersonate: {
        unauthenticated: true
    }
};

// executeGraphql with impersonated unauthenticated user scope
const gqlResponse = await dataConnect.executeGraphql<UserData, undefined>(queryGetPostsImpersonation, optionsUnauthenticated);

ユーザーのなりすまし

特定のユーザーに代わって、制限付きの認証情報に基づいてスクリプトでユーザーデータを変更する場合もあります。このアプローチは、最小権限の原則に沿ったものです。

このインターフェースを使用するには、 Authenticationトークン形式に準拠したカスタマイズされた JWT 認証トークンから情報を収集します。 カスタム トークンのガイドもご覧ください。

// Get the current user's data
const queryGetUserImpersonation = `
    query getUser @auth(level: USER) {
        user(key: {uid_expr: "auth.uid"}) {
            id,
            name
        }
    }`;

// Impersonate a user with the specified auth claims
const optionsAuthenticated: GraphqlOptions<undefined> = {
    impersonate: {
        authClaims: {
            sub: 'QVBJcy5ndXJ1'
        }
    }
};

// executeGraphql with impersonated authenticated user scope
const gqlResponse = await dataConnect.executeGraphql<UserData, undefined>(queryGetUserImpersonation, optionsAuthenticated);

// gqlResponse -> { "data": { "user": { "id": "QVBJcy5ndXJ1", "name": "Fred" } } }

管理者の認証情報を使用する

管理者レベルの権限を必要とするオペレーションを実行する場合は、呼び出しからなりすましパラメータを省略します。

// User can be publicly accessible, or restricted to admins
const query = "query getProfile(id: AuthID) { user(id: $id) { id name } }";

interface UserData {
  user: {
    id: string;
    name: string;
  };
}

export interface UserVariables {
  id: string;
}

const options:GraphqlOptions<UserVariables> = { variables: { id: "QVBJcy5ndXJ1" } };

// executeGraphql
const gqlResponse = await dataConnect.executeGraphql<UserData, UserVariables>(query, options);

// executeGraphqlRead (similar to previous sample but only for read operations)
const gqlResponse = await dataConnect.executeGraphqlRead<UserData, UserVariables>(query, options);

// gqlResponse -> { "data": { "user": { "id": "QVBJcy5ndXJ1", "name": "Fred" } } }

この方法で呼び出されたオペレーションは、データベースに完全にアクセスできます。管理目的でのみ使用するクエリまたはミューテーションがある場合は、@auth(level: NO_ACCESS) ディレクティブを使用して定義する必要があります。これにより、管理者レベルの呼び出し元のみがこれらのオペレーションを実行できるようになります。

一括データ オペレーションを実行する

本番環境データベースで一括データ オペレーションを行う場合は、Admin SDKを 使用することをおすすめします。

SDK には、一括データを操作するための次のメソッドが用意されています。提供された引数から、各メソッドは GraphQL ミューテーションを構築して実行します。


// Methods of the bulk operations API
// dc is a SQL Connect admin instance from getDataConnect

const resp = await dc.insert("movie" /*table name*/, data[0]);
const resp = await dc.insertMany("movie" /*table name*/, data);
const resp = await dc.upsert("movie" /*table name*/, data[0]);
const resp = await dc.upsertMany("movie" /*table name*/, data);

一括オペレーションのパフォーマンスに関する注意事項

バックエンドへのリクエストごとに Cloud SQL へのラウンドトリップが 1 回発生するため、バッチ処理を行うほどスループットが高くなります。

ただし、バッチサイズが大きいほど、生成される SQL ステートメントが長くなります。PostgreSQL SQL ステートメントの長さ制限に達すると、エラーが発生します。

実際には、ワークロードに適したバッチサイズを見つけるために試行錯誤してください。

次のステップ