Benutzerdefinierte Tokens erstellen

Mit Firebase haben Sie die vollständige Kontrolle über die Authentifizierung, da Sie Nutzer oder Geräte mit sicheren JSON Web Tokens (JWTs) authentifizieren können. Sie generieren diese Tokens auf Ihrem Server, übergeben sie an ein Clientgerät und verwenden sie dann, um sich über die signInWithCustomToken()-Methode zu authentifizieren.

Dazu müssen Sie einen Serverendpunkt erstellen, der Anmeldedaten wie einen Nutzernamen und ein Passwort akzeptiert und bei gültigen Anmeldedaten ein benutzerdefiniertes JWT zurückgibt. Das von Ihrem Server zurückgegebene benutzerdefinierte JWT kann dann von einem Clientgerät verwendet werden, um sich bei Firebase zu authentifizieren (iOS und höher, Android, Web). Nach der Authentifizierung wird diese Identität für den Zugriff auf andere Firebase-Dienste wie Firebase Realtime Database und Cloud Storage verwendet. Außerdem ist der Inhalt des JWT im auth-Objekt in Realtime Database Security Rules und im request.auth-Objekt in Cloud Storage Security Rules verfügbar.

Sie können ein benutzerdefiniertes Token mit dem Firebase Admin SDK erstellen oder eine JWT-Bibliothek eines Drittanbieters verwenden, wenn Ihr Server in einer Sprache geschrieben ist, die von Firebase nicht nativ unterstützt wird.

Hinweis

Benutzerdefinierte Tokens sind signierte JWTs, bei denen der zum Signieren verwendete private Schlüssel zu einem Google-Dienstkonto gehört. Es gibt mehrere Möglichkeiten, das Google-Dienstkonto anzugeben, das vom Firebase Admin SDK zum Signieren benutzerdefinierter Tokens verwendet werden soll:

  • JSON-Datei für ein Dienstkonto verwenden: Diese Methode kann in jeder Umgebung verwendet werden, erfordert jedoch, dass Sie eine JSON-Datei für ein Dienstkonto zusammen mit Ihrem Code verpacken. Achten Sie darauf, dass die JSON-Datei des Dienstkontos nicht für externe Parteien zugänglich ist.
  • Admin SDK ein Dienstkonto finden lassen: Diese Methode kann in von Google verwalteten Umgebungen wie Google Cloud Functions und App Engine verwendet werden. Möglicherweise müssen Sie einige zusätzliche Berechtigungen über die Google Cloud-Konsole konfigurieren.
  • Dienstkonto-ID verwenden: Bei Verwendung in einer von Google verwalteten Umgebung werden mit dieser Methode Tokens mit dem Schlüssel des angegebenen Dienstkontos signiert. Es wird jedoch ein Remote-Webdienst verwendet und Sie müssen möglicherweise zusätzliche Berechtigungen für dieses Dienstkonto über die Google Cloud-Konsole konfigurieren.

JSON-Datei für Dienstkonto verwenden

JSON-Dateien für Dienstkonten enthalten alle Informationen zu Dienstkonten, einschließlich des privaten RSA-Schlüssels. Sie können sie über die Firebase-Konsole herunterladen. Weitere Informationen zum Initialisieren des Admin SDK mit einer JSON-Datei für ein Dienstkonto finden Sie in der Anleitung zum Einrichten des Admin SDK.

Diese Methode der Initialisierung eignet sich für eine Vielzahl von Admin SDK-Bereitstellungen. Außerdem ermöglicht es dem Admin SDK, benutzerdefinierte Tokens lokal zu erstellen und zu signieren, ohne Remote-API-Aufrufe auszuführen. Der Hauptnachteil dieses Ansatzes besteht darin, dass Sie eine JSON-Datei für das Dienstkonto zusammen mit Ihrem Code verpacken müssen. Der private Schlüssel in einer JSON-Datei für ein Dienstkonto ist vertraulich und muss besonders geschützt werden. Fügen Sie der öffentlichen Versionskontrolle insbesondere keine JSON-Dateien für Dienstkonten hinzu.

