Creazione di token personalizzati

Firebase ti offre il controllo completo sull'autenticazione consentendoti di autenticare utenti o dispositivi utilizzando token web JSON (JWT) sicuri. Generi tu questi token sul tuo server, ritrasmetteli a un dispositivo client e quindi usa di autenticarli con il metodo signInWithCustomToken().

Per farlo, devi 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, restituisce un JWT personalizzato. Il token JWT personalizzato restituito dal server può essere utilizzato da un dispositivo client per l'autenticazione con Firebase (iOS e versioni successive, Android, web). Una volta autenticata, questa identità verrà utilizzata per accedere ad altri servizi Firebase, come Firebase Realtime Database e Cloud Storage. Inoltre, i contenuti del JWT saranno disponibile nell'oggetto auth in Realtime Database Security Rules e request.auth oggetto in Cloud Storage Security Rules.

Puoi creare un token personalizzato con l'SDK Firebase Admin o utilizzare una libreria JWT di terze parti se il tuo server è scritto in un linguaggio non supportato nativamente da Firebase.

Prima di iniziare

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

  • Utilizzo di un file JSON dell'account di servizio: questo metodo può essere utilizzato in qualsiasi ambiente, ma richiede di pacchettizzare un file JSON dell'account di servizio insieme al codice. È necessario prestare particolare attenzione per assicurarsi che il file JSON dell'account di servizio non sia esposto a terze parti.
  • 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. Potresti dover configurare 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 Console Google Cloud.

Utilizzo di un file JSON dell'account di servizio

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

Questo metodo di inizializzazione è adatto a un'ampia gamma di SDK Admin deployment di machine learning. Consente inoltre all'SDK Admin di creare e firmare token personalizzati in locale, senza effettuare chiamate API remote. Lo svantaggio principale di questo approccio è che richiede di pacchettizzare un file JSON dell'account di servizio insieme al codice. Tieni inoltre presente che la chiave privata in un account di servizio Il file JSON contiene informazioni sensibili, pertanto è necessario prestare particolare attenzione confidenzialità. In particolare, evita di aggiungere file JSON degli account di servizio al controllo della versione pubblica.

Consentire all'SDK Admin di rilevare un account di servizio

Se il codice viene implementato in un ambiente gestito da Google, l'SDK Admin può tentare di rilevare automaticamente un modo per firmare i token personalizzati:

  • Se il deployment del codice viene eseguito nell'ambiente standard App Engine per Java, Python o Go, l'SDK Admin può utilizzare Servizio di identità app presenti nell'ambiente per firmare token personalizzati. Il servizio App Identity firma i dati utilizzando un account di servizio di cui è stato eseguito il provisioning per la tua app dall'app Google di ricerca.

  • Se il deployment del codice è stato eseguito in un altro ambiente gestito (ad es. Google Cloud) Functions, Google Compute Engine), l'SDK Admin Firebase può rilevare automaticamente la stringa dell'ID dell'account di servizio server metadati. L'ID dell'account di servizio rilevato viene quindi utilizzato insieme al campo IAM per firmare i token da remoto.

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

Node.js

initializeApp();

Java

FirebaseApp.initializeApp();

Python

default_app = firebase_admin.initialize_app()

Vai

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 il metodo GOOGLE_APPLICATION_CREDENTIALS variabile di ambiente per puntarla.

Se l'SDK Firebase Admin deve scoprire una stringa ID 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 account di servizio rilevato automaticamente è in genere uno degli account di servizio predefiniti forniti dal Google Cloud:

Proprio come per gli ID account di servizio specificati esplicitamente, il servizio con individuazione automatica gli ID account devono disporre dell'autorizzazione iam.serviceAccounts.signBlob per la creazione di token personalizzati. Potresti dover utilizzare la sezione IAM e amministrazione della console Google Cloud per concedere agli account di servizio predefiniti le autorizzazioni necessarie. Per ulteriori dettagli, consulta la sezione relativa alla risoluzione dei problemi riportata di seguito.

Utilizzo di un ID account di servizio

Per mantenere la coerenza tra le varie parti dell'applicazione, puoi specifica un ID account di servizio le cui chiavi verranno utilizzate per firmare i token durante l'esecuzione in un ambiente gestito da Google. In questo modo, i criteri IAM possono essere semplificati e resi più sicuri ed è possibile 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 account di servizio sono indirizzi email 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',
});

Java

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

Python

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

Vai

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 sensibili e pertanto la loro esposizione è irrilevante. Tuttavia, per firmare i token personalizzati con l'account di servizio specificato, l'SDK Firebase Admin deve invocare un servizio remoto. Devi inoltre assicurarti che l'account di servizio di SDK Admin per effettuare questa chiamata -di solito {project-name}@appspot.gserviceaccount.com- ha il iam.serviceAccounts.signBlob autorizzazione. Per ulteriori dettagli, consulta la sezione sulla risoluzione dei problemi di seguito.

