Utilizzare gli SDK Admin generati

Gli SDK Admin di Firebase Data Connect consentono di chiamare query e mutazioni da ambienti attendibili come Cloud Functions, backend personalizzati o la tua workstation. Analogamente alla generazione di SDK per le app client, puoi generare un SDK Admin personalizzato in parallelo alla progettazione di schemi, query e mutazioni di cui esegui il deployment nel servizio Data Connect. Dopodiché, puoi integrare i metodi di questo SDK nella logica di backend o negli script di amministrazione.

Come abbiamo già detto, è importante notare che le Data Connect query e le mutazioni non vengono inviate dai client al momento della richiesta. Quando viene eseguito il deployment, le operazioni Data Connect vengono archiviate sul server, come Cloud Functions. Ciò significa che ogni volta che esegui il deployment delle modifiche alle query e alle mutazioni, devi anche rigenerare gli SDK Admin ed eseguire di nuovo il deployment di tutti i servizi che li utilizzano.

Prima di iniziare

Generare gli SDK Admin

Dopo aver creato gli schemi, le query e le mutazioni di Data Connect, puoi generare un SDK Admin corrispondente:

  1. Apri o crea un file connector.yaml e aggiungi una definizione adminNodeSdk:

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

    Il file connector.yaml si trova in genere nella stessa directory dei file GraphQL (.gql) che contengono le definizioni di query e mutazioni. Se hai già generato gli SDK client, questo file è già stato creato.

  2. Genera l'SDK.

    Se hai installato l'estensione Data Connect di VS Code, gli SDK generati saranno sempre aggiornati.

    In caso contrario, utilizza l'interfaccia a riga di comando di Firebase:

    firebase dataconnect:sdk:generate

    In alternativa, per rigenerare automaticamente gli SDK quando aggiorni i file gql:

    firebase dataconnect:sdk:generate --watch

Eseguire operazioni da un SDK Admin

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

Impersonare 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 eseguirle con privilegi di amministratore completi (seguendo il principio del privilegio minimo). Devi invece 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.

Impersonare un utente

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

Quando impersoni un 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 simulare l'identità di un utente.

Quando utilizzi chiamabili Cloud Functions, 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 Firebase Authentication token all'endpoint utilizzando l'intestazione authorization, come di consueto:

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 vere e proprie, come la migrazione dei dati, da un ambiente sicuro e non accessibile pubblicamente, devi specificare un ID utente che non proviene da una fonte 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 essere utilizzate per scopi amministrativi, devi definirle con la direttiva @auth(level: NO_ACCESS). In questo modo, solo i chiamanti a livello di amministratore possono eseguire queste operazioni.