Admin SDK ein Dienstkonto finden lassen

Wenn Ihr Code in einer von Google verwalteten Umgebung bereitgestellt wird, kann das Admin SDK versuchen, automatisch eine Möglichkeit zum Signieren benutzerdefinierter Tokens zu finden:

  • Wenn Ihr Code in der App Engine-Standardumgebung für Java, Python oder Go bereitgestellt wird, kann das Admin SDK den in dieser Umgebung vorhandenen App Identity-Dienst verwenden, um benutzerdefinierte Tokens zu signieren. Der App Identity-Dienst signiert Daten mit einem Dienstkonto, das von der Google App Engine für Ihre App bereitgestellt wird.

  • Wenn Ihr Code in einer anderen verwalteten Umgebung bereitgestellt wird (z.B. Google Cloud Functions oder Google Compute Engine), kann das Firebase Admin SDK einen Dienstkonto-ID-String automatisch vom lokalen Metadatenserver ermitteln. Die ermittelte Dienstkonto-ID wird dann in Verbindung mit dem IAM-Dienst verwendet, um Tokens aus der Ferne zu signieren.

Wenn Sie diese Signaturmethoden verwenden möchten, initialisieren Sie das SDK mit den Standardanmeldedaten für Google-Anwendungen und geben Sie keinen Dienstkonto-ID-String an:

Node.js

initializeApp();

Java

FirebaseApp.initializeApp();

Python

default_app = firebase_admin.initialize_app()

Go

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

C#

FirebaseApp.Create();

Wenn Sie denselben Code lokal testen möchten, laden Sie eine JSON-Datei für das Dienstkonto herunter und legen Sie die Umgebungsvariable GOOGLE_APPLICATION_CREDENTIALS so fest, dass sie darauf verweist.

Wenn das Firebase Admin SDK einen Dienstkonto-ID-String ermitteln muss, geschieht dies, wenn Ihr Code zum ersten Mal ein benutzerdefiniertes Token erstellt. Das Ergebnis wird im Cache gespeichert und für nachfolgende Tokensignaturvorgänge wiederverwendet. Die automatisch ermittelte Dienstkonto-ID ist in der Regel eines der Standarddienstkonten, die von der Google Cloud bereitgestellt werden:

Genau wie bei explizit angegebenen Dienstkonto-IDs müssen automatisch erkannte Dienstkonto-IDs die Berechtigung iam.serviceAccounts.signBlob haben, damit die Erstellung benutzerdefinierter Tokens funktioniert. Möglicherweise müssen Sie in der Google Cloud-Konsole im Bereich IAM und Verwaltung die erforderlichen Berechtigungen für die Standarddienstkonten gewähren. Weitere Informationen finden Sie unten im Abschnitt zur Fehlerbehebung.

Dienstkonto-ID verwenden

Um für Konsistenz zwischen verschiedenen Teilen Ihrer Anwendung zu sorgen, können Sie eine Dienstkonto-ID angeben, deren Schlüssel zum Signieren von Tokens verwendet werden, wenn die Anwendung in einer von Google verwalteten Umgebung ausgeführt wird. So können IAM-Richtlinien einfacher und sicherer gestaltet werden und Sie müssen die JSON-Datei des Dienstkontos nicht in Ihren Code einbinden.

Sie finden die Dienstkonto-ID in der Google Cloud-Konsole oder im Feld client_email einer heruntergeladenen JSON-Datei für das Dienstkonto. Dienstkonto-IDs sind E-Mail-Adressen im folgenden Format: <client-id>@<project-id>.iam.gserviceaccount.com. Sie dienen der eindeutigen Identifizierung von Dienstkonten in Firebase- und Google Cloud-Projekten.

Wenn Sie benutzerdefinierte Tokens mit einer separaten Dienstkonto-ID erstellen möchten, initialisieren Sie das SDK wie unten gezeigt:

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)

Go

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

