Firebase ti offre il controllo completo sull'autenticazione consentendoti di autenticare utenti o dispositivi utilizzando token JWT (JSON Web Token) sicuri. Generi questi token sul tuo server, li restituisci a un dispositivo client e poi li utilizzi per l'autenticazione tramite il metodo signInWithCustomToken().
Per farlo, devi creare un endpoint del server che accetti le credenziali di accesso, come nome utente e 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 accedere ad altri servizi Firebase, come Firebase Realtime Database e Cloud Storage. Inoltre, i contenuti del JWT saranno disponibili nell'oggetto auth in Realtime Database Security Rules e nell'oggetto request.auth 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 in modo nativo da Firebase.
Prima di iniziare
I token personalizzati sono JWT firmati in cui la chiave privata utilizzata per la firma appartiene a un service account Google. Esistono diversi modi per specificare l'account di servizio Google che deve essere utilizzato dall'SDK Firebase Admin per firmare i token personalizzati:
- Consentire all'SDK Admin di rilevare un service account -- Questo metodo può essere utilizzato in ambienti gestiti da Google, come Google Cloud Functions e App Engine. Potresti dover configurare alcune autorizzazioni aggiuntive tramite la console Google Cloud.
- Utilizzo di un ID service account: questo metodo firma i token utilizzando le chiavi gestite da Google del service account specificato, quindi non devi gestire personalmente le chiavi private. Utilizza un servizio web remoto e potresti dover configurare autorizzazioni aggiuntive per questo service account tramite la console Google Cloud.
- Utilizzo di un file della chiave dell'account di servizio: utilizza questo metodo solo se il codice viene eseguito al di fuori di un ambiente gestito da Google e non puoi utilizzare nessuno dei metodi precedenti. Richiede la creazione, l'archiviazione e la protezione di una chiave privata di lunga durata.
Ti consigliamo i primi due metodi perché non richiedono il download o la gestione delle chiavi dei service account.
Consentire all'SDK Admin di rilevare un service account
Se il tuo codice viene distribuito in un ambiente gestito da Google, l'SDK Admin può tentare di rilevare automaticamente un mezzo per firmare i token personalizzati:
Se il tuo codice viene distribuito nell'ambiente standard App Engine per Java, Python o Go, l'SDK Admin può utilizzare il servizio App Identity presente in quell'ambiente per firmare i token personalizzati. Il servizio App Identity firma i dati utilizzando un service account di cui Google App Engine esegue il provisioning per la tua app.
Se il 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 service account dal server di metadati locale. L'ID service account rilevato viene quindi utilizzato insieme al servizio 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 senza scaricare una chiave del service account, accedi con le tue credenziali utente utilizzando Google Cloud CLI:
gcloud auth application-default login
Poiché sulla tua macchina locale non è presente un server dei metadati, l'SDK Admin non può rilevare automaticamente l'ID di un service account. Invece, specifica un ID service account quando inizializzi l'SDK e assicurati che il tuo account utente disponga del ruolo Creatore token service account per quel service account.
Se l'SDK Firebase Admin deve rilevare una stringa ID service account, 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 del service account rilevato automaticamente è in genere uno dei service account predefiniti forniti da Google Cloud:
Come per gli ID service account specificati esplicitamente, gli ID service account rilevati automaticamente devono disporre dell'autorizzazione iam.serviceAccounts.signBlob per consentire la creazione di token personalizzati. Potresti dover utilizzare la sezione IAM e amministrazione della console Google Cloud per concedere ai service account predefiniti le autorizzazioni necessarie. Per ulteriori dettagli, consulta la sezione sulla risoluzione dei problemi di seguito.
Utilizzo di un ID service account
Per mantenere la coerenza tra le varie parti dell'applicazione, puoi specificare un ID service account le cui chiavi gestite da Google verranno utilizzate per firmare i token. In questo modo, le policy IAM possono essere più semplici e sicure, evitando di dover scaricare, archiviare o eseguire il deployment di una chiave del service account.
L'ID service account è disponibile nella console Google Cloud.
Gli ID dei service account sono indirizzi email con il seguente formato:
<client-id>@<project-id>.iam.gserviceaccount.com. Identificano in modo univoco i service account nei progetti Firebase e Google Cloud.
Per creare token personalizzati utilizzando un ID service account 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 dei service account non sono informazioni sensibili e, pertanto, la loro esposizione non ha conseguenze. Tuttavia, per firmare i token personalizzati con il service account specificato, l'SDK Firebase Admin deve richiamare un servizio remoto.
Inoltre, devi assicurarti che l'identità utilizzata dall'SDK Admin per effettuare questa chiamata, di solito {project-name}@appspot.gserviceaccount.com in produzione o il tuo account utente durante lo sviluppo locale, disponga dell'autorizzazione iam.serviceAccounts.signBlob.
Per ulteriori dettagli, consulta la sezione sulla risoluzione dei problemi di seguito.
Utilizzo di un file della chiave del service account
Se il tuo codice viene eseguito in un ambiente esterno a Google Cloud in cui nessuno dei metodi sopra indicati è disponibile, puoi inizializzare l'SDK Admin con un file di chiave dell'account di servizio. Per maggiori dettagli, segui le istruzioni di configurazione dell'SDK Admin. Con questo metodo, l'SDK Admin firma i token personalizzati localmente utilizzando la chiave privata nel file, senza effettuare chiamate API remote.
Le chiavi dei service account sono credenziali di lunga durata. Se una chiave viene divulgata, chiunque ne sia in possesso può agire come service account finché la chiave non viene eliminata. Non eseguire mai il commit dei file delle chiavi nel controllo della versione e valuta la possibilità di utilizzare la federazione delle identità per i workload anziché le chiavi, se il tuo ambiente lo supporta. Tieni presente inoltre che alcune organizzazioni utilizzano i vincoli delle policy dell'organizzazione per impedire la creazione di chiavi dei service account. In questo caso, devi utilizzare uno degli altri metodi.
Crea token personalizzati utilizzando l'SDK Firebase Admin
L'SDK Firebase Admin 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);
});
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
Se vuoi, puoi anche specificare attestazioni aggiuntive 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 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
Accedi utilizzando i token personalizzati sui client
Dopo aver creato un token personalizzato, devi inviarlo alla tua app client. L'app client si autentica con il token personalizzato chiamando signInWithCustomToken():
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 va a buon fine, l'utente avrà eseguito l'accesso alla tua app client con l'account specificato dal uid incluso nel token personalizzato. Se l'account non esisteva in precedenza, verrà creato un record per l'utente.
Come per gli altri metodi di accesso (ad esempio signInWithEmailAndPassword() e signInWithCredential()), l'oggetto auth in Realtime Database Security Rules e l'oggetto request.auth in Cloud Storage Security Rules verranno compilati con il uid dell'utente. In questo caso, uid sarà quello che hai 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 rivendicazioni aggiuntive, è possibile farvi riferimento 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 un linguaggio che non dispone di un SDK Firebase Admin ufficiale, puoi comunque creare manualmente token personalizzati. Innanzitutto, trova una libreria JWT di terze parti per il tuo linguaggio. Poi, utilizza la libreria JWT per creare un JWT che includa le seguenti attestazioni:
| Attestazioni token personalizzate | ||
|---|---|---|
alg |
Algoritmo | "RS256" |
iss |
Emittente | L'indirizzo email del service account del tuo progetto |
sub |
Oggetto | L'indirizzo email del service account del tuo progetto |
aud |
Pubblico | "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit" |
iat |
Ora di emissione | L'ora corrente, in secondi dall'epoca di Unix |
exp |
Scadenza |
L'ora, in secondi dall'epoca di Unix, in cui scade il token. Può essere al massimo 3600 secondi dopo il iat.
Nota: questo controlla solo l'ora in cui scade il token personalizzato stesso. Tuttavia, una volta che un utente ha eseguito l'accesso utilizzando signInWithCustomToken(), l'accesso al
dispositivo rimarrà attivo finché la sessione non viene invalidata o l'utente non 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ù brevi offrono prestazioni migliori.
|
|
claims (facoltativo) |
Claim personalizzati facoltativi da includere nelle variabili auth/request.auth delle regole di sicurezza
|
|
Di seguito sono riportati alcuni esempi di implementazioni per creare token personalizzati in una varietà di linguaggi non supportati dall'SDK Firebase Admin. Questi esempi firmano i token localmente con una chiave privata del service account, pertanto si applicano le indicazioni di sicurezza per i file delle chiavi dei service account. Per evitare di gestire una chiave privata, puoi chiedere a Google di firmare il payload del token da remoto chiamando il metodo signJwt dell'API Service Account Credentials di IAM. Il chiamante deve disporre del ruolo Creatore token account di servizio sul service account.
PHP
Utilizzo di php-jwt:
// Requires: composer require firebase/php-jwt
use Firebase\JWT\JWT;
// Placeholder values. In production, load your service account's email
// address and private key from a secure secret store. Never hard-code
// private keys in source code.
$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
Utilizzo di ruby-jwt:
require "jwt"
# Placeholder values. In production, load your service account's email
# address and private key from a secure secret store. Never hard-code
# private keys in source code.
$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 alla tua app client per utilizzarlo per l'autenticazione con Firebase. Per scoprire come fare, consulta gli esempi di codice riportati sopra.
Risoluzione dei problemi
Questa sezione descrive alcuni problemi comuni che gli sviluppatori potrebbero riscontrare durante la creazione di token personalizzati e come risolverli.
API IAM non abilitata
Se specifichi un ID service account 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 link nel messaggio di errore in un browser web e fai clic sul pulsante "Abilita API" per abilitarla per il tuo progetto.
Il service account non dispone delle autorizzazioni richieste
Se il service account con cui viene eseguito 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}.
Per risolvere il problema, concedi il ruolo IAM Creatore token account di servizio al service account appropriato. Il service account predefinito utilizzato dipende dall'ambiente e dalla versione di Cloud Functions:
- Cloud Functions (1ª gen.): utilizza il service account App Engine predefinito.
- Cloud Functions (2nd gen): utilizza l'account di servizio predefinito di Compute Engine.
Nella console Google Cloud, vai a IAM e amministrazione.
Fai clic sull'icona di modifica corrispondente al service account che vuoi aggiornare.
Fai clic su Aggiungi un altro ruolo.
Digita
"Service Account Token Creator"nel filtro di ricerca e selezionalo dai risultati.Fai clic su Salva per confermare la concessione del ruolo.
Consulta la documentazione di IAM per maggiori dettagli su questo processo o scopri come aggiornare i ruoli utilizzando gli strumenti a riga di comando gcloud.
Impossibile determinare il service account
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 rilevare automaticamente un ID service account, assicurati che il codice sia stato eseguito il deployment in un ambiente Google gestito con un server di metadati. In caso contrario, assicurati di specificare un ID service account durante l'inizializzazione dell'SDK o, in ambienti non Google, inizializza l'SDK con un file della chiave del service account.