Firebase предоставляет полный контроль над аутентификацией, позволяя аутентифицировать пользователей или устройства с помощью защищенных JSON Web Tokens (JWT). Вы генерируете эти токены на своем сервере, передаете их обратно на клиентское устройство, а затем используете их для аутентификации через метод signInWithCustomToken() .
Для этого необходимо создать серверную конечную точку, которая принимает учетные данные для входа — например, имя пользователя и пароль — и, если учетные данные действительны, возвращает пользовательский JWT. Возвращаемый сервером пользовательский JWT затем может использоваться клиентским устройством для аутентификации в Firebase ( iOS+ , Android , web ). После аутентификации эта идентификация будет использоваться при доступе к другим сервисам Firebase, таким как Firebase Realtime Database и Cloud Storage . Кроме того, содержимое JWT будет доступно в объекте auth в Realtime Database Security Rules и в объекте request.auth в Cloud Storage Security Rules .
Вы можете создать собственный токен с помощью Firebase Admin SDK или использовать стороннюю библиотеку JWT, если ваш сервер написан на языке, который Firebase не поддерживает изначально.
Прежде чем начать
Пользовательские токены — это подписанные JWT-токены, закрытый ключ которых принадлежит сервисной учетной записи Google. Существует несколько способов указать сервисную учетную запись Google, которую должен использовать Firebase Admin SDK для подписи пользовательских токенов:
- Использование JSON-файла учетной записи службы — этот метод можно использовать в любой среде, но он требует включения JSON-файла учетной записи службы в ваш код. Особое внимание следует уделить тому, чтобы JSON-файл учетной записи службы не был доступен внешним сторонам.
- Разрешение SDK администратора обнаруживать учетную запись службы — этот метод можно использовать в средах, управляемых Google, таких как Google Cloud Functions и App Engine . Возможно, потребуется настроить дополнительные разрешения через консоль Google Cloud .
- Использование идентификатора сервисной учетной записи — При использовании в среде, управляемой Google, этот метод будет подписывать токены с помощью ключа указанной сервисной учетной записи. Однако он использует удаленную веб-службу, и вам может потребоваться настроить дополнительные разрешения для этой сервисной учетной записи через консоль Google Cloud .
Использование JSON-файла учетной записи службы
JSON-файлы учетных записей служб содержат всю информацию, относящуюся к учетным записям служб (включая закрытый ключ RSA). Их можно загрузить из консоли Firebase . Для получения дополнительной информации о том, как инициализировать Admin SDK с помощью JSON-файла учетной записи службы, следуйте инструкциям по настройке Admin SDK.
Этот метод инициализации подходит для широкого спектра развертываний Admin SDK. Кроме того, он позволяет Admin SDK создавать и подписывать пользовательские токены локально, без выполнения каких-либо удаленных вызовов API. Главный недостаток этого подхода заключается в том, что он требует упаковки JSON-файла учетной записи службы вместе с вашим кодом. Также следует отметить, что закрытый ключ в JSON-файле учетной записи службы является конфиденциальной информацией, и необходимо проявлять особую осторожность для сохранения его секретности. В частности, следует воздерживаться от добавления JSON-файлов учетных записей служб в общедоступные системы контроля версий.
Предоставление Admin SDK возможности обнаружить учетную запись службы
Если ваш код развернут в среде, управляемой Google, Admin SDK может попытаться автоматически найти способ подписать пользовательские токены:
Если ваш код развернут в стандартной среде App Engine для Java, Python или Go, Admin SDK может использовать службу App Identity, присутствующую в этой среде, для подписи пользовательских токенов. Служба App Identity подписывает данные, используя учетную запись службы, предоставленную для вашего приложения Google App Engine.
Если ваш код развернут в другой управляемой среде (например, Google Cloud Functions, Google Compute Engine), Firebase Admin SDK может автоматически определить идентификатор учетной записи службы на локальном сервере метаданных . Затем этот идентификатор учетной записи службы используется совместно со службой IAM для удаленной подписи токенов.
Для использования этих методов подписи инициализируйте SDK с использованием учетных данных Google Application Default и не указывайте строковый идентификатор учетной записи службы:
Node.js
initializeApp();
Java
FirebaseApp.initializeApp();
Python
default_app = firebase_admin.initialize_app()
Идти
app, err := firebase.NewApp(context.Background(), nil)
if err != nil {
log.Fatalf("error initializing app: %v\n", err)
}
C#
FirebaseApp.Create();
Чтобы протестировать тот же код локально, загрузите JSON-файл учетной записи службы и установите переменную среды GOOGLE_APPLICATION_CREDENTIALS так, чтобы она указывала на этот файл.
Если Firebase Admin SDK необходимо определить строковый идентификатор учетной записи службы, это происходит при первом создании пользовательского токена вашим кодом. Результат кэшируется и повторно используется для последующих операций подписи токенов. Автоматически определяемый идентификатор учетной записи службы обычно является одной из учетных записей служб по умолчанию, предоставляемых Google Cloud :
- Учетная запись службы Compute Engine по умолчанию
- Учетная запись службы Cloud Functions по умолчанию
Как и в случае с явно указанными идентификаторами учетных записей служб, для создания пользовательского токена автоматически обнаруженные идентификаторы учетных записей служб должны иметь разрешение iam.serviceAccounts.signBlob . Возможно, вам потребуется использовать раздел IAM и администрирования в консоли Google Cloud , чтобы предоставить учетным записям служб по умолчанию необходимые разрешения. Дополнительные сведения см. в разделе устранения неполадок ниже.
Использование идентификатора служебной учетной записи
Для обеспечения согласованности между различными частями вашего приложения вы можете указать идентификатор учетной записи службы, ключи которой будут использоваться для подписи токенов при работе в среде, управляемой Google. Это может упростить и повысить безопасность политик IAM, а также избежать необходимости включения JSON-файла учетной записи службы в ваш код.
Идентификатор сервисной учетной записи можно найти в консоли Google Cloud или в поле client_email загруженного JSON-файла сервисной учетной записи. Идентификаторы сервисных учетных записей представляют собой адреса электронной почты в следующем формате: <client-id>@<project-id>.iam.gserviceaccount.com . Они однозначно идентифицируют сервисные учетные записи в проектах Firebase и Google Cloud .
Для создания пользовательских токенов с использованием отдельного идентификатора учетной записи службы инициализируйте SDK, как показано ниже:
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)
Идти
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",
});
Идентификаторы учетных записей служб не являются конфиденциальной информацией, поэтому их раскрытие не имеет значения. Однако для подписи пользовательских токенов с помощью указанной учетной записи службы Firebase Admin SDK должен вызвать удаленную службу. Более того, необходимо также убедиться, что учетная запись службы, используемая Admin SDK для этого вызова (обычно {project-name}@appspot.gserviceaccount.com ), имеет разрешение iam.serviceAccounts.signBlob . Дополнительные сведения см. в разделе устранения неполадок ниже.
Создавайте пользовательские токены с помощью Firebase Admin SDK.
В Firebase Admin SDK есть встроенный метод для создания пользовательских токенов. Как минимум, вам нужно указать uid , который может быть любой строкой, но должен однозначно идентифицировать пользователя или устройство, которое вы аутентифицируете. Срок действия этих токенов истекает через один час.
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)
Идти
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
Вы также можете дополнительно указать дополнительные утверждения, которые будут включены в пользовательский токен. Например, ниже в пользовательский токен добавлено поле premiumAccount , которое будет доступно в объектах auth / request.auth в ваших правилах безопасности:
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)
Идти
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
Зарезервированные имена пользовательских токенов
Вход в систему с использованием пользовательских токенов на клиентских компьютерах.
После создания пользовательского токена его следует отправить в клиентское приложение. Клиентское приложение выполняет аутентификацию с помощью пользовательского токена, вызывая метод signInWithCustomToken() :
iOS+
Objective-C
[[FIRAuth auth] signInWithCustomToken:customToken
completion:^(FIRAuthDataResult * _Nullable authResult,
NSError * _Nullable error) {
// ...
}];
Быстрый
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);
}
}
});
Единство
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;
// ...
});
Если аутентификация пройдет успешно, ваш пользователь войдет в ваше клиентское приложение с учетной записью, указанной в uid , содержащемся в пользовательском токене. Если эта учетная запись ранее не существовала, будет создана запись для этого пользователя.
Аналогично другим методам авторизации (таким как signInWithEmailAndPassword() и signInWithCredential() ), объект auth в Realtime Database Security Rules и объект request.auth в Cloud Storage Security Rules будут заполнены идентификатором пользователя uid ). В данном случае uid будет тем, который вы указали при генерации пользовательского токена.
Правила базы данных
{
"rules": {
"adminContent": {
".read": "auth.uid === 'some-uid'"
}
}
}
Правила хранения
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";
}
}
}
Если пользовательский токен содержит дополнительные утверждения, на них можно ссылаться из объекта auth.token ( Firebase Realtime Database ) или request.auth.token ( Cloud Storage ) в ваших правилах:
Правила базы данных
{
"rules": {
"premiumContent": {
".read": "auth.token.premiumAccount === true"
}
}
}
Правила хранения
service firebase.storage {
match /b/<your-firebase-storage-bucket>/o {
match /premiumContent/{filename} {
allow read, write: if request.auth.token.premiumAccount == true;
}
}
}
Создавайте пользовательские токены, используя стороннюю библиотеку JWT.
Если ваш бэкенд написан на языке, для которого нет официального SDK Firebase Admin, вы все равно можете создавать пользовательские токены вручную. Сначала найдите стороннюю библиотеку для создания JWT- токенов для вашего языка. Затем используйте эту библиотеку для создания JWT-токена, содержащего следующие утверждения:
| Пользовательские заявки на получение токенов | ||
|---|---|---|
alg | Алгоритм | "RS256" |
iss | Эмитент | Адрес электронной почты учетной записи службы вашего проекта |
sub | Предмет | Адрес электронной почты учетной записи службы вашего проекта |
aud | Аудитория | "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit" |
iat | Выпущено в момент | Текущее время в секундах с начала эпохи UNIX. |
exp | Срок действия | Время в секундах с начала эпохи UNIX, по истечении которого срок действия токена истекает. Максимальное время может быть на 3600 секунд позже , чем iat UNIX.Примечание: это управляет только временем истечения срока действия самого пользовательского токена . Но как только вы авторизуете пользователя с помощью signInWithCustomToken() , он останется авторизованным на устройстве до тех пор, пока его сессия не будет аннулирована или пользователь не выйдет из системы. |
uid | Уникальный идентификатор авторизованного пользователя должен представлять собой строку длиной от 1 до 128 символов включительно. Более короткие uid пользователей обеспечивают лучшую производительность. | |
claims (необязательно) | Дополнительные пользовательские утверждения для включения в переменные auth / request.auth правил безопасности. | |
Ниже приведены примеры реализации создания пользовательских токенов на различных языках, которые не поддерживаются Firebase Admin SDK:
PHP
Использование 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-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
После создания пользовательского токена отправьте его в клиентское приложение для аутентификации в Firebase. Примеры кода, демонстрирующие этот процесс, приведены выше.
Поиск неисправностей
В этом разделе описаны некоторые распространенные проблемы, с которыми разработчики могут столкнуться при создании пользовательских токенов, и способы их решения.
API IAM не включен.
Если вы указываете идентификатор учетной записи службы для подписи токенов, вы можете получить ошибку, подобную следующей:
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.
Firebase Admin SDK использует API IAM для подписи токенов. Эта ошибка указывает на то, что API IAM в данный момент не включен для вашего проекта Firebase. Откройте ссылку в сообщении об ошибке в веб-браузере и нажмите кнопку «Включить API», чтобы включить его для вашего проекта.
У учетной записи службы отсутствуют необходимые разрешения.
Если у учетной записи службы, от имени которой запущен Firebase Admin SDK, отсутствует разрешение iam.serviceAccounts.signBlob , вы можете получить сообщение об ошибке следующего вида:
Permission iam.serviceAccounts.signBlob is required to perform this operation
on service account projects/-/serviceAccounts/{your-service-account-id}.
Самый простой способ решить эту проблему — предоставить соответствующей учетной записи службы роль IAM "Создатель токена учетной записи службы", обычно это {project-name}@appspot.gserviceaccount.com :
- Откройте страницу IAM и администрирования в консоли Google Cloud .
- Выберите свой проект и нажмите «Продолжить».
- Щелкните значок редактирования, соответствующий учетной записи службы, которую вы хотите обновить.
- Нажмите кнопку "Добавить еще одну роль".
- Введите в поисковый фильтр «Создатель токенов учетных записей служб» и выберите его из результатов поиска.
- Нажмите «Сохранить», чтобы подтвердить предоставление роли.
Для получения более подробной информации об этом процессе обратитесь к документации IAM или узнайте, как обновлять роли с помощью инструментов командной строки gcloud.
Не удалось определить учетную запись службы.
Если вы получили сообщение об ошибке, подобное приведенному ниже, это означает, что Firebase Admin SDK не был должным образом инициализирован.
Failed to determine service account ID. Initialize the SDK with service account credentials or specify a service account ID with iam.serviceAccounts.signBlob permission.
Если вы полагаетесь на SDK для автоматического определения идентификатора учетной записи службы, убедитесь, что код развернут в управляемой среде Google с сервером метаданных. В противном случае обязательно укажите JSON-файл учетной записи службы или идентификатор учетной записи службы при инициализации SDK.