Twórz niestandardowe tokeny

Firebase zapewnia pełną kontrolę nad uwierzytelnianiem, umożliwiając uwierzytelnianie użytkowników lub urządzeń za pomocą bezpiecznych tokenów internetowych JSON (JWT). Generujesz te tokeny na swoim serwerze, przekazujesz je z powrotem do urządzenia klienckiego, a następnie używasz ich do uwierzytelnienia za pomocą metody signInWithCustomToken() .

Aby to osiągnąć, należy utworzyć punkt końcowy serwera, który akceptuje poświadczenia logowania — takie jak nazwa użytkownika i hasło — i jeśli poświadczenia są prawidłowe, zwraca niestandardowy token JWT. Niestandardowy token JWT zwrócony z serwera może być następnie użyty przez urządzenie klienckie do uwierzytelnienia w Firebase ( iOS+ , Android , web ). Po uwierzytelnieniu ta tożsamość będzie używana podczas uzyskiwania dostępu do innych usług Firebase, takich jak Firebase Realtime Database i Cloud Storage. Ponadto zawartość tokenu JWT będzie dostępna w obiekcie auth w Regułach bezpieczeństwa bazy danych czasu rzeczywistego oraz w obiekcie request.auth w Regułach bezpieczeństwa Cloud Storage .

Możesz utworzyć niestandardowy token za pomocą Firebase Admin SDK lub możesz użyć biblioteki JWT innej firmy, jeśli Twój serwer jest napisany w języku, którego Firebase nie obsługuje natywnie.

Zanim zaczniesz

Tokeny niestandardowe to podpisane tokeny JWT, w których klucz prywatny używany do podpisywania należy do konta usługi Google. Istnieje kilka sposobów określenia konta usługi Google, które powinno być używane przez pakiet Firebase Admin SDK do podpisywania niestandardowych tokenów:

  • Korzystanie z pliku JSON konta usługi — tej metody można używać w dowolnym środowisku, ale wymaga spakowania pliku JSON konta usługi wraz z kodem. Należy zachować szczególną ostrożność, aby plik JSON konta usługi nie był narażony na działanie stron zewnętrznych.
  • Zezwalanie pakietowi Admin SDK na wykrycie konta usługi — tej metody można używać w środowiskach zarządzanych przez Google, takich jak Google Cloud Functions i App Engine. Może być konieczne skonfigurowanie dodatkowych uprawnień za pośrednictwem Google Cloud Console.
  • Używanie identyfikatora konta usługi — w przypadku użycia w środowisku zarządzanym przez Google ta metoda będzie podpisywać tokeny przy użyciu klucza określonego konta usługi. Korzysta jednak ze zdalnej usługi internetowej i może być konieczne skonfigurowanie dodatkowych uprawnień dla tego konta usługi za pośrednictwem Google Cloud Console.

Korzystanie z pliku JSON konta usługi

Pliki JSON konta usługi zawierają wszystkie informacje odpowiadające kontom usługi (w tym klucz prywatny RSA). Można je pobrać z konsoli Firebase. Postępuj zgodnie z instrukcjami konfiguracji pakietu Admin SDK, aby uzyskać więcej informacji o inicjowaniu pakietu Admin SDK przy użyciu pliku JSON konta usługi.

Ta metoda inicjowania jest odpowiednia dla szerokiego zakresu wdrożeń pakietu Admin SDK. Umożliwia także pakietowi Admin SDK tworzenie i podpisywanie niestandardowych tokenów lokalnie, bez wykonywania jakichkolwiek zdalnych wywołań interfejsu API. Główną wadą tego podejścia jest to, że wymaga spakowania pliku JSON konta usługi wraz z kodem. Należy również pamiętać, że klucz prywatny w pliku JSON konta usługi to informacja poufna i należy zachować szczególną ostrożność, aby zachować ją w poufności. W szczególności powstrzymaj się od dodawania 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 wdrażany w środowisku zarządzanym przez Google, pakiet Admin SDK może podjąć próbę automatycznego wykrycia sposobu podpisywania niestandardowych tokenów:

  • Jeśli Twój kod jest wdrożony w standardowym środowisku App Engine dla języka Java, Python lub Go, pakiet Admin SDK może używać usługi tożsamości aplikacji dostępnej w tym środowisku do podpisywania niestandardowych tokenów. Usługa App Identity podpisuje dane przy użyciu konta usługi obsługiwanego dla Twojej aplikacji przez Google App Engine.

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

Aby skorzystać z tych metod podpisywania, zainicjuj pakiet SDK przy użyciu domyślnych danych logowania aplikacji Google i nie podawaj ciągu identyfikatora konta usługi:

Node.js

initializeApp();

Jawa

FirebaseApp.initializeApp();

Pyton

default_app = firebase_admin.initialize_app()

Iść

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 zmienną środowiskową GOOGLE_APPLICATION_CREDENTIALS tak, aby na niego wskazywała.

