Utilizzare gli SDK Admin generati

Firebase SQL Connect gli SDK di amministrazione 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 di amministrazione personalizzato in parallelo alla progettazione di schemi, query e mutazioni di cui esegui il deployment nel servizio SQL 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 SQL Connect query e mutazioni non vengono inviate dai client al momento della richiesta. Quando viene eseguito il deployment, le operazioni di SQL 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

  • Scopri di più sulla progettazione di schemi, query e mutazioni SQL Connect. In un flusso di lavoro tipico, li sviluppi in parallelo al codice dell'applicazione, inclusi tutti i servizi che utilizzano gli SDK di amministrazione.
  • Installa l'interfaccia a riga di comando di Firebase.

  • Includi l'SDK Admin per Node.js come dipendenza ovunque prevedi di chiamare gli SDK di amministrazione generati.

Generare SDK di amministrazione

Dopo aver creato gli schemi, le query e le mutazioni di SQL Connect, puoi generare un SDK di amministrazione 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 SQL 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 di amministrazione

L'SDK di amministrazione generato contiene interfacce e funzioni che corrispondono alle definizioni gql, che puoi utilizzare per eseguire operazioni sul database. Supponiamo, ad esempio, 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 di amministrazione sono progettati per essere eseguiti da ambienti attendibili e pertanto hanno accesso illimitato ai database.

Quando esegui operazioni pubbliche con l'SDK di amministrazione, devi evitare di eseguire l'operazione 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 parte o tutto 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 valore del server auth.uid a cui puoi fare riferimento nelle operazioni GraphQL SQL 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 impersonare 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. Supponiamo, ad esempio, che l'endpoint sia implementato come una semplice funzione HTTP e che tu abbia passato il Firebase Authentication token all'endpoint utilizzando l'authorization intestazione, 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 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.