Utilizzare l'SDK Admin con Data Connect

Il Firebase Admin SDK è un insieme di librerie server che ti consente di interagire con Firebase da ambienti con privilegi per eseguire azioni come query e mutazioni su un servizio Firebase Data Connect per la gestione collettiva dei dati e altre operazioni con privilegi elevati e credenziali impersonate.

Il Admin SDK fornisce un'API per chiamare le operazioni in modalità di lettura/scrittura e di sola lettura. Con le operazioni di sola lettura, puoi implementare le funzioni amministrative che non possono modificare i dati nei database.

Configurazione dell'SDK Admin

Per iniziare a utilizzare l'SDK Admin con Firebase Data Connect sul server, devi prima installare e configurare l'Admin SDK per Node.js.

Inizializzare l'SDK Admin negli script

Per inizializzare l'SDK, importa le estensioni Data Connect e dichiara l'ID e la località del servizio del progetto.


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

Progettare query e mutazioni da utilizzare con il Admin SDK

L'Admin SDK è utile per eseguire le operazioni Data Connect, tenendo conto delle seguenti considerazioni.

Comprendere l'SDK e la direttiva dell'operazione @auth(level: NO_ACCESS)

Poiché il Admin SDK opera con privilegi, può eseguire qualsiasi query e mutazione indipendentemente dai livelli di accesso impostati utilizzando le direttive @auth, incluso il livello NO_ACCESS.

Se, oltre alle operazioni client, organizzi le query e le mutazioni amministrative nei file di origine .gql per l'importazione negli script amministrativi, Firebase ti consiglia di contrassegnare le operazioni amministrative senza alcun livello di accesso di autorizzazione o, in alternativa, di impostarle in modo più esplicito come NO_ACCESS. In entrambi i casi, queste operazioni non possono essere eseguite dai client o in altri contesti non privilegiati.

Utilizzare l'SDK con l'emulatore Data Connect

Negli ambienti di prototipazione e test, può essere utile eseguire il seeding dei dati e altre operazioni sui dati locali. Il Admin SDK ti consente di semplificare i tuoi flussi di lavoro perché può ignorare l'autenticazione e l'autorizzazione per i flussi locali. Puoi anche attivare esplicitamente la conformità alla configurazione di autenticazione e autorizzazione delle operazioni con l'impersonificazione di un utente.

Gli SDK Admin Firebase si connettono automaticamente all'Data Connect emulatore quando è impostata la variabile di ambiente DATA_CONNECT_EMULATOR_HOST:

export DATA_CONNECT_EMULATOR_HOST="127.0.0.1:9399"

Per ulteriori informazioni, vedi:

Eseguire operazioni di amministrazione

L'Admin SDK viene fornito per le operazioni con privilegi sui dati critici.

L'SDK Admin fornisce tre insiemi di API:

  • SDK Admin generati, che sono SDK con sicurezza dei tipi generati dalle definizioni gql nello stesso modo in cui generi gli SDK client.
  • Un'interfaccia generale per l'esecuzione di operazioni GraphQL arbitrarie, in cui il codice implementa query e mutazioni e le passa al metodo di lettura/scrittura executeGraphql o al metodo di sola lettura executeGraphqlRead.
  • Un'interfaccia specializzata per le operazioni collettive sui dati, che, anziché metodi executeGraphql generici, espone metodi dedicati per le operazioni di mutazione: insert, insertMany, upsert e upsertMany.

Gestire i dati con gli SDK generati

Generi gli SDK Admin dalle definizioni gql nello stesso modo in cui generi gli SDK client.

L'SDK Admin generato contiene interfacce e funzioni che corrispondono alle definizioni gql, che puoi utilizzare per eseguire operazioni sul database. Ad esempio, supponiamo di aver generato un SDK per un database di brani, insieme a una query, 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 } }
);

In alternativa, per specificare una configurazione del connettore:

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

Utilizzare l'identità di un utente non autenticato

Gli SDK Admin sono progettati per essere eseguiti da ambienti attendibili e pertanto hanno accesso illimitato ai database.

Quando esegui operazioni pubbliche con l'SDK Admin, devi evitare di eseguire l'operazione con privilegi di amministratore completi (seguendo il principio del privilegio minimo). In alternativa, devi eseguire l'operazione come utente impersonato (vedi la sezione successiva) o come utente non autenticato impersonato. Gli utenti non autenticati possono eseguire solo le operazioni contrassegnate come PUBLIC.

