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). Te tokeny generujesz na serwerze, przekazujesz je z powrotem do urządzenia klienta, a następnie używasz ich do uwierzytelniania za pomocą metody signInWithCustomToken().
Aby to zrobić, musisz utworzyć punkt końcowy serwera, który akceptuje dane logowania, takie jak nazwa użytkownika i hasło, i jeśli są one prawidłowe, zwraca niestandardowy token JWT. Niestandardowy token JWT zwrócony z serwera może być następnie używany przez urządzenie klienta do uwierzytelniania w Firebase (iOS+, Android, sieć). 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ść tokena JWT będzie dostępna w obiekcie auth w Realtime Database Security Rules i obiekcie request.auth w Cloud Storage Security Rules.
Możesz utworzyć token niestandardowy za pomocą pakietu Firebase Admin SDK lub użyć zewnętrznej biblioteki JWT, 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 tokenów niestandardowych:
- Umożliwienie pakietowi Admin SDK wykrycia 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ń w konsoli Google Cloud.
- Użycie identyfikatora konta usługi – ta metoda podpisuje tokeny przy użyciu kluczy zarządzanych przez Google dla określonego konta usługi, dzięki czemu nie musisz samodzielnie obsługiwać kluczy prywatnych. Używa zdalnej usługi internetowej i może być konieczne skonfigurowanie dodatkowych uprawnień dla tego konta usługi w konsoli Google Cloud.
- Użycie pliku klucza konta usługi – użyj tej metody tylko wtedy, gdy kod jest uruchamiany poza środowiskiem zarządzanym przez Google i nie możesz użyć żadnej z powyższych metod. Wymaga utworzenia, przechowywania i ochrony długotrwałego klucza prywatnego.
Zalecamy 2 pierwsze metody, ponieważ nie wymagają one pobierania kluczy kont usługi ani zarządzania nimi.
Umożliwianie pakietowi Admin SDK wykrywania konta usługi
Jeśli kod jest wdrażany 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 środowisku standardowym App Engine dla Javy, Pythona lub Go, pakiet Admin SDK może używać usługi tożsamości aplikacji obecnej w tym środowisku do podpisywania tokenów niestandardowych. Usługa tożsamości aplikacji podpisuje dane za pomocą konta usługi udostępnionego dla aplikacji przez Google App Engine.
Jeśli kod jest wdrażany w innym środowisku zarządzanym (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ą uprawnień do zdalnego podpisywania tokenów.
Aby korzystać 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();
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 bez pobierania klucza konta usługi, zaloguj się za pomocą własnych danych logowania użytkownika przy użyciu Google Cloud CLI:
gcloud auth application-default login
Na komputerze lokalnym nie ma serwera metadanych, więc pakiet Admin SDK nie może automatycznie wykryć identyfikatora konta usługi. Zamiast tego podczas inicjowania pakietu SDK określ identyfikator konta usługi i upewnij się, że Twoje konto użytkownika ma rolę twórcy tokenów konta usługi na tym koncie usługi.
Jeśli pakiet Firebase Admin SDK musi wykryć ciąg identyfikatora konta usługi, robi to, gdy kod po raz pierwszy tworzy token niestandardowy. Wynik jest zapisywany w pamięci podręcznej i używany ponownie w kolejnych operacjach podpisywania tokenów. Identyfikator automatycznie wykrytego konta usługi to zwykle jedno z domyślnych kont usługi udostępnianych przez Google Cloud:
Podobnie jak w przypadku jawnie określonych identyfikatorów kont usługi, automatycznie wykryte identyfikatory kont usługi muszą mieć uprawnienie iam.serviceAccounts.signBlob, aby tworzenie tokenów niestandardowych działało. Aby przyznać domyślnym kontom usługi niezbędne uprawnienia, może być konieczne użycie sekcji Administracja i uprawnienia w konsoli Google Cloud. Więcej informacji znajdziesz w sekcji dotyczącej 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ślić identyfikator konta usługi, którego klucze zarządzane przez Google będą używane do podpisywania tokenów. Dzięki temu zasady uprawnień mogą być prostsze i bezpieczniejsze, a Ty nie musisz pobierać, przechowywać ani wdrażać klucza konta usługi.
Identyfikator konta usługi znajdziesz w konsoli Google Cloud.
Identyfikatory kont usługi to adresy e-mail w tym formacie: <client-id>@<project-id>.iam.gserviceaccount.com. Jednoznacznie identyfikują konta usługi w projektach Firebase i Google Cloud.
Aby utworzyć tokeny niestandardowe za pomocą osobnego identyfikatora konta usługi, zainicjuj pakiet SDK w sposób pokazany 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 nie ma znaczenia. Aby jednak podpisywać tokeny niestandardowe za pomocą określonego konta usługi, pakiet Firebase Admin SDK musi wywołać usługę zdalną.
Musisz też upewnić się, że tożsamość, której pakiet Admin SDK używa do wykonania tego wywołania (zwykle {project-name}@appspot.gserviceaccount.com w środowisku produkcyjnym lub Twoje własne konto użytkownika podczas programowania lokalnego), ma uprawnienie iam.serviceAccounts.signBlob.
Więcej informacji znajdziesz w sekcji dotyczącej rozwiązywania problemów poniżej.
Używanie pliku klucza konta usługi
Jeśli Twój kod działa w środowisku poza Google Cloud, w którym żadna z powyższych metod nie jest dostępna, możesz zainicjować pakiet Admin SDK za pomocą pliku klucza konta usługi. Szczegółowe informacje znajdziesz w instrukcjach konfiguracji pakietu Admin SDK. W przypadku tej metody pakiet Admin SDK podpisuje tokeny niestandardowe lokalnie przy użyciu klucza prywatnego w pliku, bez wykonywania zdalnych wywołań interfejsu API.
Klucze kont usługi to długotrwałe dane uwierzytelniające. Jeśli klucz zostanie ujawniony, każda osoba, która go ma, może działać jako konto usługi, dopóki klucz nie zostanie usunięty. Nigdy nie zatwierdzaj plików kluczy w systemie kontroli wersji i rozważ użycie federacji tożsamości zadań zamiast kluczy, jeśli Twoje środowisko to obsługuje. Pamiętaj też, że niektóre organizacje używają ograniczeń zasad organizacji, aby uniemożliwić tworzenie kluczy kont usługi. W takim przypadku musisz użyć jednej z innych metod.
Tworzenie tokenów niestandardowych za pomocą pakietu Firebase Admin SDK
Pakiet Firebase Admin SDK ma wbudowaną metodę tworzenia tokenów niestandardowych. Musisz podać co najmniej uid, który może być dowolnym ciągiem znaków, ale powinien jednoznacznie identyfikować 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 oświadczenia, które mają być uwzględnione w tokenie niestandardowym. Na przykład poniżej do tokena niestandardowego dodano pole premiumAccount, które będzie dostępne 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
Zastrzeżone nazwy tokenów niestandardowych
Logowanie się przy użyciu tokenów niestandardowych na klientach
Po utworzeniu tokena niestandardowego wyślij go do aplikacji klienckiej. Aplikacja kliencka uwierzytelnia się za pomocą tokena niestandardowego, wywołując 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 w aplikacji klienckiej na konto określone przez uid zawarty w tokenie niestandardowym. Jeśli to konto wcześniej nie istniało, zostanie utworzony rekord dla tego użytkownika.
Podobnie jak w przypadku innych metod logowania (takich jak signInWithEmailAndPassword() i signInWithCredential()) obiekt auth w Realtime Database Security Rules i obiekt request.auth w Cloud Storage Security Rules zostaną wypełnione wartością uid użytkownika. W takim przypadku uid będzie wartością podaną podczas generowania tokena niestandardowego.
Reguły bazy danych
{
"rules": {
"adminContent": {
".read": "auth.uid === 'some-uid'"
}
}
}
Reguły dotyczące miejsca na dane
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 (Firebase Realtime Database) lub request.auth.token (Cloud Storage):
Reguły bazy danych
{
"rules": {
"premiumContent": {
".read": "auth.token.premiumAccount === true"
}
}
}
Reguły dotyczące miejsca na dane
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 Twój backend jest w języku, który nie ma oficjalnego pakietu SDK Firebase Admin, 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ć token JWT zawierający te deklaracje:
| Deklaracje tokenów niestandardowych | ||
|---|---|---|
alg |
Algorytm | "RS256" |
iss |
Wystawca | Adres e-mail konta usługi Twojego projektu |
sub |
Temat | Adres e-mail konta usługi Twojego projektu |
aud |
Odbiorcy | "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit" |
iat |
Czas wystawienia | Bieżący czas w sekundach od początku epoki uniksowej |
exp |
Data ważności |
Czas w sekundach od początku epoki uniksowej, po którym token wygasa. Może to być maksymalnie 3600 sekund później niż iat.
Uwaga: to ustawienie określa tylko czas, po którym wygasa sam token niestandardowy. Jednak po zalogowaniu się użytkownika za pomocą signInWithCustomToken() pozostanie on zalogowany na urządzeniu, dopóki jego sesja nie zostanie unieważniona lub dopóki się nie wyloguje.
|
uid |
Unikalny identyfikator zalogowanego użytkownika musi być ciągiem znaków o długości od 1 do 128 znaków. Krótsze uid zapewniają lepszą wydajność.
|
|
claims (opcjonalnie) |
Opcjonalne oświadczenia niestandardowe, które mają być uwzględnione w zmiennych auth / request.auth reguł zabezpieczeń
|
|
Oto kilka przykładowych implementacji tworzenia tokenów niestandardowych w różnych językach, których nie obsługuje pakiet Firebase Admin SDK. W tych przykładach tokeny są podpisywane lokalnie za pomocą klucza prywatnego konta usługi, więc obowiązują wskazówki dotyczące bezpieczeństwa plików kluczy konta usługi. Aby uniknąć zarządzania kluczem prywatnym, możesz poprosić Google o zdalne podpisanie ładunku tokena przez wywołanie metody signJwt interfejsu Service Account Credentials API. Wywołujący musi mieć rolę Twórca tokenów konta usługi na koncie usługi.
PHP
Korzystanie z php-jwt:
// Requires: composer require firebase/php-jwt
use Firebase\JWT\JWT;
// Placeholder values. In production, load your service account's email
// address and private key from a secure secret store. Never hard-code
// private keys in source code.
$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
Korzystanie z ruby-jwt:
require "jwt"
# Placeholder values. In production, load your service account's email
# address and private key from a secure secret store. Never hard-code
# private keys in source code.
$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 użyć go do uwierzytelniania w Firebase. Aby dowiedzieć się, jak to zrobić, zapoznaj się z przykładowym kodem powyżej.
Rozwiązywanie problemów
W tej sekcji opisujemy niektóre typowe problemy, które deweloperzy mogą napotkać podczas tworzenia tokenów niestandardowych, oraz sposoby ich rozwiązywania.
Interfejs IAM API nie jest włączony
Jeśli podasz 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 oznacza, że interfejs IAM API nie jest obecnie włączony w projekcie w Firebase. Otwórz link w komunikacie o błędzie w przeglądarce i kliknij przycisk „Włącz interfejs API”, aby włączyć go w 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 pojawić się 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}.
Aby rozwiązać ten problem, przypisz rolę uprawnień Twórca tokenów konta usługi do odpowiedniego konta usługi. Używane domyślne konto usługi zależy od środowiska i wersji Cloud Functions:
- Cloud Functions (1 generacji): używa domyślnego konta usługi App Engine.
- Cloud Functions (2 generacji): używa domyślnego konta usługi Compute Engine.
W konsoli Google Cloud otwórz Uprawnienia i administracja.
Kliknij ikonę edycji odpowiadającą kontu usługi, które chcesz zaktualizować.
Kliknij Dodaj kolejną rolę.
Wpisz
"Service Account Token Creator"w filtrze wyszukiwania i wybierz tę usługę z wyników.Kliknij Zapisz, aby potwierdzić przyznanie roli.
Więcej informacji o tym procesie znajdziesz w dokumentacji uprawnień. Możesz też dowiedzieć 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, 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 korzystasz z pakietu SDK do automatycznego wykrywania identyfikatora konta usługi, upewnij się, że kod jest wdrożony w zarządzanym środowisku Google z serwerem metadanych. W przeciwnym razie pamiętaj, aby podczas inicjowania pakietu SDK podać identyfikator konta usługi lub w środowiskach innych niż Google zainicjować pakiet SDK za pomocą pliku klucza konta usługi.