Firebase daje Ci 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 osiągnąć, musisz utworzyć punkt końcowy serwera, który akceptuje dane logowania, takie jak nazwa użytkownika i hasło, a jeśli dane logowania są 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 oraz w obiekcie
request.auth w
Cloud Storage Security Rules.
Możesz utworzyć token niestandardowy 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ż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:
- Używanie pliku JSON konta usługi – ta metoda może być używana w dowolnym środowisku, ale wymaga spakowania pliku JSON konta usługi razem z kodem. Należy zachować szczególną ostrożność, aby plik JSON konta usługi nie był widoczny dla osób trzecich.
- Umożliwienie pakietowi Admin SDK wykrycia konta usługi – ta metoda może być używana w środowiskach zarządzanych przez Google, takich jak 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 za pomocą klucza określonego konta usługi. Używa ona jednak zdalnej usługi internetowej i może być konieczne skonfigurowanie dodatkowych uprawnień dla tego konta usługi w Google Cloud konsoli.
Używanie pliku JSON konta usługi
Pliki JSON konta usługi zawierają wszystkie informacje dotyczące kont usługi (w tym klucz prywatny RSA). Można je pobrać z Firebase konsoli. Więcej informacji o tym, jak zainicjować pakiet Admin SDK za pomocą pliku JSON konta usługi, znajdziesz w instrukcjach konfiguracji pakietu Admin SDK.
Ta metoda inicjowania jest odpowiednia dla wielu 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 razem z kodem. Pamiętaj też, że klucz prywatny w pliku JSON konta usługi to poufna informacja, dlatego należy zachować szczególną ostrożność, aby go nie ujawnić. W szczególności nie dodawaj plików JSON konta usługi do publicznej kontroli wersji.
Umożliwienie pakietowi Admin SDK wykrycia konta usługi
Jeśli kod jest wdrożony w środowisku zarządzanym przez Google, pakiet Admin SDK może automatycznie wykryć sposób podpisywania tokenów niestandardowych:
Jeśli 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 App Identity dostępnej w tym środowisku do podpisywania tokenów niestandardowych. Usługa App Identity podpisuje dane za pomocą konta usługi udostępnionego aplikacji przez Google App Engine.
Jeśli kod jest wdrożony 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 na lokalnym serwerze 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 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. 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 użycie sekcji
IAM i administracja
w konsoli Google Cloud, aby przyznać domyślnym kontom usługi
niezbędne uprawnienia. Więcej informacji znajdziesz w sekcji Rozwiązywanie 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. Może to uprościć i zwiększyć bezpieczeństwo zasad IAM oraz uniknąć konieczności dołączania pliku JSON konta usługi do kodu.
Identyfikator konta usługi można znaleźć w
Google Cloud konsoli,
lub w polu client_email pobranego pliku JSON konta usługi.
Identyfikatory kont usługi to adresy e-mail w następującym formacie:
<client-id>@<project-id>.iam.gserviceaccount.com. Unikalnie identyfikują one
konta usługi w projektach Firebase i Google Cloud projects.
Aby tworzyć tokeny niestandardowe za pomocą osobnego identyfikatora konta usługi, zainicjuj pakiet SDK w sposób opisany 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, dlatego ich ujawnienie nie ma znaczenia. Aby jednak podpisać tokeny niestandardowe za pomocą określonego konta usługi, pakiet Firebase Admin SDK musi wywołać usługę zdalną.
Ponadto musisz się upewnić, ż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 Rozwiązywanie 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ć 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
Opcjonalnie możesz też określić dodatkowe deklaracje, 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 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);
});
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ą 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 metodę 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 na konto określone przez uid zawarty w tokenie niestandardowym. Jeśli to konto wcześniej nie istniało, zostanie utworzony rekord tego użytkownika.
Podobnie jak w przypadku innych metod logowania (takich jak
signInWithEmailAndPassword() i signInWithCredential()), obiekt auth w Twoich Realtime Database Security Rules i
obiekt request.auth w Twoich
Cloud Storage Security Rules zostaną
wypełnione wartością uid użytkownika. W tym przypadku uid będzie tym, który został określony podczas generowania tokena niestandardowego.
Reguły bazy danych
{
"rules": {
"adminContent": {
".read": "auth.uid === 'some-uid'"
}
}
}
Reguły pamięci masowej
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 deklaracje, 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 pamięci masowej
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 backend jest napisany w języku, dla którego nie ma oficjalnego pakietu Firebase Admin SDK, 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 wygenerować token JWT, który zawiera te deklaracje:
| Deklaracje tokena niestandardowego | ||
|---|---|---|
alg |
Algorytm | "RS256" |
iss |
Wystawca | Adres e-mail konta usługi projektu |
sub |
Temat | Adres e-mail konta usługi projektu |
aud |
Odbiorcy | "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit" |
iat |
Godzina wystawienia | Aktualny czas w sekundach od początku epoki uniksowej |
exp |
Data ważności |
Czas w sekundach od początku epoki uniksowej, w którym token wygasa. Może to być
maksymalnie 3600 sekund później niż iat.
Uwaga: ta wartość określa tylko czas wygaśnięcia tokena niestandardowego samego tokena. Gdy jednak zalogujesz użytkownika za pomocą metody signInWithCustomToken(), pozostanie on zalogowany na
urządzeniu, dopóki jego sesja nie zostanie unieważniona 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 zapewniają lepszą
wydajność.
|
|
claims (opcjonalne) |
Opcjonalne deklaracje niestandardowe, które mają być uwzględnione w zmiennych w regułach bezpieczeństwa auth /
request.auth
|
|
Oto kilka przykładowych implementacji tworzenia tokenów niestandardowych w różnych językach, których pakiet Firebase Admin SDK nie obsługuje:
PHP
Używanie 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
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 tokena niestandardowego wyślij go do aplikacji klienckiej, aby użyć go do uwierzytelniania w Firebase. Informacje o tym, jak to zrobić, znajdziesz w przykładowych kodach powyżej.
Rozwiązywanie problemów
W tej sekcji opisujemy niektóre typowe problemy, które mogą wystąpić podczas tworzenia tokenów niestandardowych, oraz sposoby ich rozwiązywania.
Nie włączono interfejsu IAM API
Jeśli określisz identyfikator konta usługi do podpisywania tokenów, może się pojawić 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 w Firebase. Otwórz link w komunikacie o błędzie w przeglądarce i kliknij przycisk „Włącz interfejs API”, aby go włączyć 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 się pojawić 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, przyznaj odpowiedniemu kontu usługi rolę Twórca tokenów konta usługi IAM. 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 IAM 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ę rolę z wyników.Aby potwierdzić przyznanie roli, kliknij Zapisz.
Więcej informacji o tym procesie znajdziesz w dokumentacji IAM lub dowiesz 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, 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 tym, że pakiet SDK automatycznie wykryje identyfikator konta usługi, upewnij się, że kod jest wdrożony w środowisku zarządzanym przez Google z serwerem metadanych. W przeciwnym razie podczas inicjowania pakietu SDK określ plik JSON konta usługi lub identyfikator konta usługi.