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 учетной записи службы не был доступен внешним сторонам.
- Разрешение Admin SDK обнаружить учетную запись службы — этот метод можно использовать в средах, управляемых Google, таких как Google Cloud Functions и App Engine . Возможно, вам придется настроить некоторые дополнительные разрешения через консоль Google Cloud .
- Использование идентификатора учетной записи службы — при использовании в среде, управляемой Google, этот метод будет подписывать токены с использованием ключа указанной учетной записи службы. Однако он использует удаленную веб-службу, и вам может потребоваться настроить дополнительные разрешения для этой учетной записи службы через консоль Google Cloud .
Использование JSON-файла учетной записи службы
Файлы JSON учетных записей служб содержат всю информацию, соответствующую учетным записям служб (включая закрытый ключ RSA). Их можно загрузить из консоли Firebase . Следуйте инструкциям по настройке Admin SDK для получения дополнительной информации о том, как инициализировать Admin SDK с помощью файла JSON учетной записи службы.
Этот метод инициализации подходит для широкого спектра развертываний 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();
Ява
FirebaseApp.initializeApp();
Питон
default_app = firebase_admin.initialize_app()
Идти
app, err := firebase.NewApp(context.Background(), nil)
if err != nil {
log.Fatalf("error initializing app: %v\n", err)
}
С#
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',
});
Ява
FirebaseOptions options = FirebaseOptions.builder()
.setCredentials(GoogleCredentials.getApplicationDefault())
.setServiceAccountId("my-client-id@my-project-id.iam.gserviceaccount.com")
.build();
FirebaseApp.initializeApp(options);
Питон
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)
}
С#
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);
});
Ява
String uid = "some-uid";
String customToken = FirebaseAuth.getInstance().createCustomToken(uid);
// Send token back to client
Питон
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)
С#
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);
});
Ява
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
Питон
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)
С#
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
// ...
}
андроид
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);
});
С++
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
Если ваш бэкенд написан на языке, для которого нет официального Firebase Admin SDK, вы все равно можете вручную создавать пользовательские токены. Сначала найдите стороннюю библиотеку JWT для вашего языка. Затем используйте эту библиотеку JWT для создания JWT, который включает следующие утверждения:
Требования к пользовательским токенам | ||
---|---|---|
alg | Алгоритм | "RS256" |
iss | Эмитент | Адрес электронной почты учетной записи службы вашего проекта |
sub | Предмет | Адрес электронной почты учетной записи службы вашего проекта |
aud | Аудитория | "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit" |
iat | Выпущено в момент | Текущее время в секундах с начала эпохи UNIX |
exp | Срок годности | Время в секундах с эпохи UNIX, когда истекает срок действия токена. Может быть максимум на 3600 секунд позже, чем iat .Примечание: это контролирует только время истечения срока действия самого пользовательского токена . Но как только вы введете пользователя с помощью signInWithCustomToken() , он останется в системе устройства до тех пор, пока его сеанс не будет аннулирован или пользователь не выйдет из системы. |
uid | Уникальный идентификатор вошедшего в систему пользователя должен быть строкой длиной от 1 до 128 символов включительно. Более короткие uid обеспечивают лучшую производительность. | |
claims (необязательно) | Дополнительные пользовательские утверждения для включения в переменные Security Rules 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 использует IAM API для подписи токенов. Эта ошибка указывает на то, что IAM API в настоящее время не включен для вашего проекта 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.