Erstellen Sie benutzerdefinierte Token

Firebase gibt Ihnen die vollständige Kontrolle über die Authentifizierung, indem es Ihnen ermöglicht, Benutzer oder Geräte mithilfe sicherer JSON Web Tokens (JWTs) zu authentifizieren. Sie generieren diese Token auf Ihrem Server, geben sie an ein Clientgerät zurück und verwenden sie dann zur Authentifizierung über die Methode signInWithCustomToken() .

Um dies zu erreichen, müssen Sie einen Serverendpunkt erstellen, der Anmeldeinformationen – wie einen Benutzernamen und ein Passwort – akzeptiert und, wenn die Anmeldeinformationen gültig sind, ein benutzerdefiniertes JWT zurückgibt. Das von Ihrem Server zurückgegebene benutzerdefinierte JWT kann dann von einem Clientgerät zur Authentifizierung bei Firebase ( iOS+ , Android , Web ) verwendet werden. Nach der Authentifizierung wird diese Identität beim Zugriff auf andere Firebase-Dienste wie die Firebase-Echtzeitdatenbank und den Cloud-Speicher verwendet. Darüber hinaus sind die Inhalte des JWT im auth Objekt in Ihren Echtzeit-Datenbank-Sicherheitsregeln und im request.auth Objekt in Ihren Cloud-Speicher-Sicherheitsregeln verfügbar.

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

Bevor Sie beginnen

Benutzerdefinierte Token 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 Token verwendet werden soll:

  • Verwenden einer JSON-Datei für ein Dienstkonto – Diese Methode kann in jeder Umgebung verwendet werden, erfordert jedoch, dass Sie eine JSON-Datei für ein Dienstkonto zusammen mit Ihrem Code packen. Es muss besonders darauf geachtet werden, sicherzustellen, dass die JSON-Datei des Dienstkontos nicht für externe Parteien zugänglich ist.
  • Das Admin SDK ein Dienstkonto erkennen 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 Console konfigurieren.
  • Verwenden einer Dienstkonto-ID – Bei Verwendung in einer von Google verwalteten Umgebung signiert diese Methode Token mit dem Schlüssel des angegebenen Dienstkontos. Es verwendet jedoch einen Remote-Webdienst und Sie müssen möglicherweise zusätzliche Berechtigungen für dieses Dienstkonto über die Google Cloud Console konfigurieren.

Verwenden einer JSON-Datei für ein Dienstkonto

JSON-Dateien des Dienstkontos enthalten alle Informationen zu Dienstkonten (einschließlich des privaten RSA-Schlüssels). Sie können von der Firebase-Konsole heruntergeladen werden. Befolgen Sie die Anweisungen zum Einrichten des Admin SDK , um weitere Informationen zum Initialisieren des Admin SDK mit einer JSON-Datei für ein Dienstkonto zu erhalten.

Diese Initialisierungsmethode eignet sich für eine Vielzahl von Admin-SDK-Bereitstellungen. Außerdem ermöglicht es dem Admin SDK, benutzerdefinierte Token lokal zu erstellen und zu signieren, ohne Remote-API-Aufrufe durchzuführen. Der Hauptnachteil dieses Ansatzes besteht darin, dass Sie zusammen mit Ihrem Code eine JSON-Datei für ein Dienstkonto packen müssen. Beachten Sie außerdem, dass es sich beim privaten Schlüssel in einer JSON-Datei eines Dienstkontos um vertrauliche Informationen handelt und besondere Sorgfalt auf seine Vertraulichkeit gelegt werden muss. Insbesondere sollten Sie davon absehen, JSON-Dateien von Dienstkonten zur öffentlichen Versionskontrolle hinzuzufügen.

Ermöglichen, dass das Admin SDK ein Dienstkonto erkennt

Wenn Ihr Code in einer von Google verwalteten Umgebung bereitgestellt wird, kann das Admin SDK versuchen, automatisch eine Möglichkeit zum Signieren benutzerdefinierter Token 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 Token zu signieren. Der App Identity-Dienst signiert Daten mithilfe eines Dienstkontos, das von Google App Engine für Ihre App bereitgestellt wird.

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

Um diese Signaturmethoden zu verwenden, initialisieren Sie das SDK mit den Google Application Default-Anmeldeinformationen und geben Sie keine Dienstkonto-ID-Zeichenfolge an:

Node.js

initializeApp();

Java

