Tworzenie tokenów niestandardowych

Firebase zapewnia pełną kontrolę nad uwierzytelnianiem, umożliwiając: uwierzytelniać użytkowników lub urządzenia przy użyciu bezpiecznych tokenów sieciowych JSON (JWT). Generujesz te tokeny na serwerze, przekaż je z powrotem do urządzenia klienckiego, a następnie użyj ich uwierzytelnienie za pomocą metody signInWithCustomToken().

Aby to osiągnąć, musisz utworzyć punkt końcowy serwera, który akceptuje logowanie danych logowania (takich jak nazwa użytkownika i hasło), a jeśli dane logowania Prawidłowy, zwraca niestandardowy token JWT. Niestandardowy token JWT zwrócony z serwera może wtedy być używane przez urządzenie klienckie do uwierzytelniania w Firebase (iOS+, Android, sieć). Po uwierzytelnieniu ta tożsamość zostanie używane przy uzyskiwaniu dostępu do innych usług Firebase, takich jak Firebase Realtime Database i Cloud Storage. Ponadto zawartość tokena JWT będzie dostępna w obiekcie auth w Realtime Database Security Rules oraz request.auth obiekt w Cloud Storage Security Rules

Możesz utworzyć token niestandardowy za pomocą pakietu Firebase Admin SDK lub użyj zewnętrznej biblioteki JWT, jeśli Twój serwer jest zapisany w Język, którego Firebase nie obsługuje natywnie.

Zanim zaczniesz

Tokeny niestandardowe to podpisane tokeny JWT, do których należy klucz prywatny używany do podpisywania konto usługi Google. Usługę Google można określić na kilka sposobów konto, które powinno być używane przez pakiet Firebase Admin SDK do podpisywania niestandardowego tokeny:

  • Użycie pliku JSON konta usługi – Metoda ta może być używana w każdym środowisko, ale wymaga pakietu pliku JSON konta usługi wraz z kodem. Zachowaj szczególną ostrożność, aby sprawdzić, czy Plik JSON konta usługi nie jest dostępny dla osób zewnętrznych.
  • Zezwalanie pakietowi Admin SDK na wykrywanie konta usługi -- Ta metoda można używać w środowiskach zarządzanych przez Google, takich jak Google Cloud Funkcje i App Engine. Konieczne może być skonfigurowanie dodatkowych uprawnień w konsoli Google Cloud.
  • Używanie identyfikatora konta usługi – W środowisku zarządzanym przez Google ta metoda będzie podpisywać tokeny za pomocą: klucza określonego konta usługi. Korzysta jednak ze zdalnej usługi sieciowej i może być konieczne skonfigurowanie dodatkowych uprawnień dla tego konta usługi za pośrednictwem Konsola Google Cloud.

Za pomocą pliku JSON konta usługi

Pliki JSON konta usługi zawierają wszystkie informacje powiązane z usługą (w tym klucz prywatny RSA). Można je pobrać z Konsola Firebase. Postępuj zgodnie z instrukcjami konfiguracji pakietu Admin SDK , gdzie znajdziesz więcej informacji o tym, zainicjować pakiet Admin SDK plikiem JSON konta usługi.

Ta metoda inicjowania jest odpowiednia w przypadku wielu różnych pakietów Admin SDK wdrożeniach. Umożliwia też tworzenie i podpisywanie tokenów niestandardowych za pomocą pakietu Admin SDK. lokalnie, bez wykonywania zdalnych wywołań interfejsu API. Główną wadą polega na tym, że musisz spakować plik JSON konta usługi wraz z kodem. Pamiętaj też, że klucz prywatny konta usługi Plik JSON zawiera informacje poufne i należy zachować szczególną ostrożność, aby je poufne. W szczególności nie dodawaj plików JSON konta usługi do publicznej kontroli wersji.

Zezwalanie pakietowi Admin SDK na wykrywanie konta usługi

Jeśli Twój kod jest wdrożony w środowisku zarządzanym przez Google, pakiet Admin SDK może spróbować automatycznie wykryć sposób podpisywania tokenów niestandardowych:

  • Jeśli Twój kod jest wdrożony w standardowym środowisku App Engine w usłudze Java, Python lub Go, pakiet Admin SDK może używać Usługa tożsamości aplikacji dostępnych w tym środowisku do podpisywania tokenów niestandardowych. Usługa tożsamości aplikacji podpisuje dane za pomocą konta usługi udostępnionego przez aplikację Google Wyszukiwarka.

  • Jeśli Twój kod został wdrożony w innym środowisku zarządzanym (np. Google Cloud) Funkcje, Google Compute Engine), pakiet Firebase Admin SDK może automatycznie wykryć ciąg identyfikatora konta usługi z lokalnego serwer metadanych. Wykryty identyfikator konta usługi jest następnie używany w połączeniu z uprawnieniami do zdalnego podpisywania tokenów.

