Catch up on highlights from Firebase at Google I/O 2023. Learn more

Crea token personalizzati

Firebase ti offre il controllo completo sull'autenticazione consentendoti di autenticare utenti o dispositivi utilizzando JSON Web Token (JWT) sicuri. Generi questi token sul tuo server, li passi di nuovo a un dispositivo client e poi li usi per l'autenticazione tramite il metodo signInWithCustomToken() .

Per ottenere ciò, è necessario creare un endpoint del server che accetti le credenziali di accesso, ad esempio un nome utente e una password, e, se le credenziali sono valide, restituisca un JWT personalizzato. Il JWT personalizzato restituito dal tuo server può quindi essere utilizzato da un dispositivo client per l'autenticazione con Firebase ( iOS+ , Android , Web ). Una volta autenticata, questa identità verrà utilizzata per l'accesso ad altri servizi Firebase, come Firebase Realtime Database e Cloud Storage. Inoltre, i contenuti del JWT saranno disponibili nell'oggetto auth nelle regole di sicurezza del database in tempo reale e nell'oggetto request.auth nelle regole di sicurezza di Cloud Storage .

Puoi creare un token personalizzato con Firebase Admin SDK oppure puoi utilizzare una libreria JWT di terze parti se il tuo server è scritto in una lingua che Firebase non supporta in modo nativo.

Prima di iniziare

I token personalizzati sono JWT firmati in cui la chiave privata utilizzata per la firma appartiene a un account di servizio Google. Esistono diversi modi per specificare l'account di servizio Google che deve essere utilizzato dall'SDK Firebase Admin per firmare i token personalizzati:

  • Utilizzo di un file JSON dell'account di servizio : questo metodo può essere utilizzato in qualsiasi ambiente, ma richiede di creare un pacchetto di un file JSON dell'account di servizio insieme al codice. È necessario prestare particolare attenzione per garantire che il file JSON dell'account di servizio non sia esposto a parti esterne.
  • Consentire all'Admin SDK di rilevare un account di servizio : questo metodo può essere utilizzato in ambienti gestiti da Google come Google Cloud Functions e App Engine. Potrebbe essere necessario configurare alcune autorizzazioni aggiuntive tramite Google Cloud Console.
  • Utilizzo di un ID account di servizio : se utilizzato in un ambiente gestito da Google, questo metodo firmerà i token utilizzando la chiave dell'account di servizio specificato. Tuttavia, utilizza un servizio Web remoto e potrebbe essere necessario configurare autorizzazioni aggiuntive per questo account di servizio tramite Google Cloud Console.

Utilizzo di un file JSON dell'account di servizio

I file JSON degli account di servizio contengono tutte le informazioni corrispondenti agli account di servizio (inclusa la chiave privata RSA). Possono essere scaricati dalla console Firebase. Segui le istruzioni di configurazione dell'Admin SDK per ulteriori informazioni su come inizializzare l'Admin SDK con un file JSON dell'account di servizio.

Questo metodo di inizializzazione è adatto a un'ampia gamma di distribuzioni di Admin SDK. Consente inoltre ad Admin SDK di creare e firmare token personalizzati in locale, senza effettuare chiamate API remote. Lo svantaggio principale di questo approccio è che richiede di impacchettare un file JSON dell'account di servizio insieme al codice. Si noti inoltre che la chiave privata in un file JSON dell'account di servizio è un'informazione riservata e occorre prestare particolare attenzione per mantenerla riservata. In particolare, evita di aggiungere file JSON dell'account di servizio al controllo della versione pubblica.

Consentire all'Admin SDK di rilevare un account di servizio

Se il tuo codice viene distribuito in un ambiente gestito da Google, Admin SDK può tentare di rilevare automaticamente un mezzo per firmare token personalizzati:

  • Se il codice viene distribuito nell'ambiente standard di App Engine per Java, Python o Go, Admin SDK può utilizzare il servizio App Identity presente in tale ambiente per firmare token personalizzati. Il servizio App Identity firma i dati utilizzando un account di servizio fornito per la tua app da Google App Engine.

  • Se il tuo codice viene distribuito in un altro ambiente gestito (ad es. Google Cloud Functions, Google Compute Engine), l'SDK Firebase Admin può rilevare automaticamente una stringa ID dell'account di servizio dal server di metadati locale . L'ID dell'account di servizio rilevato viene quindi utilizzato insieme al servizio IAM per firmare i token in remoto.