Creare token personalizzati utilizzando l'SDK Admin Firebase

L'SDK Firebase Admin dispone di un metodo integrato per la creazione di token personalizzati. Alle ore minimo, devi fornire un valore uid, che può essere qualsiasi stringa ma 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);
  });

Java

String uid = "some-uid";

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

Python

uid = 'some-uid'

custom_token = auth.create_custom_token(uid)

Vai

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 specificare ulteriori rivendicazioni da includere nel di accesso. Ad esempio, di seguito è stato aggiunto un campo premiumAccount al token personalizzato, che sarà disponibile negli oggetti auth/request.auth nelle 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);
  });

Java

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

Python

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

custom_token = auth.create_custom_token(uid, additional_claims)

Vai

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 dei token personalizzati riservati

Accedere utilizzando token personalizzati sui client

Dopo aver creato un token personalizzato, devi inviarlo all'app client. L'app client si autentica con il token personalizzato chiamandosignInWithCustomToken():

iOS+

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

Android

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

Unity

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

Web

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

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 al tuo l'app client con l'account specificato da uid incluso nel di accesso. Se l'account non esisteva in precedenza, verrà creato un record per quell'utente è stato creato.

Come per altri metodi di accesso (ad esempio signInWithEmailAndPassword() e signInWithCredential()), l'oggetto auth nel Realtime Database Security Rules e l'oggetto request.auth nel Cloud Storage Security Rules verranno compilati con il uid dell'utente. In questo caso, sarà uid 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 richieste aggiuntive, è possibile farvi riferimento auth.token (Firebase Realtime Database) o request.auth.token (Cloud Storage) nelle tue 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;
    }
  }
}

Creare token personalizzati utilizzando una libreria JWT di terze parti

Se il backend è in una lingua che non ha un amministratore Firebase ufficiale SDK, puoi comunque creare manualmente token personalizzati. Innanzitutto, trova una libreria JWT di terze parti per la tua lingua. Poi, utilizza la libreria JWT per coniare un JWT che includa le seguenti attestazioni:

Attestazioni token personalizzati
alg Algoritmo "RS256"
iss Emittente Indirizzo email dell'account di servizio del progetto
sub Oggetto Indirizzo email dell'account di servizio del progetto
aud Pubblico "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat Data/ora di emissione L'ora attuale, in secondi dall'epoca di UNIX.
exp Scadenza Il tempo, in secondi, dall'epoca UNIX, in cui scade il token. Deve essere al massimo 3600 secondi dopo il iat.
Nota: questo controlla solo la data di scadenza del token personalizzato stesso. Tuttavia, una volta che un utente esegue l'accesso utilizzando signInWithCustomToken(), manterranno l'accesso al dispositivo finché la sessione non viene invalidata o l'utente si disconnette.
uid L'identificatore univoco dell'utente che ha eseguito l'accesso deve essere una stringa, compresa tra Lunghezza compresa tra 1 e 128 caratteri, inclusi. I uid più brevi offrono un rendimento migliore.
claims (facoltativo) Dichiarazioni personalizzate facoltative da includere nelle variabili auth/ request.auth delle regole di sicurezza

Di seguito sono riportati alcuni esempi di implementazione di come creare token personalizzati in una serie di lingue non supportate dall'SDK Firebase Admin:

PHP

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

Ruby

In 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 autenticarsi con Firebase. Per scoprire come, consulta gli esempi di codice riportati sopra.

Risoluzione dei problemi

Questa sezione illustra alcuni problemi comuni che gli sviluppatori potrebbero riscontrare quando creando token personalizzati e spiegando come risolverli.

API IAM non abilitata

Se specifichi 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 in un browser web il link contenuto nel messaggio di errore e fai clic sul pulsante "Abilita API" per abilitarlo per il tuo progetto.

L'account di servizio non dispone delle autorizzazioni richieste

Se l'account di servizio con cui viene eseguito l'SDK Firebase Admin non dispone dell'autorizzazioneiam.serviceAccounts.signBlob, potresti visualizzare 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 il problema è concedere il ruolo IAM "Creatore token account di servizio" all'account di servizio in questione, in genere{project-name}@appspot.gserviceaccount.com:

  1. Apri la pagina IAM e amministrazione nella console Google Cloud.
  2. Seleziona il progetto e fai clic su "Continua".
  3. Fai clic sull'icona di modifica corrispondente all'account di servizio che vuoi aggiornare.
  4. Fai clic su "Aggiungi un altro ruolo".
  5. Digita "Creatore token account di servizio" nel filtro di ricerca e selezionare dai risultati.
  6. Fai clic su "Salva" per confermare l'assegnazione del ruolo.

Consulta la documentazione IAM per ulteriori dettagli su questa procedura o scopri come aggiornare i ruoli utilizzando il a riga di comando 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 utilizzi l'SDK per il rilevamento automatico dell'ID account di servizio, assicurati il deployment del codice viene eseguito 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 account di servizio all'inizializzazione dell'SDK.