FirebaseApp.initializeApp();

Python

default_app = firebase_admin.initialize_app()

Gehen

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

C#

FirebaseApp.Create();

Um denselben Code lokal zu testen, laden Sie eine JSON-Datei für ein Dienstkonto herunter und legen Sie die Umgebungsvariable GOOGLE_APPLICATION_CREDENTIALS so fest, dass sie darauf verweist.

Wenn das Firebase Admin SDK eine Dienstkonto-ID-Zeichenfolge erkennen muss, geschieht dies, wenn Ihr Code zum ersten Mal ein benutzerdefiniertes Token erstellt. Das Ergebnis wird zwischengespeichert und für nachfolgende Token-Signaturvorgänge wiederverwendet. Die automatisch erkannte Dienstkonto-ID ist normalerweise eines der von Google Cloud bereitgestellten Standarddienstkonten:

Genau wie bei explizit angegebenen Dienstkonto-IDs müssen automatisch erkannte Dienstkonto-IDs über die Berechtigung iam.serviceAccounts.signBlob verfügen, damit die benutzerdefinierte Tokenerstellung funktioniert. Möglicherweise müssen Sie den IAM- und Admin- Bereich der Google Cloud Console verwenden, um den Standarddienstkonten die erforderlichen Berechtigungen zu erteilen. Weitere Einzelheiten finden Sie im Abschnitt zur Fehlerbehebung weiter unten.

Verwendung einer Dienstkonto-ID

Um die Konsistenz zwischen verschiedenen Teilen Ihrer Anwendung aufrechtzuerhalten, können Sie eine Dienstkonto-ID angeben, deren Schlüssel zum Signieren von Token verwendet werden, wenn sie in einer von Google verwalteten Umgebung ausgeführt wird. Dies kann IAM-Richtlinien einfacher und sicherer machen und es vermeiden, dass Sie die JSON-Datei des Dienstkontos in Ihren Code einbinden müssen.

Die Dienstkonto-ID finden Sie in der Google Cloud Console oder im Feld client_email einer heruntergeladenen JSON-Datei des Dienstkontos. Dienstkonto-IDs sind E-Mail-Adressen mit dem folgenden Format: <client-id>@<project-id>.iam.gserviceaccount.com . Sie identifizieren Dienstkonten in Firebase- und Google Cloud-Projekten eindeutig.

Um benutzerdefinierte Token mit einer separaten Dienstkonto-ID zu erstellen, 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)

Gehen

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 Informationen und daher hat ihre Offenlegung keine Konsequenzen. Um jedoch benutzerdefinierte Token mit dem angegebenen Dienstkonto zu signieren, muss das Firebase Admin SDK einen Remotedienst aufrufen. Darüber hinaus müssen Sie sicherstellen, dass das Dienstkonto, das das Admin SDK für diesen Aufruf verwendet – normalerweise {project-name}@appspot.gserviceaccount.com – über die Berechtigung iam.serviceAccounts.signBlob verfügt. Weitere Einzelheiten finden Sie im Abschnitt zur Fehlerbehebung weiter unten.

Erstellen Sie benutzerdefinierte Token mit dem Firebase Admin SDK

Das Firebase Admin SDK verfügt über eine integrierte Methode zum Erstellen benutzerdefinierter Token. Sie müssen mindestens eine uid angeben, die eine beliebige Zeichenfolge sein kann, aber den Benutzer oder das Gerät, das Sie authentifizieren, eindeutig identifizieren sollte. Diese Token verfallen nach einer Stunde.

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)

Gehen

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

Sie können optional auch zusätzliche Ansprüche angeben, die in das benutzerdefinierte Token aufgenommen werden sollen. Im Folgenden wurde beispielsweise ein premiumAccount Feld zum benutzerdefinierten Token hinzugefügt, das in den auth / request.auth Objekten in Ihren Sicherheitsregeln verfügbar sein wird:

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)

Gehen

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 benutzerdefinierte Token-Namen

Melden Sie sich mit benutzerdefinierten Token auf Clients an

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

iOS+

Ziel c
[[FIRAuth auth] signInWithCustomToken:customToken
                           completion:^(FIRAuthDataResult * _Nullable authResult,
                                        NSError * _Nullable error) {
  // ...
}];
Schnell
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-Namespace-API

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