Dienstkonto-IDs sind keine vertraulichen Daten und ihre Offenlegung ist daher nicht von Bedeutung. Damit benutzerdefinierte Tokens jedoch mit dem angegebenen Dienstkonto signiert werden können, muss das Firebase Admin SDK einen Remote-Dienst aufrufen. Außerdem muss das Dienstkonto, das vom Admin SDK für diesen Aufruf verwendet wird (normalerweise {project-name}@appspot.gserviceaccount.com), die Berechtigung iam.serviceAccounts.signBlob haben. Weitere Informationen finden Sie im Abschnitt zur Fehlerbehebung unten.

Benutzerdefinierte Tokens mit dem Firebase Admin SDK erstellen

Das Firebase Admin SDK verfügt über eine integrierte Methode zum Erstellen benutzerdefinierter Tokens. Sie müssen mindestens eine uid angeben. Dies kann ein beliebiger String sein, der den Nutzer oder das Gerät, das Sie authentifizieren, eindeutig identifiziert. Diese Tokens laufen nach einer Stunde ab.

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)

Go

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

Optional können Sie auch zusätzliche Ansprüche angeben, die im benutzerdefinierten Token enthalten sein sollen. Im folgenden Beispiel wurde dem benutzerdefinierten Token ein premiumAccount-Feld hinzugefügt, das in den auth-/request.auth-Objekten in Ihren Sicherheitsregeln verfügbar ist:

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)

Go

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

Reservierte Namen benutzerdefinierter Tokens

Anmeldung mit benutzerdefinierten Tokens auf Clients

Nachdem Sie ein benutzerdefiniertes Token erstellt haben, sollten Sie es an Ihre Clientanwendung senden. Die Clientanwendung authentifiziert sich mit dem benutzerdefinierten Token, indem sie signInWithCustomToken() aufruft:

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

Einheit

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

Wenn die Authentifizierung erfolgreich war, ist der Nutzer jetzt mit dem Konto angemeldet, das durch das uid im benutzerdefinierten Token angegeben ist. Wenn dieses Konto noch nicht vorhanden war, wird ein Eintrag für diesen Nutzer erstellt.

Wie bei anderen Anmeldemethoden (z. B. signInWithEmailAndPassword() und signInWithCredential()) werden das auth-Objekt in Realtime Database Security Rules und das request.auth-Objekt in Cloud Storage Security Rules mit der uid des Nutzers ausgefüllt. In diesem Fall ist uid die ID, die Sie beim Generieren des benutzerdefinierten Tokens angegeben haben.

Datenbankregeln

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

Speicherregeln

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

Wenn das benutzerdefinierte Token zusätzliche Ansprüche enthält, können sie auf das Objekt auth.token (Firebase Realtime Database) oder request.auth.token (Cloud Storage) in Ihren Regeln verwiesen werden:

Datenbankregeln

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

Speicherregeln

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

Benutzerdefinierte Tokens mit der JWT-Bibliothek eines Drittanbieters erstellen

Wenn Ihr Back-End in einer Sprache geschrieben ist, für die es kein offizielles Firebase Admin SDK gibt, können Sie trotzdem benutzerdefinierte Tokens manuell erstellen. Suchen Sie zuerst nach einer JWT-Bibliothek eines Drittanbieters für Ihre Sprache. Verwenden Sie dann diese JWT-Bibliothek, um ein JWT zu erstellen, das die folgenden Anforderungen enthält:

Benutzerdefinierte Token-Anforderungen
alg Algorithmus "RS256"
iss Aussteller Die E-Mail-Adresse des Dienstkontos Ihres Projekts
sub Betreff Die E-Mail-Adresse des Dienstkontos Ihres Projekts
aud Zielgruppe "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat Ausstellungszeit Die aktuelle Zeit in Sekunden seit der UNIX-Epoche
exp Ablaufzeit Die Zeit in Sekunden seit der UNIX-Epoche, zu der das Token abläuft. Dies kann maximal 3.600 Sekunden später als iat sein.
Beachten Sie, dass damit nur der Zeitpunkt gesteuert wird, zu dem das benutzerdefinierte Token selbst abläuft. Wenn Sie jedoch einen Nutzer mit signInWithCustomToken() anmelden, bleiben er so lange auf dem Gerät angemeldet, bis die Sitzung ungültig wird oder der Nutzer sich abmeldet.
uid Die eindeutige Kennung des angemeldeten Nutzers muss ein String zwischen 1 und 128 Zeichen lang sein. Je kürzer die uid, desto besser die Leistung.
claims (optional) Optionale benutzerdefinierte Anforderungen, die in die Variablen auth/request.auth der Sicherheitsregeln aufgenommen werden sollen

Hier sind einige Beispielimplementierungen zum Erstellen benutzerdefinierter Tokens in verschiedenen Sprachen, die vom Firebase Admin SDK nicht unterstützt werden:

PHP

Mit 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

Mit 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

Nachdem Sie das benutzerdefinierte Token erstellt haben, senden Sie es an Ihre Clientanwendung, um sich damit bei Firebase zu authentifizieren. Weitere Informationen finden Sie in den Codebeispielen oben.

Fehlerbehebung

In diesem Abschnitt werden einige häufige Probleme beschrieben, die beim Erstellen benutzerdefinierter Tokens auftreten können, und wie sie behoben werden können.

IAM API nicht aktiviert

Wenn Sie eine Dienstkonto-ID für die Signatur von Tokens angeben, erhalten Sie möglicherweise einen ähnlichen Fehler wie den folgenden:

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.

Das Firebase Admin SDK verwendet die IAM API, um Tokens zu signieren. Dieser Fehler gibt an, dass die IAM API derzeit nicht für Ihr Firebase-Projekt aktiviert ist. Öffnen Sie den Link in der Fehlermeldung in einem Webbrowser und klicken Sie auf die Schaltfläche „API aktivieren“, um sie für Ihr Projekt zu aktivieren.

Das Dienstkonto hat nicht die erforderlichen Berechtigungen

Wenn das Dienstkonto, unter dem das Firebase Admin SDK ausgeführt wird, nicht die Berechtigung iam.serviceAccounts.signBlob hat, wird möglicherweise eine Fehlermeldung wie die folgende angezeigt:

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

Am einfachsten lässt sich das Problem beheben, indem Sie dem betreffenden Dienstkonto die IAM-Rolle „Ersteller von Dienstkonto-Tokens“ zuweisen, in der Regel {project-name}@appspot.gserviceaccount.com:

  1. Öffnen Sie in der Google Cloud Console die Seite IAM und Verwaltung.
  2. Wählen Sie Ihr Projekt aus und klicken Sie auf „Weiter“.
  3. Klicken Sie auf das Bearbeitungssymbol für das Dienstkonto, das Sie aktualisieren möchten.
  4. Klicken Sie auf „Weitere Rolle hinzufügen“.
  5. Geben Sie „Ersteller von Dienstkonto-Tokens“ in den Suchfilter ein und wählen Sie die Option aus den Ergebnissen aus.
  6. Klicken Sie auf „Speichern“, um die Rollenzuweisung zu bestätigen.

Weitere Informationen zu diesem Vorgang finden Sie in der IAM-Dokumentation. Dort erfahren Sie auch, wie Sie Rollen mit dem gcloud-Befehlszeilentool aktualisieren.

Dienstkonto konnte nicht ermittelt werden

Wenn Sie eine Fehlermeldung wie die folgende erhalten, wurde das Firebase Admin SDK nicht richtig initialisiert.

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

Wenn Sie sich darauf verlassen, dass das SDK eine Dienstkonto-ID automatisch erkennt, muss der Code in einer verwalteten Google-Umgebung mit einem Metadatenserver bereitgestellt werden. Andernfalls müssen Sie bei der SDK-Initialisierung die JSON-Datei des Dienstkontos oder die Dienstkonto-ID angeben.