Crea token personalizzati

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

A tale scopo, è necessario creare un endpoint 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 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 accedere ad altri servizi Firebase, come Firebase Realtime Database e Cloud Storage. Inoltre, il contenuto del JWT sarà disponibile nell'oggetto auth nelle regole di sicurezza del database Realtime e nell'oggetto request.auth nelle regole di sicurezza del 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 un linguaggio 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 del servizio Google che deve essere utilizzato dall'SDK Admin Firebase per firmare token personalizzati:

  • Utilizzo di un file JSON dell'account di servizio : questo metodo può essere utilizzato in qualsiasi ambiente, ma richiede la creazione del 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 soggetti esterni.
  • Consentire all'SDK Admin 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 la console Google Cloud.
  • 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 la console Google Cloud.

Utilizzando un file JSON dell'account di servizio

I file JSON dell'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 per un'ampia gamma di distribuzioni di Admin SDK. Inoltre, consente all'Admin SDK di creare e firmare token personalizzati localmente, senza effettuare chiamate API remote. Lo svantaggio principale di questo approccio è che richiede la creazione del pacchetto di un file JSON dell'account di servizio insieme al codice. Tieni inoltre presente che la chiave privata nel file JSON di un account di servizio è un'informazione riservata e deve essere prestata particolare attenzione per mantenerla riservata. In particolare, astieniti dall'aggiungere file JSON dell'account di servizio al controllo della versione pubblica.

Consentire all'SDK Admin di rilevare un account di servizio

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

  • Se il tuo 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 esempio Google Cloud Functions, Google Compute Engine), l'SDK Admin Firebase può individuare 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 Google Application Default 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 localmente, scarica un file JSON dell'account di servizio e imposta la variabile di ambiente GOOGLE_APPLICATION_CREDENTIALS in modo che punti ad esso.

Se l'SDK Admin Firebase deve individuare una stringa dell'ID dell'account di servizio, lo fa quando il 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 esplicitamente, gli ID account di servizio rilevati automaticamente devono disporre dell'autorizzazione iam.serviceAccounts.signBlob affinché la creazione del token personalizzato funzioni. Potrebbe essere necessario utilizzare la sezione IAM e di amministrazione della console Google Cloud per concedere agli account di servizio predefiniti le autorizzazioni necessarie. Per ulteriori dettagli, vedere la sezione relativa alla risoluzione dei problemi di seguito.

Utilizzando un ID account di servizio

Per mantenere la coerenza tra le varie parti della tua 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 è disponibile nella console Google Cloud o nel campo client_email di un file JSON dell'account di servizio scaricato. Gli ID dell'account di servizio sono indirizzi e-mail che hanno 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 degli account di servizio non sono informazioni riservate e pertanto la loro esposizione non ha conseguenze. Tuttavia, per firmare token personalizzati con l'account di servizio specificato, Firebase Admin SDK deve richiamare un servizio remoto. Inoltre, devi anche assicurarti che l'account di servizio utilizzato dall'SDK Admin per effettuare questa chiamata, in genere {project-name}@appspot.gserviceaccount.com , disponga dell'autorizzazione iam.serviceAccounts.signBlob . Per ulteriori dettagli, vedere la sezione relativa alla risoluzione dei problemi di seguito.

Crea token personalizzati utilizzando l'SDK Admin Firebase

Firebase Admin SDK dispone di un metodo integrato per la creazione di token personalizzati. Come minimo, devi fornire un uid , che può essere qualsiasi stringa ma deve 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

Facoltativamente, puoi anche specificare ulteriori attestazioni da includere nel token personalizzato. Ad esempio, di seguito, è stato aggiunto un campo premiumAccount al token personalizzato, 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, dovresti 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) {
  // ...
}];
Veloce
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, il tuo utente avrà ora effettuato l'accesso all'app client con l'account specificato uid incluso nel token personalizzato. Se l'account non esisteva in precedenza, verrà creato un record per quell'utente.

Allo stesso modo degli 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 del cloud storage verranno popolati con l' uid dell'utente . In questo caso, l' uid sarà quello specificato durante la generazione del token personalizzato.

Regole del database

{
  "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 fare riferimento ad esse dall'oggetto auth.token (Firebase Realtime Database) o request.auth.token (Cloud Storage) nelle regole:

Regole del database

{
  "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 Admin Firebase ufficiale, puoi comunque creare manualmente token personalizzati. Innanzitutto, trova una libreria JWT di terze parti per la tua lingua. Quindi, utilizza la libreria JWT per coniare un JWT che includa le seguenti affermazioni:

Rivendicazioni di 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 in tempo 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 massimo 3600 secondi dopo rispetto allo iat .
Nota: questo controlla solo il momento in cui scade il token personalizzato stesso. Tuttavia, una volta effettuato l'accesso da parte di un utente utilizzando signInWithCustomToken() , l'utente rimarrà connesso al dispositivo finché la sua sessione non verrà invalidata o l'utente non si disconnetterà.
uid L'identificatore univoco dell'utente che ha effettuato l'accesso deve essere una stringa di lunghezza compresa tra 1 e 128 caratteri inclusi. uid più brevi offrono prestazioni migliori.
claims (facoltativo) Attestazioni personalizzate facoltative da includere nelle variabili auth / request.auth delle regole di sicurezza

Ecco alcune implementazioni di esempio su come creare token personalizzati in una varietà di lingue che Firebase Admin SDK non supporta:

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 per utilizzarlo per l'autenticazione con Firebase. Vedi gli esempi di codice qui sopra per sapere come eseguire questa operazione.

Risoluzione dei problemi

In questa sezione vengono descritti alcuni problemi comuni che gli sviluppatori potrebbero riscontrare 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 Admin Firebase 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 utilizzato da Firebase Admin SDK 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 nella console Google Cloud.
  2. Seleziona il tuo progetto e clicca 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 la concessione del ruolo.

Fai riferimento alla documentazione IAM per maggiori dettagli su questo processo o scopri come aggiornare i ruoli utilizzando gli strumenti a riga di comando gcloud.

Impossibile determinare l'account di servizio

Se ricevi un messaggio di errore simile al seguente, Firebase Admin SDK 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 il rilevamento automatico dell'ID di un account di servizio, assicurati che il codice sia distribuito 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.