Modulare Web-API

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 ist, wird Ihr Benutzer nun bei Ihrer Client-App mit dem Konto angemeldet, das durch die im benutzerdefinierten Token enthaltene uid angegeben wird. Wenn dieses Konto zuvor nicht existierte, wird ein Datensatz für diesen Benutzer erstellt.

Auf die gleiche Weise wie bei anderen Anmeldemethoden (z. B. signInWithEmailAndPassword() und signInWithCredential() ) werden das auth Objekt in Ihren Echtzeitdatenbank-Sicherheitsregeln und das request.auth Objekt in Ihren Cloud Storage-Sicherheitsregeln mit der uid des Benutzers gefüllt . In diesem Fall ist die uid diejenige, die Sie beim Generieren des benutzerdefinierten Tokens angegeben haben.

Datenbankregeln

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

Aufbewahrungsregeln

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 diese über das Objekt auth.token (Firebase Realtime Database) oder request.auth.token (Cloud Storage) in Ihren Regeln referenziert werden:

Datenbankregeln

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

Aufbewahrungsregeln

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

Erstellen Sie benutzerdefinierte Token mithilfe einer JWT-Bibliothek eines Drittanbieters

Wenn Ihr Backend in einer Sprache ist, für die es kein offizielles Firebase Admin SDK gibt, können Sie dennoch manuell benutzerdefinierte Token erstellen. Suchen Sie zunächst nach einer JWT-Bibliothek eines Drittanbieters für Ihre Sprache. Verwenden Sie dann diese JWT-Bibliothek, um ein JWT zu erstellen, das die folgenden Ansprüche enthält:

Benutzerdefinierte Token-Ansprüche
alg Algorithmus "RS256"
iss Aussteller Die E-Mail-Adresse des Dienstkontos Ihres Projekts
sub Thema Die E-Mail-Adresse des Dienstkontos Ihres Projekts
aud Publikum "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat Ausgestellt zum Zeitpunkt 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. Es kann maximal 3600 Sekunden später als das iat sein.
Hinweis: Dies steuert nur die Zeit, zu der das benutzerdefinierte Token selbst abläuft. Sobald Sie jedoch einen Benutzer mit signInWithCustomToken() anmelden, bleibt er beim Gerät angemeldet, bis seine Sitzung ungültig wird oder der Benutzer sich abmeldet.
uid Die eindeutige Kennung des angemeldeten Benutzers muss eine Zeichenfolge mit einer Länge zwischen 1 und 128 Zeichen sein. Kürzere uid bieten eine bessere Leistung.
claims (optional) Optionale benutzerdefinierte Ansprüche, die in die auth / request.auth -Variablen der Sicherheitsregeln aufgenommen werden sollen

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

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

Rubin

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 Client-App, um es zur Authentifizierung bei Firebase zu verwenden. Sehen Sie sich die Codebeispiele oben an, um zu erfahren, wie das geht.

Fehlerbehebung

In diesem Abschnitt werden einige häufige Probleme beschrieben, auf die Entwickler beim Erstellen benutzerdefinierter Token stoßen können, und wie diese behoben werden können.

IAM-API nicht aktiviert

Wenn Sie eine Dienstkonto-ID zum Signieren von Token angeben, erhalten Sie möglicherweise eine Fehlermeldung ähnlich der 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 zum Signieren von Tokens. Dieser Fehler weist darauf hin, 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 verfügt nicht über die erforderlichen Berechtigungen

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

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

Der einfachste Weg, dieses Problem zu lösen, besteht darin, dem betreffenden Dienstkonto die IAM-Rolle „Service Account Token Creator“ zuzuweisen, normalerweise {project-name}@appspot.gserviceaccount.com :

  1. Öffnen Sie die IAM- und Admin- Seite in der Google Cloud Console.
  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 „Service Account Token Creator“ in den Suchfilter ein und wählen Sie es 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 . Alternativ erfahren Sie, wie Sie mit den gcloud-Befehlszeilentools Aktualisierungsrollen durchführen.

Dienstkonto konnte nicht ermittelt werden

Wenn Sie eine Fehlermeldung ähnlich der folgenden erhalten, wurde das Firebase Admin SDK nicht ordnungsgemäß 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, stellen Sie sicher, dass der Code in einer verwalteten Google-Umgebung mit einem Metadatenserver bereitgestellt wird. Andernfalls stellen Sie sicher, dass Sie bei der SDK-Initialisierung die JSON-Datei oder die Dienstkonto-ID des Dienstkontos angeben.