Aby korzystać z tych metod podpisywania, zainicjuj pakiet SDK w Google Domyślne dane logowania aplikacji i nie określaj ciągu identyfikatora konta usługi:

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

Aby przetestować ten sam kod lokalnie, pobierz plik JSON konta usługi i ustaw Zmienna środowiskowa GOOGLE_APPLICATION_CREDENTIALS wskazująca ją.

Jeśli pakiet SDK Firebase Admin musi wykryć ciąg identyfikatora konta usługi, więc gdy kod tworzy token niestandardowy po raz pierwszy. Wynik jest przechowywany w pamięci podręcznej i używany ponownie podczas kolejnych operacji podpisywania tokenów. Identyfikator automatycznie wykrytego konta usługi jest zwykle jedną z usług domyślnych konta dostarczone przez Google Cloud:

Podobnie jak w przypadku bezpośrednio określonych identyfikatorów kont usługi, usługa automatycznie wykrywana identyfikatory kont muszą mieć uprawnienie iam.serviceAccounts.signBlob w przypadku niestandardowego tokenu. Konieczne może być użycie Sekcja Administracja konsoli Google Cloud, aby przyznać domyślne konta usługi niezbędnych uprawnień. Więcej informacji znajdziesz w sekcji rozwiązywania problemów poniżej.

Używanie identyfikatora konta usługi

Aby zachować spójność między różnymi częściami aplikacji, możesz określ identyfikator konta usługi, którego klucze będą używane do podpisywania tokenów podczas działania w środowisku zarządzanym przez Google. Może to uprościć i zwiększyć bezpieczeństwo zasad uprawnień, a także uniknąć konieczności umieść w kodzie plik JSON konta usługi.

Identyfikator konta usługi znajdziesz w konsola Google Cloud, lub w polu client_email pobranego pliku JSON konta usługi. Identyfikatory kont usługi to adresy e-mail w tym formacie: <client-id>@<project-id>.iam.gserviceaccount.com Identyfikują one jednoznacznie kont usługi w Firebase i Google Cloud projektach.

Aby utworzyć tokeny niestandardowe przy użyciu osobnego identyfikatora konta usługi, zainicjuj pakiet SDK jak poniżej:

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

Identyfikatory kont usługi nie są informacjami poufnymi, więc ich ujawnienie jest bez znaczenia. Jednak aby podpisywać tokeny niestandardowe za pomocą określonej usługi pakiet SDK Firebase Admin musi wywołać usługę zdalną. Upewnij się też, że konto usługi, do którego jest używany pakiet Admin SDK, użyj, aby nawiązać to połączenie – zwykle {project-name}@appspot.gserviceaccount.com– ma iam.serviceAccounts.signBlob uprawnienia. Więcej informacji znajdziesz w sekcji rozwiązywania problemów poniżej.

Tworzenie tokenów niestandardowych za pomocą pakietu Firebase Admin SDK

Pakiet SDK Firebase Admin ma wbudowaną metodę tworzenia tokenów niestandardowych. Na minimalnie, musisz podać uid, który może być dowolnym ciągiem znaków, ale powinien jednoznacznie identyfikują użytkownika lub urządzenie, które uwierzytelniasz. Te tokeny wygasają po godzinie.

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

Możesz też opcjonalnie określić dodatkowe roszczenia, które chcesz uwzględnić w niestandardowym token. Przykładowo poniżej dodano pole premiumAccount do token niestandardowy, który będzie dostępny w obiektach auth / request.auth w Regułach zabezpieczeń:

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

Zarezerwowane nazwy tokenów niestandardowych

Logowanie się przy użyciu tokenów niestandardowych na kontach klientów

Po utworzeniu tokena niestandardowego wyślij go do aplikacji klienckiej. aplikacja kliencka uwierzytelnia się za pomocą tokena niestandardowego przez wywołanie 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;
    // ...
  });

Jeśli uwierzytelnianie się powiedzie, użytkownik zostanie zalogowany na aplikacja kliencka z kontem określonym w polu uid uwzględnionym w niestandardowym token. Jeśli to konto wcześniej nie istniało, rekord dla tego użytkownika zostanie Utworzono.

Tak samo jak w przypadku innych metod logowania (np. signInWithEmailAndPassword() i signInWithCredential()) obiekt auth w Realtime Database Security Rules oraz obiekt request.auth w Cloud Storage Security Rules będzie zawiera wartość uid użytkownika. W tym przypadku uid będzie tym, który określonego podczas generowania tokena niestandardowego.

Reguły bazy danych

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

