Firebase ti offre il controllo completo dell'autenticazione consentendoti di autenticare utenti o dispositivi utilizzando token web JSON (JWT) 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 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, 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 oppure utilizzare una libreria JWT di terze parti se il 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 account di servizio Google. Esistono diversi modi per specificare il service account Google che deve essere utilizzato dall'SDK Firebase Admin per la firma di token personalizzati:
- Utilizzo di un file JSON dell'account di servizio: questo metodo può essere utilizzato in qualsiasi ambiente, ma richiede di includere un file JSON dell'account di servizio nel codice. È necessario prestare particolare attenzione per garantire che il file JSON del service account non venga esposto a terze parti.
- 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 account di servizio: quando viene utilizzato in un ambiente gestito da Google, questo metodo firma i token utilizzando la chiave dell'account di servizio specificato. Tuttavia, utilizza un servizio web remoto e potresti dover configurare autorizzazioni aggiuntive per questo service account tramite la console Google Cloud.
Utilizzo di un file JSON dell'account di servizio
I file JSON del service account contengono tutte le informazioni corrispondenti agli account di servizio (inclusa la chiave privata RSA). Possono essere scaricati dalla console Firebase. Per ulteriori informazioni su come inizializzare l'SDK Admin con un file JSON dell'account di servizio, segui le istruzioni di configurazione dell'SDK Admin.
Questo metodo di inizializzazione è adatto a un'ampia gamma di implementazioni dell'SDK Admin. Inoltre, consente all'SDK Admin di creare e firmare token personalizzati localmente, 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 presente inoltre che la chiave privata in un file JSON dell'account di servizio è un'informazione sensibile e deve essere trattata con particolare attenzione per mantenerla riservata. In particolare, evita di aggiungere file JSON dell'account di servizio al controllo della versione pubblico.
Consentire all'SDK Admin di rilevare un service account
Se il codice viene implementato in un ambiente gestito da Google, l'Admin SDK può tentare di rilevare automaticamente un modo per firmare i token personalizzati:
Se il codice viene implementato nell'ambiente standard App Engine per Java, Python o Go, l'SDK Admin può utilizzare il servizio di identità app presente in quell'ambiente per firmare i token personalizzati. Il servizio App Identity firma i dati utilizzando un account di servizio di cui Google App Engine esegue il provisioning per la tua app.
Se il tuo codice viene implementato in un altro ambiente gestito (ad es. Google Cloud Functions, Google Compute Engine), l'SDK Firebase Admin può rilevare automaticamente una stringa ID account di servizio 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 service account:
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 localmente, scarica un file JSON dell'account di servizio e imposta la variabile di ambiente GOOGLE_APPLICATION_CREDENTIALS
in modo che rimandi al file.
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 del token. L'ID service account rilevato automaticamente è in genere uno dei service account predefiniti forniti da Google Cloud:
Proprio come per gli ID service account specificati in modo esplicito, gli ID service account rilevati automaticamente devono disporre dell'autorizzazione iam.serviceAccounts.signBlob
per il funzionamento della 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 Risoluzione dei problemi riportata di seguito.
Utilizzo di un ID service account
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. In questo modo, le norme IAM possono essere più semplici e sicure ed è possibile evitare di includere il file JSON dell'account di servizio nel codice.
L'ID service account si trova nella
console Google Cloud
o nel campo client_email
di un file JSON del service account scaricato.
Gli ID service account 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 service account non sono informazioni sensibili 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'SDK Admin per effettuare questa chiamata, di solito {project-name}@appspot.gserviceaccount.com
, disponga dell'iam.serviceAccounts.signBlob
autorizzazione.
Per ulteriori dettagli, consulta la sezione Risoluzione dei problemi riportata di seguito.
Creare 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 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
Puoi anche specificare facoltativamente ulteriori rivendicazioni 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
Accedere 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+
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 accederà all'app client con l'account specificato da uid
incluso nel token personalizzato. Se l'account non esisteva in precedenza, verrà creato un record per l'utente.
Come per 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 l'uid
dell'utente. In questo caso, il 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 ulteriori rivendicazioni, è possibile farvi riferimento
nell'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;
}
}
}
Creare token personalizzati utilizzando una libreria JWT di terze parti
Se il tuo backend è in una lingua per cui non esiste un SDK Firebase Admin ufficiale, puoi comunque creare manualmente token personalizzati. Innanzitutto, trova una libreria JWT di terze parti per la tua lingua. Poi, utilizza questa libreria JWT per generare 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 attuale, in secondi a partire dall'epoca UNIX |
exp |
Scadenza |
Il momento in cui il token scade, espresso in secondi a partire dal tempo Unix. Può
essere al massimo 3600 secondi dopo iat .
Nota: questo valore controlla solo la scadenza del token personalizzato. Tuttavia, una volta che un utente ha eseguito l'accesso utilizzando signInWithCustomToken() , l'accesso rimarrà attivo sul
dispositivo finché la sessione non viene invalidata o l'utente non esce.
|
uid |
L'identificatore univoco dell'utente che ha eseguito l'accesso deve essere una stringa compresa tra
1 e 128 caratteri inclusi. I uid più brevi offrono prestazioni migliori.
|
|
(Facoltativo) claims |
Rivendicazioni personalizzate facoltative da includere nelle variabili delle regole di sicurezza auth /
request.auth
|
Ecco alcuni esempi di implementazioni di come creare token personalizzati in una serie di linguaggi non supportati dall'SDK Firebase Admin:
PHP
Utilizzo di 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
Utilizzo di 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. 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 visualizzare un messaggio di errore come il 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, di solito
{project-name}@appspot.gserviceaccount.com
:
- Apri la pagina IAM e amministrazione nella console Google Cloud.
- Seleziona il progetto e fai clic su "Continua".
- Fai clic sull'icona di modifica corrispondente al service account che vuoi aggiornare.
- Fai clic su "Aggiungi un altro ruolo".
- Digita "Creatore token account di servizio" nel filtro di ricerca e selezionalo dai risultati.
- Fai clic su "Salva" per confermare la concessione del ruolo.
Consulta la documentazione IAM per maggiori dettagli su questa procedura 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 account di servizio, assicurati che il codice sia 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.