Per utilizzare questi metodi di firma, inizializza l'SDK con le credenziali predefinite dell'applicazione Google e non specificare una stringa ID dell'account di servizio:

Node.js

initializeApp();

Giava

FirebaseApp.initializeApp();

Pitone

default_app = firebase_admin.initialize_app()

Andare

app, err := firebase.NewApp(context.Background(), nil)
if err != nil {
	log.Fatalf("error initializing app: %v\n", err)
}

C#

FirebaseApp.Create();

Per testare lo stesso codice in locale, scarica un file JSON dell'account di servizio e imposta la variabile di ambiente GOOGLE_APPLICATION_CREDENTIALS in modo che faccia riferimento a esso.

Se l'SDK Firebase Admin deve rilevare una stringa ID dell'account di servizio, lo fa quando il tuo codice crea un token personalizzato per la prima volta. Il risultato viene memorizzato nella cache e riutilizzato per le successive operazioni di firma dei token. L'ID dell'account di servizio rilevato automaticamente è in genere uno degli account di servizio predefiniti forniti da Google Cloud:

Proprio come con gli ID account di servizio specificati in modo esplicito, gli ID account di servizio con individuazione automatica devono disporre dell'autorizzazione iam.serviceAccounts.signBlob affinché la creazione del token personalizzato funzioni. Potrebbe essere necessario utilizzare la sezione IAM e amministrazione di Google Cloud Console per concedere agli account di servizio predefiniti le autorizzazioni necessarie. Vedere la sezione sulla risoluzione dei problemi di seguito per ulteriori dettagli.

Utilizzo di un ID account di servizio

Per mantenere la coerenza tra le varie parti dell'applicazione, puoi specificare un ID account di servizio le cui chiavi verranno utilizzate per firmare i token durante l'esecuzione in un ambiente gestito da Google. Ciò può rendere le policy IAM più semplici e sicure ed evitare di dover includere il file JSON dell'account di servizio nel codice.

L'ID dell'account di servizio si trova in Google Cloud Console o nel campo client_email di un file JSON dell'account di servizio scaricato. Gli ID account di servizio sono indirizzi e-mail con il seguente formato: <client-id>@<project-id>.iam.gserviceaccount.com . Identificano in modo univoco gli account di servizio nei progetti Firebase e Google Cloud.

Per creare token personalizzati utilizzando un ID account di servizio separato, inizializza l'SDK come mostrato di seguito:

Node.js

initializeApp({
  serviceAccountId: 'my-client-id@my-project-id.iam.gserviceaccount.com',
});

Giava

FirebaseOptions options = FirebaseOptions.builder()
    .setCredentials(GoogleCredentials.getApplicationDefault())
    .setServiceAccountId("my-client-id@my-project-id.iam.gserviceaccount.com")
    .build();
FirebaseApp.initializeApp(options);

Pitone

options = {
    'serviceAccountId': 'my-client-id@my-project-id.iam.gserviceaccount.com',
}
firebase_admin.initialize_app(options=options)

Andare

conf := &firebase.Config{
	ServiceAccountID: "my-client-id@my-project-id.iam.gserviceaccount.com",
}
app, err := firebase.NewApp(context.Background(), conf)
if err != nil {
	log.Fatalf("error initializing app: %v\n", err)
}

C#

FirebaseApp.Create(new AppOptions()
{
    Credential = GoogleCredential.GetApplicationDefault(),
    ServiceAccountId = "my-client-id@my-project-id.iam.gserviceaccount.com",
});

Gli ID dell'account di servizio non sono informazioni riservate e pertanto la loro esposizione è irrilevante. Tuttavia, per firmare token personalizzati con l'account di servizio specificato, l'SDK Firebase Admin deve richiamare un servizio remoto. Inoltre, devi anche assicurarti che l'account di servizio utilizzato dall'Admin SDK per effettuare questa chiamata, in genere {project-name}@appspot.gserviceaccount.com , disponga dell'autorizzazione iam.serviceAccounts.signBlob . Vedere la sezione sulla risoluzione dei problemi di seguito per ulteriori dettagli.

Crea token personalizzati utilizzando l'SDK Firebase Admin