Reguły przechowywania

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

Jeśli token niestandardowy zawiera dodatkowe żądania, można się do nich odwoływać w auth.token (Firebase Realtime Database) lub request.auth.token (Cloud Storage) w regułach:

Reguły bazy danych

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

Reguły przechowywania

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

Tworzenie tokenów niestandardowych za pomocą biblioteki JWT innej firmy

Jeśli backend jest w języku, który nie ma oficjalnego administratora Firebase SDK, nadal możesz ręcznie tworzyć tokeny niestandardowe. Po pierwsze, Znajdź zewnętrzną bibliotekę JWT dla swojego języka. Następnie użyj funkcji tę bibliotekę JWT do utworzenia tokena JWT, który zawiera następujące informacje:

Deklaracje tokenów niestandardowych
alg Algorytm "RS256"
iss Wystawca Adres e-mail konta usługi projektu
sub Temat Adres e-mail konta usługi projektu
aud Odbiorcy "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat Wystawiony w momencie Bieżący czas (w sekundach) od początku epoki UNIX
exp Okres ważności Czas (w sekundach) od początku epoki systemu UNIX, w którym wygasa token. it może być maksymalnie 3600 sekund później niż iat.
Uwaga: określa to tylko czas wystąpienia tokenu niestandardowego. wygasa. Gdy jednak zalogujesz się za pomocą signInWithCustomToken(), pozostanie zalogowany(-a) w aplikacji do czasu unieważnienia sesji lub wylogowania się użytkownika.
uid Unikalny identyfikator zalogowanego użytkownika musi być ciągiem znaków z zakresu Długość od 1 do 128 znaków. Krótsze uid są lepsze skuteczność reklam.
claims (opcjonalnie) Opcjonalne żądania niestandardowe, które mają być uwzględnione w regułach zabezpieczeń auth / request.auth zmiennych

Oto kilka przykładów implementacji tokenów niestandardowych w różne języki, których nie obsługuje pakiet Firebase Admin SDK:

PHP

Przy użyciu 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

Przy użyciu 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

Po utworzeniu tokena niestandardowego wyślij go do aplikacji klienckiej, aby został użyty w: uwierzytelniać się w Firebase. Aby dowiedzieć się, jak to zrobić, zapoznaj się z przykładami powyżej.

Rozwiązywanie problemów

W tej sekcji omawiamy kilka częstych problemów, które deweloperzy mogą napotkać podczas jak tworzyć tokeny niestandardowe i jak z nimi korzystać.

Nie włączono interfejsu IAM API

Jeśli określasz identyfikator konta usługi na potrzeby tokenów podpisywania, możesz otrzymać podobny błąd:

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.

Pakiet Firebase Admin SDK używa IAM API aby podpisywać tokeny. Ten błąd oznacza, że interfejs IAM API nie jest obecnie włączony dla projekt Firebase. Otwórz w przeglądarce link w komunikacie o błędzie, kliknij „Włącz API”. aby włączyć ją w projekcie.

Konto usługi nie ma wymaganych uprawnień

Jeśli na koncie usługi działa pakiet Firebase Admin SDK, który nie ma iam.serviceAccounts.signBlob, może pojawić się komunikat o błędzie, taki jak następujące:

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

Najłatwiejszym sposobem rozwiązania tego problemu jest przyznanie uprawnienia „twórcy tokenów konta usługi” rolę uprawnień w odniesieniu do danego konta usługi, zwykle {project-name}@appspot.gserviceaccount.com:

  1. Otwórz Administracja. w konsoli Google Cloud.
  2. Wybierz projekt i kliknij „Dalej”.
  3. Kliknij ikonę edycji odpowiadającą kontu usługi, które chcesz zaktualizować.
  4. Kliknij „Dodaj kolejną rolę”.
  5. Wpisz „Twórca tokenów konta usługi” do filtra wyszukiwania i wybierz z wyników wyszukiwania.
  6. Kliknij „Zapisz”. aby potwierdzić przypisanie roli.

Zapoznaj się z dokumentacją uprawnień. , aby dowiedzieć się więcej o tym procesie lub jak zaktualizować role za pomocą w narzędziach wiersza poleceń gcloud.

Nie udało się określić konta usługi

Jeśli pojawi się komunikat o błędzie podobny do tego poniżej, oznacza to, że pakiet Firebase Admin SDK nie został poprawnie zainicjowany.

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

Jeśli używasz pakietu SDK do automatycznego wykrywania identyfikatora konta usługi, sprawdź, kod jest wdrażany w zarządzanym środowisku Google z serwerem metadanych. W przeciwnym razie podaj plik JSON konta usługi lub identyfikator konta usługi podczas inicjowania pakietu SDK.