Используйте Admin SDK с Data Connect

Firebase Admin SDK — это набор серверных библиотек, позволяющих взаимодействовать с Firebase из привилегированных сред для выполнения таких действий, как запросы и мутации к сервису Firebase Data Connect для массового управления данными и других операций с повышенными привилегиями и использованием учетных данных, имитирующих реальные.

Admin SDK предоставляет API для вызова операций как в режиме чтения/записи, так и в режиме только для чтения. В режиме только для чтения вы можете быть уверены в реализации административных функций, которые не могут изменять данные в ваших базах данных.

Настройка Admin SDK

Для начала работы с Firebase Data Connect на вашем сервере вам сначала потребуется установить и настроить Admin SDK для Node.js.

Инициализируйте Admin SDK в своих скриптах.

Для инициализации SDK импортируйте расширения Data Connect и укажите идентификатор службы вашего проекта и его местоположение.


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 полезен для выполнения операций Data Connect .

Разберитесь в SDK и директиве ` @auth(level: NO_ACCESS)

Поскольку Admin SDK работает с привилегиями, он может выполнять любые ваши запросы и мутации независимо от уровней доступа, установленных с помощью директив @auth , включая уровень NO_ACCESS .

Если помимо клиентских операций вы организуете административные запросы и мутации в исходных файлах .gql для импорта в административные скрипты, Firebase рекомендует помечать административные операции без указания уровня доступа или, возможно, более явно установить для них значение NO_ACCESS . В любом случае это предотвратит выполнение таких операций с клиентских устройств или в других непривилегированных контекстах.

Используйте SDK с эмулятором Data Connect .

В средах прототипирования и тестирования может быть полезно выполнять заполнение данных и другие операции с локальными данными. Admin SDK позволяет упростить рабочие процессы, поскольку он может игнорировать аутентификацию и авторизацию для локальных потоков. (Вы также можете явно включить соответствие конфигурации аутентификации и авторизации ваших операций с использованием имитации пользователя.)

При установке переменной среды DATA_CONNECT_EMULATOR_HOST компонент Firebase Admin SDK автоматически подключается к эмулятору Data Connect :

export DATA_CONNECT_EMULATOR_HOST="127.0.0.1:9399"

Для получения более подробной информации см.:

Выполнение административных операций

Admin SDK предоставляется для выполнения привилегированных операций с вашими критически важными данными.

Admin SDK предоставляет три набора API:

  • Сгенерированные административные SDK — это типобезопасные SDK, созданные на основе ваших определений gql аналогично тому, как вы генерируете клиентские SDK.
  • Общий интерфейс для выполнения произвольных операций GraphQL, в котором ваш код реализует запросы и мутации и передает их методу executeGraphql с возможностью чтения и записи или методу executeGraphqlRead только для чтения.
  • Специализированный интерфейс для операций с большими объемами данных, который вместо общих методов executeGraphql предоставляет выделенные методы для операций изменения: insert , insertMany , upsert и upsertMany .

Управляйте данными с помощью сгенерированных SDK.

Административные SDK генерируются из определений gql точно так же, как и клиентские SDK.

Сгенерированный административный 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 } }
);

Выдача себя за неавторизованного пользователя

SDK администратора предназначены для запуска в доверенных средах и, следовательно, имеют неограниченный доступ к вашим базам данных.

При выполнении общедоступных операций с помощью SDK администратора следует избегать выполнения операции с полными правами администратора (следуя принципу минимальных привилегий). Вместо этого следует выполнять операцию либо от имени пользователя, имитирующего пользователя (см. следующий раздел), либо от имени неаутентифицированного пользователя, имитирующего пользователя. Неаутентифицированные пользователи могут выполнять только операции, помеченные как PUBLIC .

В приведенном выше примере запрос getSongs выполняется от имени неаутентифицированного пользователя.

Выдача себя за пользователя

Вы также можете выполнять операции от имени конкретных пользователей, передавая часть или весь токен Firebase Authentication в параметре impersonate ; как минимум, необходимо указать идентификатор пользователя в утверждении sub. (Это то же значение, что и значение сервера auth.uid , на которое можно ссылаться в операциях GraphQL Data 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 } }
    );

    // ...
});

В противном случае используйте метод verifyIdToken из Admin SDK для проверки и декодирования токена аутентификации. Например, предположим, что ваша конечная точка реализована как обычная HTTP-функция, и вы передали токен Firebase Authentication в вашу конечную точку, используя заголовок authorization , как это принято:

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

    // ...
});

Указывать идентификатор пользователя, не полученный из проверяемого источника, следует только при выполнении действительно административных задач, таких как миграция данных, из защищенной, недоступной для публичного доступа среды:

// 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) . Это гарантирует, что только пользователи с правами администратора смогут выполнять эти операции.

Управление данными с помощью методов executeGraphql

Если вам необходимо выполнить разовые операции, для которых вы не определили мутации или запросы gql , вы можете использовать метод executeGraphql или метод executeGraphqlRead предназначенный только для чтения.

Выдача себя за неавторизованного пользователя

При выполнении общедоступных операций с помощью 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);

Выдача себя за пользователя

Также существуют сценарии использования, когда вам нужно, чтобы ваши скрипты изменяли пользовательские данные на основе ограниченных учетных данных от имени конкретного пользователя. Такой подход соответствует принципу минимальных привилегий.

Для использования этого интерфейса необходимо получить информацию из пользовательского токена аутентификации JWT, соответствующего формату токена Authentication . См. также руководство по пользовательским токенам .

// 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" } } }

Используйте административные учетные данные.

Если вы выполняете операцию, требующую прав администратора, опустите параметр impersonate из вызова:

// 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) . Это гарантирует, что только пользователи с правами администратора смогут выполнять эти операции.

Выполняйте операции с большими объемами данных.

Firebase рекомендует использовать Admin SDK для выполнения массовых операций с данными в производственных базах данных.

SDK предоставляет следующие методы для работы с большими объемами данных. На основе предоставленных аргументов каждый метод создает и выполняет мутацию GraphQL.


// Methods of the bulk operations API
// dc is a Data 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, поэтому чем больше запросов вы обрабатываете пакетами, тем выше пропускная способность.

Однако, чем больше размер пакета, тем длиннее генерируемый SQL-запрос. При достижении лимита длины SQL-запроса в PostgreSQL возникнет ошибка.

На практике, экспериментируйте, чтобы найти оптимальный размер партии для вашей рабочей нагрузки.

Что дальше?