L'SDK Firebase Admin ha un metodo integrato per la creazione di token personalizzati. Come minimo, devi fornire un uid , che può essere qualsiasi stringa ma dovrebbe identificare in modo univoco l'utente o il dispositivo che stai autenticando. Questi token scadono dopo un'ora.

Node.js

const uid = 'some-uid';

getAuth()
  .createCustomToken(uid)
  .then((customToken) => {
    // Send token back to client
  })
  .catch((error) => {
    console.log('Error creating custom token:', error);
  });

Giava

String uid = "some-uid";

String customToken = FirebaseAuth.getInstance().createCustomToken(uid);
// Send token back to client

Pitone

uid = 'some-uid'

custom_token = auth.create_custom_token(uid)

Andare

client, err := app.Auth(context.Background())
if err != nil {
	log.Fatalf("error getting Auth client: %v\n", err)
}

token, err := client.CustomToken(ctx, "some-uid")
if err != nil {
	log.Fatalf("error minting custom token: %v\n", err)
}

log.Printf("Got custom token: %v\n", token)

C#

var uid = "some-uid";

string customToken = await FirebaseAuth.DefaultInstance.CreateCustomTokenAsync(uid);
// Send token back to client

È inoltre possibile specificare facoltativamente attestazioni aggiuntive da includere nel token personalizzato. Ad esempio, di seguito, al token personalizzato è stato aggiunto un campo premiumAccount , che sarà disponibile negli oggetti auth / request.auth nelle tue regole di sicurezza:

Node.js

const userId = 'some-uid';
const additionalClaims = {
  premiumAccount: true,
};

getAuth()
  .createCustomToken(userId, additionalClaims)
  .then((customToken) => {
    // Send token back to client
  })
  .catch((error) => {
    console.log('Error creating custom token:', error);
  });

Giava

String uid = "some-uid";
Map<String, Object> additionalClaims = new HashMap<String, Object>();
additionalClaims.put("premiumAccount", true);

String customToken = FirebaseAuth.getInstance()
    .createCustomToken(uid, additionalClaims);
// Send token back to client

Pitone

uid = 'some-uid'
additional_claims = {
    'premiumAccount': True
}

custom_token = auth.create_custom_token(uid, additional_claims)

Andare

client, err := app.Auth(context.Background())
if err != nil {
	log.Fatalf("error getting Auth client: %v\n", err)
}

claims := map[string]interface{}{
	"premiumAccount": true,
}

token, err := client.CustomTokenWithClaims(ctx, "some-uid", claims)
if err != nil {
	log.Fatalf("error minting custom token: %v\n", err)
}

log.Printf("Got custom token: %v\n", token)

C#

var uid = "some-uid";
var additionalClaims = new Dictionary<string, object>()
{
    { "premiumAccount", true },
};

string customToken = await FirebaseAuth.DefaultInstance
    .CreateCustomTokenAsync(uid, additionalClaims);
// Send token back to client

Nomi di token personalizzati riservati

Accedi utilizzando token personalizzati sui client

Dopo aver creato un token personalizzato, devi inviarlo all'app client. L'app client esegue l'autenticazione con il token personalizzato chiamando signInWithCustomToken() :

iOS+

Obiettivo-C
[[FIRAuth auth] signInWithCustomToken:customToken
                           completion:^(FIRAuthDataResult * _Nullable authResult,
                                        NSError * _Nullable error) {
  // ...
}];
Rapido
Auth.auth().signIn(withCustomToken: customToken ?? "") { user, error in
  // ...
}

Androide

mAuth.signInWithCustomToken(mCustomToken)
        .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isSuccessful()) {
                    // Sign in success, update UI with the signed-in user's information
                    Log.d(TAG, "signInWithCustomToken:success");
                    FirebaseUser user = mAuth.getCurrentUser();
                    updateUI(user);
                } else {
                    // If sign in fails, display a message to the user.
                    Log.w(TAG, "signInWithCustomToken:failure", task.getException());
                    Toast.makeText(CustomAuthActivity.this, "Authentication failed.",
                            Toast.LENGTH_SHORT).show();
                    updateUI(null);
                }
            }
        });

Unità

auth.SignInWithCustomTokenAsync(custom_token).ContinueWith(task => {
  if (task.IsCanceled) {
    Debug.LogError("SignInWithCustomTokenAsync was canceled.");
    return;
  }
  if (task.IsFaulted) {
    Debug.LogError("SignInWithCustomTokenAsync encountered an error: " + task.Exception);
    return;
  }

  Firebase.Auth.AuthResult result = task.Result;
  Debug.LogFormat("User signed in successfully: {0} ({1})",
      result.User.DisplayName, result.User.UserId);
});