Nell'esempio precedente, la query getSongs viene eseguita come utente non autenticato.

Utilizzare l'identità di un altro utente

Puoi anche eseguire operazioni per conto di utenti specifici passando parte o tutto un token Firebase Authentication nell'opzione impersonate; come minimo, devi specificare l'ID utente dell'utente nella rivendicazione secondaria. (Questo è lo stesso valore del valore del server auth.uida cui puoi fare riferimento nelle operazioni GraphQL Data Connect)

Quando utilizzi l'identità di un altro utente, l'operazione avrà esito positivo solo se i dati utente forniti superano i controlli di autenticazione specificati nella definizione GraphQL.

Se chiami l'SDK generato da un endpoint accessibile pubblicamente, è fondamentale che l'endpoint richieda l'autenticazione e che tu convalidi l'integrità del token di autenticazione prima di utilizzarlo per utilizzare l'identità di un altro utente.

Quando utilizzi le Cloud Functions chiamabili, il token di autenticazione viene verificato automaticamente e puoi utilizzarlo come nell'esempio seguente:

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

    // ...
});

In caso contrario, utilizza il Admin SDK's verifyIdToken method per convalidare e decodificare il token di autenticazione. Ad esempio, supponiamo che l'endpoint sia implementato come una semplice funzione HTTP e che tu abbia passato il token Firebase Authentication all'endpoint utilizzando l'intestazione authorization, come standard:

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

    // ...
});

Solo quando esegui attività amministrative reali, come la migrazione dei dati, da un ambiente sicuro e non accessibile pubblicamente, devi specificare un ID utente che non proviene da un'origine verificabile:

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

Eseguire con accesso illimitato

Se stai eseguendo un'operazione che richiede autorizzazioni a livello di amministratore, ometti il parametro impersonate dalla chiamata:

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

Un'operazione chiamata in questo modo ha accesso completo al database. Se hai query o mutazioni destinate solo a scopi di amministrazione, devi definirle con la direttiva @auth(level: NO_ACCESS). In questo modo, solo i chiamanti a livello di amministratore possono eseguire queste operazioni.

Gestire i dati con i metodi executeGraphql

Se devi eseguire operazioni una tantum per le quali non hai definito gql mutazioni o query, puoi utilizzare il metodo executeGraphql o il metodo executeGraphqlRead di sola lettura.

Utilizzare l'identità di un utente non autenticato

Quando esegui operazioni pubbliche con l'SDK Admin, devi evitare di eseguire l'operazione con privilegi di amministratore completi (seguendo il principio del privilegio minimo). In alternativa, devi eseguire l'operazione come utente impersonato (vedi la sezione successiva) o come utente non autenticato impersonato. Gli utenti non autenticati possono eseguire solo le operazioni contrassegnate come 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);

Utilizzare l'identità di un altro utente

Esistono anche casi d'uso in cui vuoi che gli script modifichino i dati utente in base a credenziali limitate, per conto di un utente specifico. Questo approccio rispetta il principio del privilegio minimo.

Per utilizzare questa interfaccia, raccogli le informazioni da un token di autenticazione JWT personalizzato che segue il formato del token Authentication. Consulta anche la guida sui token personalizzati.

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

Utilizzare le credenziali amministrative

Se stai eseguendo un'operazione che richiede autorizzazioni a livello di amministratore, ometti il parametro impersonate dalla chiamata:

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

Un'operazione chiamata in questo modo ha accesso completo al database. Se hai query o mutazioni destinate solo a scopi di amministrazione, devi definirle con la direttiva @auth(level: NO_ACCESS). In questo modo, solo i chiamanti a livello di amministratore possono eseguire queste operazioni.

Eseguire operazioni collettive sui dati

Firebase ti consiglia di utilizzare il Admin SDK per le operazioni collettive sui dati nei database di produzione.

L'SDK fornisce i seguenti metodi per lavorare con i dati collettivi. Dai parametri forniti, ogni metodo costruisce ed esegue una mutazione 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);

Note sul rendimento per le operazioni collettive

Ogni richiesta al backend comporterà un round trip a Cloud SQL, quindi maggiore è il batch, maggiore è la velocità effettiva.

Tuttavia, maggiore è la dimensione del batch, più lunga è l'istruzione SQL generata. Quando viene raggiunto il limite di lunghezza dell'istruzione SQL PostgreSQL, si verifica un errore.

In pratica, sperimenta per trovare la dimensione del batch appropriata per il tuo carico di lavoro.

Passaggi successivi