Jeśli pakiet Firebase Admin SDK musi wykryć ciąg identyfikatora konta usługi, robi to, gdy Twój kod tworzy niestandardowy token po raz pierwszy. Wynik jest buforowany i ponownie używany do kolejnych operacji podpisywania tokenu. Automatycznie wykryty identyfikator konta usługi jest zwykle jednym z domyślnych kont usług udostępnianych przez Google Cloud:

Podobnie jak w przypadku jawnie określonych identyfikatorów kont usług, automatycznie wykrywane identyfikatory kont usług muszą mieć uprawnienie iam.serviceAccounts.signBlob , aby tworzenie tokenów niestandardowych działało. Może być konieczne skorzystanie z sekcji IAM i admin w Google Cloud Console, aby przyznać domyślnym kontom usługi niezbędne uprawnienia. Zobacz sekcję rozwiązywania problemów poniżej, aby uzyskać więcej informacji.

Używanie identyfikatora konta usługi

Aby zachować spójność między różnymi częściami aplikacji, możesz określić identyfikator konta usługi, którego klucze będą używane do podpisywania tokenów podczas uruchamiania w środowisku zarządzanym przez Google. Dzięki temu zasady IAM mogą być prostsze i bezpieczniejsze, a także uniknąć konieczności dołączania pliku JSON konta usługi do kodu.

Identyfikator konta usługi można znaleźć w Google Cloud Console lub w polu client_email pobranego pliku JSON konta usługi. Identyfikatory konta usługi to adresy e-mail o następującym formacie: <client-id>@<project-id>.iam.gserviceaccount.com . W unikalny sposób identyfikują konta usług w projektach Firebase i Google Cloud.

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

Node.js

initializeApp({
  serviceAccountId: 'my-client-id@my-project-id.iam.gserviceaccount.com',
});

Jawa

FirebaseOptions options = FirebaseOptions.builder()
    .setCredentials(GoogleCredentials.getApplicationDefault())
    .setServiceAccountId("my-client-id@my-project-id.iam.gserviceaccount.com")
    .build();
FirebaseApp.initializeApp(options);

Pyton

options = {
    'serviceAccountId': 'my-client-id@my-project-id.iam.gserviceaccount.com',
}
firebase_admin.initialize_app(options=options)

Iść

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 konta usługi nie są informacjami wrażliwymi, dlatego ich ujawnienie jest nieistotne. Aby jednak podpisać niestandardowe tokeny przy użyciu określonego konta usługi, pakiet Firebase Admin SDK musi wywołać usługę zdalną. Ponadto musisz również upewnić się, że konto usługi używane przez pakiet Admin SDK do wykonania tego wywołania — zwykle {project-name}@appspot.gserviceaccount.com — ma uprawnienie iam.serviceAccounts.signBlob . Zobacz sekcję rozwiązywania problemów poniżej, aby uzyskać więcej informacji.

Twórz niestandardowe tokeny za pomocą pakietu Firebase Admin SDK

Pakiet Firebase Admin SDK ma wbudowaną metodę tworzenia niestandardowych tokenów. Jako minimum musisz podać uid , który może być dowolnym ciągiem, ale powinien jednoznacznie identyfikować użytkownika lub urządzenie, które uwierzytelniasz. Te tokeny tracą ważność 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);
  });

Jawa

String uid = "some-uid";

String customToken = FirebaseAuth.getInstance().createCustomToken(uid);
// Send token back to client

Pyton

uid = 'some-uid'

custom_token = auth.create_custom_token(uid)

Iść

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 również opcjonalnie określić dodatkowe oświadczenia, które mają zostać uwzględnione w tokenie niestandardowym. Na przykład poniżej do niestandardowego tokena zostało dodane pole premiumAccount , które będzie dostępne w obiektach auth / request.auth w Twoich Regułach bezpieczeństwa:

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

Jawa

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

Pyton

uid = 'some-uid'
additional_claims = {
    'premiumAccount': True
}

custom_token = auth.create_custom_token(uid, additional_claims)

Iść

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 niestandardowe nazwy tokenów

Zaloguj się przy użyciu niestandardowych tokenów na klientach

Po utworzeniu tokenu niestandardowego należy wysłać go do aplikacji klienckiej. Aplikacja kliencka uwierzytelnia się za pomocą niestandardowego tokena, wywołując funkcję signInWithCustomToken() :

iOS+

Cel C
[[FIRAuth auth] signInWithCustomToken:customToken
                           completion:^(FIRAuthDataResult * _Nullable authResult,
                                        NSError * _Nullable error) {
  // ...
}];
Szybki
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);
                }
            }
        });

Jedność

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

Internetowy interfejs API z przestrzenią nazw

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

Modułowy interfejs API sieci 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 powiedzie się, użytkownik zostanie teraz zalogowany do aplikacji klienckiej przy użyciu konta określonego przez identyfikator uid zawarty w tokenie niestandardowym. Jeśli to konto nie istniało wcześniej, zostanie utworzony rekord dla tego użytkownika.