C++

firebase::Future<firebase::auth::AuthResult> result =
    auth->SignInWithCustomToken(custom_token);

API con spazio dei nomi Web

firebase.auth().signInWithCustomToken(token)
  .then((userCredential) => {
    // Signed in
    var user = userCredential.user;
    // ...
  })
  .catch((error) => {
    var errorCode = error.code;
    var errorMessage = error.message;
    // ...
  });

API modulare Web

import { getAuth, signInWithCustomToken } from "firebase/auth";

const auth = getAuth();
signInWithCustomToken(auth, token)
  .then((userCredential) => {
    // Signed in
    const user = userCredential.user;
    // ...
  })
  .catch((error) => {
    const errorCode = error.code;
    const errorMessage = error.message;
    // ...
  });

Se l'autenticazione ha esito positivo, l'utente accederà ora all'app client con l'account specificato uid incluso nel token personalizzato. Se quell'account non esisteva in precedenza, verrà creato un record per quell'utente.

Analogamente ad altri metodi di accesso (come signInWithEmailAndPassword() e signInWithCredential() ) l'oggetto auth nelle regole di sicurezza del database in tempo reale e l'oggetto request.auth nelle regole di sicurezza di Cloud Storage verranno popolati con l' uid dell'utente . In questo caso, l' uid sarà quello specificato durante la generazione del token personalizzato.

Regole della banca dati

{
  "rules": {
    "adminContent": {
      ".read": "auth.uid === 'some-uid'"
    }
  }
}

Regole di archiviazione

service firebase.storage {
  match /b/<your-firebase-storage-bucket>/o {
    match /adminContent/{filename} {
      allow read, write: if request.auth != null && request.auth.uid == "some-uid";
    }
  }
}

Se il token personalizzato contiene attestazioni aggiuntive, è possibile farvi riferimento dall'oggetto auth.token (Firebase Realtime Database) o request.auth.token (Cloud Storage) nelle tue regole:

Regole della banca dati

{
  "rules": {
    "premiumContent": {
      ".read": "auth.token.premiumAccount === true"
    }
  }
}

Regole di archiviazione

service firebase.storage {
  match /b/<your-firebase-storage-bucket>/o {
    match /premiumContent/{filename} {
      allow read, write: if request.auth.token.premiumAccount == true;
    }
  }
}

Crea token personalizzati utilizzando una libreria JWT di terze parti

Se il tuo backend è in una lingua che non dispone di un SDK Firebase Admin ufficiale, puoi comunque creare manualmente token personalizzati. Innanzitutto, trova una libreria JWT di terze parti per la tua lingua. Quindi, usa quella libreria JWT per coniare un JWT che includa le seguenti affermazioni:

Reclami token personalizzati
alg Algoritmo "RS256"
iss Emittente L'indirizzo email dell'account di servizio del tuo progetto
sub Soggetto L'indirizzo email dell'account di servizio del tuo progetto
aud Pubblico "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat Rilasciato al momento L'ora corrente, in secondi dall'epoca UNIX
exp Data di scadenza L'ora, in secondi dall'epoca UNIX, in cui scade il token. Può essere un massimo di 3600 secondi dopo l' iat .
Nota: questo controlla solo l'ora in cui il token personalizzato stesso scade. Tuttavia, una volta che l'utente effettua l'accesso utilizzando signInWithCustomToken() , rimarrà connesso al dispositivo fino a quando la sua sessione non viene invalidata o l'utente si disconnette.
uid L'identificatore univoco dell'utente che ha eseguito l'accesso deve essere una stringa di lunghezza compresa tra 1 e 128 caratteri inclusi. uid più corti offrono prestazioni migliori.
claims (facoltativo) Attestazioni personalizzate facoltative da includere nelle variabili auth / request.auth delle regole di sicurezza

Di seguito sono riportati alcuni esempi di implementazioni di come creare token personalizzati in una varietà di lingue non supportate da Firebase Admin SDK:

PHP

Utilizzando php-jwt :

// Requires: composer require firebase/php-jwt
use Firebase\JWT\JWT;

