Twórz niestandardowe tokeny

Firebase zapewnia pełną kontrolę nad uwierzytelnianiem, umożliwiając uwierzytelnianie użytkowników lub urządzeń przy użyciu 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ą metodysignInWithCustomToken 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 następnie zostać 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 baza danych Firebase Realtime Database i Cloud Storage. Co więcej, zawartość 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ą pakietu 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 natywnie nie obsługuje.

Zanim zaczniesz

Tokeny niestandardowe to podpisane 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 tokenów niestandardowych:

  • Korzystanie z pliku JSON konta usługi — tej metody można użyć 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ł udostępniony stronom zewnętrznym.
  • Zezwolenie 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 konsoli Google Cloud.
  • Korzystanie z 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 konsoli Google Cloud.

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 na temat inicjowania pakietu Admin SDK za pomocą pliku JSON konta usługi.

Ta metoda inicjalizacji jest odpowiednia w przypadku 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ń 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ć jego poufność. 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 zostanie wdrożony 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 został wdrożony w standardowym środowisku App Engine dla języka Java, Python lub Go, pakiet Admin SDK może używać usługi App Identity dostępnej w tym środowisku do podpisywania niestandardowych tokenów. Usługa App Identity podpisuje dane przy użyciu konta usługi udostępnionego dla Twojej aplikacji przez Google App Engine.

  • Jeśli Twój kod zostanie 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 . Odkryty 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 uwierzytelniających 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 po raz pierwszy tworzy token niestandardowy. Wynik jest buforowany i ponownie wykorzystywany w kolejnych operacjach podpisywania tokenu. Automatycznie wykryty identyfikator konta usługi to zazwyczaj jedno z domyślnych kont usług udostępnianych przez Google Cloud:

Podobnie jak w przypadku jawnie określonych identyfikatorów kont usług, automatycznie wykryte identyfikatory kont usług muszą mieć uprawnienie iam.serviceAccounts.signBlob , aby tworzenie niestandardowego tokenu działało. Być może będziesz musiał użyć sekcji IAM i administratora w konsoli Google Cloud, aby przyznać domyślnym kontom usług niezbędne uprawnienia. Aby uzyskać więcej informacji, zobacz sekcję dotyczącą rozwiązywania problemów poniżej.

Korzystanie z identyfikatora konta usługi

Aby zachować spójność pomię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 działania w środowisku zarządzanym przez Google. Może to sprawić, że zasady uprawnień będą prostsze i bezpieczniejsze oraz pozwolą uniknąć konieczności dołączania pliku JSON konta usługi do kodu.

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

Aby utworzyć tokeny niestandardowe przy użyciu osobnego 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 kont usług nie są informacjami wrażliwymi i dlatego ich ujawnienie jest nieistotne. Aby jednak podpisać tokeny niestandardowe za pomocą określonego konta usługi, pakiet SDK administratora Firebase musi wywołać usługę zdalną. Ponadto musisz także upewnić się, że konto usługi, którego używa pakiet Admin SDK do wykonywania tego wywołania — zwykle {project-name}@appspot.gserviceaccount.com — ma uprawnienie iam.serviceAccounts.signBlob . Aby uzyskać więcej informacji, zobacz sekcję dotyczącą rozwiązywania problemów poniżej.

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 znaków, ale powinien jednoznacznie identyfikować użytkownika lub urządzenie, które uwierzytelniasz. Tokeny te 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

Opcjonalnie możesz również określić dodatkowe oświadczenia, które mają zostać uwzględnione w tokenie niestandardowym. Przykładowo poniżej do niestandardowego tokena dodano 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ą tokena niestandardowego, wywołując metodę 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łowe API sieciowe

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

Podobnie jak w przypadku innych metod logowania (takich signInWithEmailAndPassword() 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 tym przypadku uid będzie ten, który podałeś podczas generowania tokenu niestandardowego.

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 token niestandardowy zawiera dodatkowe oświadczenia, można się do nich odwoływać w regułach za pomocą obiektu auth.token (baza danych Firebase Realtime) lub request.auth.token (Cloud Storage):

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, korzystając z biblioteki JWT innej firmy

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

Niestandardowe oświadczenia dotyczące 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 Wydane w odpowiednim czasie Bieżący czas w sekundach od epoki UNIX
exp Data ważności Czas w sekundach od epoki UNIX, kiedy token wygasa. Może to być maksymalnie 3600 sekund później niż iat .
Uwaga: kontroluje to tylko czas wygaśnięcia samego tokena niestandardowego . Jednak gdy zarejestrujesz użytkownika za pomocą signInWithCustomToken() , pozostanie on zalogowany na urządzeniu do czasu unieważnienia sesji lub wylogowania się użytkownika.
uid Unikalny identyfikator zalogowanego użytkownika musi być ciągiem znaków o długości od 1 do 128 znaków włącznie. Krótsze uid zapewniają lepszą wydajność.
claims (opcjonalnie) Opcjonalne oświadczenia niestandardowe, które można uwzględnić w zmiennych auth / request.auth reguł zabezpieczeń

Oto kilka przykładowych implementacji tworzenia niestandardowych tokenów w różnych językach, których nie obsługuje pakiet 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żywając 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 tokenu niestandardowego wyślij go do aplikacji klienckiej, aby użyć go do 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, jakie mogą napotkać programiści podczas tworzenia tokenów niestandardowych, oraz sposoby ich rozwiązania.

Nie włączono interfejsu IAM API

Jeśli podasz identyfikator konta usługi do podpisywania tokenów, może pojawić się błąd podobny do następującego:

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 SDK administratora Firebase używa interfejsu API IAM do podpisywania tokenów. Ten błąd wskazuje, że interfejs IAM API 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 dla swojego projektu.

Konto usługi nie ma wymaganych uprawnień

Jeśli konto usługi, na którym działa zestaw SDK administratora Firebase, nie ma uprawnienia iam.serviceAccounts.signBlob , może pojawić się komunikat o błędzie podobny do następującego:

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

Najprostszym sposobem rozwiązania tego problemu jest przyznanie roli IAM „Kreator tokenu konta usługi” danemu kontu usługi, zwykle {project-name}@appspot.gserviceaccount.com :

  1. Otwórz stronę IAM i administratora w konsoli Google Cloud.
  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 „Kreator tokenu konta usługi” w filtrze wyszukiwania i wybierz go z wyników.
  6. Kliknij „Zapisz”, aby potwierdzić przydzielenie roli.

Więcej szczegółów na temat tego procesu znajdziesz w dokumentacji uprawnień lub dowiedz się, jak zaktualizować 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 SDK administratora Firebase 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, upewnij się, że kod został wdrożony 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.