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ślania konta usługi Google, którego pakiet Firebase Admin SDK powinien używać do podpisywania tokenów niestandardowych:
- Użycie pliku JSON konta usługi – tej metody można używać w dowolnym środowisku, ale wymaga ona spakowania pliku JSON konta usługi wraz z kodem. Należy zachować szczególną ostrożność, aby plik JSON konta usługi nie został ujawniony osobom trzecim.
- 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żywanie identyfikatora konta usługi – gdy ta metoda jest używana w środowisku zarządzanym przez Google, tokeny są podpisywane 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 w konsoli Google Cloud.
Używanie pliku JSON konta usługi
Pliki JSON kont usługi zawierają wszystkie informacje odpowiadające kontom usługi (w tym klucz prywatny RSA). Można je pobrać z konsoli Firebase. Więcej informacji o inicjowaniu pakietu Admin SDK za pomocą pliku JSON konta usługi znajdziesz w instrukcjach konfigurowania pakietu Admin SDK.
Ta metoda inicjowania jest odpowiednia w przypadku wielu różnych wdrożeń pakietu Admin SDK. Umożliwia też pakietowi Admin SDK tworzenie i podpisywanie tokenów niestandardowych lokalnie, bez wykonywania zdalnych wywołań interfejsu API. Główną wadą tego podejścia jest to, że wymaga ono spakowania pliku JSON konta usługi wraz z kodem. Pamiętaj też, że klucz prywatny w pliku JSON konta usługi to informacje poufne, dlatego należy zachować szczególną ostrożność, aby zachować jego poufność. W szczególności unikaj dodawania plików JSON kont usługi do publicznej kontroli wersji.
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, pobierz plik JSON konta usługi i ustaw zmienną środowiskową GOOGLE_APPLICATION_CREDENTIALS tak, aby wskazywała ten plik.
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 wyraźnie określonych identyfikatorów kont usługi, automatycznie wykryte identyfikatory kont usługi muszą mieć uprawnienie iam.serviceAccounts.signBlob, aby można było utworzyć token niestandardowy. Aby przyznać domyślnym kontom usługi niezbędne uprawnienia, być może musisz użyć sekcji Uprawnienia i administrator 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 będą używane do podpisywania tokenów podczas działania w środowisku zarządzanym przez Google. Dzięki temu zasady uprawnień mogą być prostsze i bezpieczniejsze, a Ty nie musisz dołączać pliku JSON konta usługi do kodu.
Identyfikator konta usługi znajdziesz w konsoli 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. 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 ten sposób:
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 konto usługi, którego pakiet Admin SDK używa do wykonania tego wywołania (zwykle {project-name}@appspot.gserviceaccount.com), ma uprawnienie iam.serviceAccounts.signBlob.
Więcej informacji znajdziesz w sekcji dotyczącej rozwiązywania problemów poniżej.
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 za pomocą konta określonego 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ć z obiektu 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 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:
| Niestandardowe deklaracje tokenów | ||
|---|---|---|
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 on być maksymalnie o 3600 sekund późniejszy 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 (włącznie). Krótsze uid zapewniają lepszą wydajność.
|
|
claims (opcjonalnie) |
Opcjonalne niestandardowe oświadczenia, 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:
PHP
Korzystanie z 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
Korzystanie z 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 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 tego poniżej, 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 podczas inicjowania pakietu SDK podaj plik JSON konta usługi lub identyfikator konta usługi.