W taki sam sposób jak w przypadku innych metod logowania (takich jak signInWithEmailAndPassword() i signInWithCredential() ) obiekt auth w regułach bezpieczeństwa bazy danych czasu rzeczywistego i obiekt request.auth w regułach bezpieczeństwa Cloud Storage zostaną wypełnione uid użytkownika . W takim przypadku uid będzie taki sam, jak określono podczas generowania niestandardowego tokena.

Zasady bazy danych

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

Zasady 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 niestandardowy token zawiera dodatkowe roszczenia, można się do nich odwoływać poza obiektem auth.token (baza danych czasu rzeczywistego Firebase) lub request.auth.token (Cloud Storage) w twoich regułach:

Zasady bazy danych

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

Zasady przechowywania

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

Twórz niestandardowe tokeny przy użyciu biblioteki JWT innej firmy

Jeśli Twój backend jest w języku, który nie ma oficjalnego Firebase Admin SDK, nadal możesz ręcznie tworzyć niestandardowe tokeny. Najpierw znajdź bibliotekę JWT innej firmy dla swojego języka. Następnie użyj tej biblioteki JWT, aby wybić JWT, który zawiera następujące roszczenia:

Niestandardowe żądania tokenów
alg Algorytm "RS256"
iss Emitent Adres e-mail konta usługi Twojego projektu
sub Temat Adres e-mail konta usługi Twojego projektu
aud Publiczność "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat Wydawane w czasie Bieżący czas w sekundach od epoki UNIX
exp Data ważności Czas w sekundach od epoki systemu UNIX, w którym token traci ważność. Może to być maksymalnie 3600 sekund później niż iat .
Uwaga: kontroluje to tylko czas wygaśnięcia tokena niestandardowego . Jednak po zalogowaniu użytkownika przy użyciu signInWithCustomToken() pozostanie on zalogowany na urządzeniu do momentu unieważnienia sesji lub wylogowania się użytkownika.
uid Unikalny identyfikator zalogowanego użytkownika musi być ciągiem o długości od 1 do 128 znaków włącznie. Krótsze uid oferują lepszą wydajność.
claims (opcjonalnie) Opcjonalne niestandardowe oświadczenia do uwzględnienia w regułach bezpieczeństwa zmiennych auth / request.auth

Oto kilka przykładowych implementacji tworzenia niestandardowych tokenów w różnych językach, których nie obsługuje Firebase Admin SDK:

PHP

Używając 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

Używanie 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 niestandardowego tokenu wyślij go do aplikacji klienckiej w celu uwierzytelnienia w Firebase. Zobacz przykłady kodu powyżej, aby dowiedzieć się, jak to zrobić.

Rozwiązywanie problemów

W tej sekcji opisano niektóre typowe problemy, które programiści mogą napotkać podczas tworzenia niestandardowych tokenów, oraz sposoby ich rozwiązywania.

Interfejs API uprawnień nie jest włączony

Jeśli określasz identyfikator konta usługi do podpisywania tokenów, może pojawić się błąd podobny do tego:

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 interfejsu IAM API do podpisywania tokenów. Ten błąd wskazuje, że interfejs API IAM nie jest obecnie włączony w Twoim projekcie Firebase. Otwórz łącze w komunikacie o błędzie w przeglądarce internetowej i kliknij przycisk „Włącz interfejs API”, aby włączyć go w swoim projekcie.

Konto usługi nie ma wymaganych uprawnień

Jeśli konto usługi, na którym działa pakiet Firebase Admin SDK, nie ma uprawnienia iam.serviceAccounts.signBlob , może zostać wyświetlony komunikat o błędzie podobny do tego:

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 nadanie kontu usługi roli IAM „Twórca tokenu konta usługi”, zwykle {project-name}@appspot.gserviceaccount.com :

  1. Otwórz stronę uprawnień i administratora w Google Cloud Console.
  2. Wybierz swój projekt i kliknij „Kontynuuj”.
  3. Kliknij ikonę edycji odpowiadającą kontu usługi, które chcesz zaktualizować.
  4. Kliknij „Dodaj kolejną rolę”.
  5. Wpisz „Service Account Token Creator” w filtrze wyszukiwania i wybierz go z wyników.
  6. Kliknij „Zapisz”, aby potwierdzić przyznanie roli.

Zapoznaj się z dokumentacją uprawnień, aby uzyskać więcej informacji na temat tego procesu, lub dowiedz się, jak aktualizować role za pomocą narzędzi wiersza poleceń gcloud.

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

Jeśli pojawi się komunikat o błędzie podobny do poniższego, oznacza to, że pakiet Firebase Admin SDK nie został prawidłowo 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 polegasz na pakiecie SDK do automatycznego wykrywania identyfikatora konta usługi, upewnij się, że kod jest wdrażany w zarządzanym środowisku Google z serwerem metadanych. W przeciwnym razie pamiętaj o określeniu pliku JSON konta usługi lub identyfikatora konta usługi podczas inicjowania zestawu SDK.