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 na urządzenie 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, a jeśli są one prawidłowe, zwraca niestandardowy token JWT. Niestandardowy token JWT zwrócony przez serwer może być następnie używany przez urządzenie klienta do uwierzytelniania w Firebase (iOS+, Android, internet). 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 w obiekcie request.auth
w Cloud Storage Security Rules.
Możesz utworzyć niestandardowy token za pomocą pakietu Firebase Admin SDK lub użyć biblioteki JWT innej firmy, jeśli 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żyty do podpisywania należy do konta usługi Google. Istnieje kilka sposobów określania konta usługi Google, które powinno być używane przez pakiet Firebase Admin SDK do podpisywania niestandardowych tokenów:
- Używanie 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 był dostępny dla podmiotów zewnętrznych.
- Umożliwianie pakietowi Admin SDK wykrywania konta usługi – ta metoda może być używana 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 – w środowisku zarządzanym przez Google ta metoda podpisuje tokeny za pomocą klucza określonego konta usługi. Korzysta ona jednak z usługi sieciowej, więc 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 dotyczące kont 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 konfiguracji pakietu Admin SDK.
Ta metoda inicjowania jest odpowiednia w przypadku wielu wdrożeń pakietu Admin SDK. Umożliwia też pakietowi Admin SDK tworzenie i podpisywanie lokalnie tokenów niestandardowych 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 nie ujawnić go osobom nieupoważnionym. W szczególności nie dodawaj plików JSON konta usługi do publicznej kontroli wersji.
Umożliwianie pakietowi Admin SDK wykrywania konta usługi
Jeśli Twój kod jest wdrażany w środowisku zarządzanym przez Google, pakiet Admin SDK może automatycznie wykrywać sposób podpisywania niestandardowych tokenów:
Jeśli Twój kod jest wdrażany w App Engineśrodowisku standardowym dla języków 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 tożsamości aplikacji podpisuje dane za pomocą konta usługi udostępnionego aplikacji przez Google App Engine.
Jeśli Twój 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ą IAM do zdalnego podpisywania tokenów.
Aby korzystać z tych metod podpisywania, zainicjuj pakiet SDK za pomocą domyślnych danych logowania aplikacji Google i nie podawaj ciągu znaków 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 Twój kod po raz pierwszy tworzy niestandardowy token. Wynik jest buforowany i ponownie wykorzystywany w przypadku kolejnych operacji podpisywania tokenów. Automatycznie wykryty identyfikator konta usługi jest zwykle jednym 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 można było utworzyć token niestandardowy. Może być konieczne przyznanie domyślnym kontom usługi niezbędnych uprawnień w sekcji IAM i administracja w konsoli Google Cloud. 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ś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 IAM mogą być prostsze i bezpieczniejsze, a w kodzie nie trzeba uwzględniać pliku JSON konta usługi.
Identyfikator konta usługi znajdziesz w Google Cloudkonsoli 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 Firebase i Google Cloud projektach;
Aby utworzyć niestandardowe tokeny przy użyciu 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 podpisać niestandardowe tokeny 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 iam.serviceAccounts.signBlob
uprawnienia.
Więcej informacji znajdziesz w sekcji rozwiązywania problemów poniżej.
Tworzenie niestandardowych tokenów za pomocą pakietu Firebase Admin SDK
Pakiet Firebase Admin SDK ma wbudowaną metodę tworzenia niestandardowych tokenów. Musisz podać co najmniej uid
, czyli dowolny ciąg znaków, który powinien jednoznacznie identyfikować uwierzytelnianego użytkownika lub urządzenie. 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 mają być uwzględnione w niestandardowym tokenie. Na przykład poniżej do niestandardowego tokena 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
Zarezerwowane nazwy tokenów niestandardowych
Logowanie za pomocą niestandardowych tokenów na klientach
Po utworzeniu niestandardowego tokena wyślij go do aplikacji klienckiej. Aplikacja kliencka uwierzytelnia się za pomocą niestandardowego tokena, wywołując funkcję 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 zaloguje się w aplikacji klienckiej za pomocą konta określonego przez parametr uid
zawarty w niestandardowym tokenie. Jeśli to konto nie istniało wcześniej, zostanie utworzony rekord tego użytkownika.
Podobnie jak w przypadku innych metod logowania (np. signInWithEmailAndPassword()
i signInWithCredential()
) obiekt auth
w Realtime Database Security Rules i obiekt request.auth
w Cloud Storage Security Rules będą wypełnione uid
użytkownika. W tym przypadku uid
będzie wartością określoną podczas generowania niestandardowego tokena.
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 niestandardowy token zawiera dodatkowe roszczenia, można się do nich odwoływać za pomocą 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 przechowywania
service firebase.storage {
match /b/<your-firebase-storage-bucket>/o {
match /premiumContent/{filename} {
allow read, write: if request.auth.token.premiumAccount == true;
}
}
}
Tworzenie niestandardowych tokenów za pomocą biblioteki JWT innej firmy
Jeśli Twój backend jest napisany w języku, dla którego nie ma oficjalnego pakietu Firebase Admin SDK, możesz ręcznie tworzyć niestandardowe tokeny. Najpierw znajdź bibliotekę JWT innej firmy w swoim języku. Następnie użyj tej biblioteki JWT, aby wygenerować token JWT zawierający te roszczenia:
Deklaracje tokenów niestandardowych | ||
---|---|---|
alg |
Algorytm | "RS256" |
iss |
Wystawca | Adres e-mail konta usługi w projekcie |
sub |
Temat | Adres e-mail konta usługi w projekcie |
aud |
Odbiorcy | "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit" |
iat |
Godzina wydania | Bieżący czas w sekundach od początku epoki systemu UNIX. |
exp |
Okres ważności |
Czas wygaśnięcia tokena podany w sekundach od początku epoki systemu UNIX. Może być maksymalnie o 3600 sekund późniejsza niż wartość iat .
Uwaga: to ustawienie kontroluje tylko czas wygaśnięcia samego tokena niestandardowego. Gdy jednak zalogujesz użytkownika za pomocą signInWithCustomToken() , pozostanie on zalogowany na urządzeniu, dopóki jego sesja nie straci ważności lub użytkownik 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 s zapewniają lepsze wyniki.
|
|
claims (opcjonalnie) |
Opcjonalne niestandardowe roszczenia, które mają być uwzględnione w zmiennych reguł zabezpieczeń auth /
request.auth
|
Oto przykłady implementacji tworzenia niestandardowych tokenów 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 niestandardowego tokena wyślij go do aplikacji klienta, aby użyć go do uwierzytelniania w Firebase. Sposób, w jaki to zrobić, znajdziesz w przykładowych fragmentach kodu powyżej.
Rozwiązywanie problemów
W tej sekcji opisujemy typowe problemy, które mogą napotkać deweloperzy podczas tworzenia niestandardowych tokenów, oraz sposoby ich rozwiązywania.
Interfejs IAM API nie jest włączony
Jeśli podajesz 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 Twoim projekcie 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}.
Najprostszym sposobem rozwiązania tego problemu jest przypisanie do danego konta usługi roli uprawnień „Twórca tokenów konta usługi”, zwykle {project-name}@appspot.gserviceaccount.com
:
- W konsoli Google Cloud otwórz stronę Administracja.
- Wybierz projekt i kliknij „Dalej”.
- Kliknij ikonę edycji odpowiadającą kontu usługi, które chcesz zaktualizować.
- Kliknij „Dodaj kolejną rolę”.
- Wpisz w filtrze wyszukiwania „Twórca tokenów konta usługi” i wybierz go z wyników.
- Aby potwierdzić przyznanie roli, kliknij „Zapisz”.
Więcej informacji o tym procesie znajdziesz w dokumentacji IAM. 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 wdrażany 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.