// Get your service account's email address and private key from the JSON key file
$service_account_email = "abc-123@a-b-c-123.iam.gserviceaccount.com";
$private_key = "-----BEGIN PRIVATE KEY-----...";

function create_custom_token($uid, $is_premium_account) {
  global $service_account_email, $private_key;

  $now_seconds = time();
  $payload = array(
    "iss" => $service_account_email,
    "sub" => $service_account_email,
    "aud" => "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
    "iat" => $now_seconds,
    "exp" => $now_seconds+(60*60),  // Maximum expiration time is one hour
    "uid" => $uid,
    "claims" => array(
      "premium_account" => $is_premium_account
    )
  );
  return JWT::encode($payload, $private_key, "RS256");
}

Rubino

Usando ruby-jwt :

require "jwt"

# Get your service account's email address and private key from the JSON key file
$service_account_email = "service-account@my-project-abc123.iam.gserviceaccount.com"
$private_key = OpenSSL::PKey::RSA.new "-----BEGIN PRIVATE KEY-----\n..."

def create_custom_token(uid, is_premium_account)
  now_seconds = Time.now.to_i
  payload = {:iss => $service_account_email,
             :sub => $service_account_email,
             :aud => "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
             :iat => now_seconds,
             :exp => now_seconds+(60*60), # Maximum expiration time is one hour
             :uid => uid,
             :claims => {:premium_account => is_premium_account}}
  JWT.encode payload, $private_key, "RS256"
end

Dopo aver creato il token personalizzato, invialo all'app client da utilizzare per l'autenticazione con Firebase. Vedere gli esempi di codice sopra per come eseguire questa operazione.

Risoluzione dei problemi

Questa sezione delinea alcuni problemi comuni che gli sviluppatori possono incontrare durante la creazione di token personalizzati e come risolverli.

API IAM non abilitata

Se stai specificando un ID account di servizio per la firma dei token, potresti ricevere un errore simile al seguente:

Identity and Access Management (IAM) API has not been used in project
1234567890 before or it is disabled. Enable it by visiting
https://console.developers.google.com/apis/api/iam.googleapis.com/overview?project=1234567890
then retry. If you enabled this API recently, wait a few minutes for the action
to propagate to our systems and retry.

L'SDK Firebase Admin utilizza l' API IAM per firmare i token. Questo errore indica che l'API IAM non è attualmente abilitata per il tuo progetto Firebase. Apri il collegamento nel messaggio di errore in un browser Web e fai clic sul pulsante "Abilita API" per abilitarlo per il tuo progetto.

L'account di servizio non dispone delle autorizzazioni necessarie

Se l'account di servizio con cui è in esecuzione l'SDK Firebase Admin non dispone dell'autorizzazione iam.serviceAccounts.signBlob , potresti ricevere un messaggio di errore simile al seguente:

Permission iam.serviceAccounts.signBlob is required to perform this operation
on service account projects/-/serviceAccounts/{your-service-account-id}.

Il modo più semplice per risolvere questo problema è concedere il ruolo IAM "Service Account Token Creator" all'account di servizio in questione, in genere {project-name}@appspot.gserviceaccount.com :

  1. Apri la pagina IAM e di amministrazione in Google Cloud Console.
  2. Seleziona il tuo progetto e fai clic su "Continua".
  3. Fai clic sull'icona di modifica corrispondente all'account di servizio che desideri aggiornare.
  4. Fare clic su "Aggiungi un altro ruolo".
  5. Digita "Service Account Token Creator" nel filtro di ricerca e selezionalo dai risultati.
  6. Fare clic su "Salva" per confermare l'assegnazione del ruolo.

Consulta la documentazione IAM per ulteriori dettagli su questo processo o scopri come aggiornare i ruoli utilizzando gli strumenti a riga di comando di gcloud.

Impossibile determinare l'account di servizio

Se ricevi un messaggio di errore simile al seguente, l'SDK Firebase Admin non è stato inizializzato correttamente.

Failed to determine service account ID. Initialize the SDK with service account
credentials or specify a service account ID with iam.serviceAccounts.signBlob
permission.

Se ti affidi all'SDK per rilevare automaticamente un ID account di servizio, assicurati che il codice venga implementato in un ambiente Google gestito con un server di metadati. In caso contrario, assicurati di specificare il file JSON dell'account di servizio o l'ID dell'account di servizio durante l'inizializzazione dell'SDK.