Создание пользовательских токенов

Firebase дает вам полный контроль над аутентификацией, позволяя вам аутентифицировать пользователей или устройства с помощью безопасных веб-токенов JSON (JWT). Вы создаете эти токены на своем сервере, передаете их обратно на клиентское устройство, а затем используете их для аутентификации с помощью signInWithCustomToken() .

Для этого необходимо создать конечную точку сервера, которая принимает учетные данные для входа, такие как имя пользователя и пароль, и, если учетные данные действительны, возвращает настраиваемый JWT. Пользовательский JWT, возвращенный с вашего сервера, может затем использоваться клиентским устройством для аутентификации в Firebase ( iOS+ , Android , веб ). После аутентификации этот идентификатор будет использоваться при доступе к другим службам Firebase, таким как база данных Firebase Realtime и облачное хранилище. Кроме того, содержимое JWT будет доступно в объекте auth в ваших правилах базы данных реального времени и в объекте request.auth в ваших правилах безопасности облачного хранилища .

Вы можете создать собственный токен с помощью Firebase Admin SDK или использовать стороннюю библиотеку JWT, если ваш сервер написан на языке, который Firebase изначально не поддерживает.

Прежде чем вы начнете

Пользовательские токены — это подписанные JWT, в которых закрытый ключ, используемый для подписи, принадлежит учетной записи службы Google. Существует несколько способов указать учетную запись службы Google, которую Firebase Admin SDK должен использовать для подписи пользовательских токенов:

  • Использование файла JSON учетной записи службы. Этот метод можно использовать в любой среде, но для этого необходимо упаковать файл JSON учетной записи службы вместе с кодом. Особое внимание следует уделить тому, чтобы файл JSON служебной учетной записи не был доступен внешним сторонам.
  • Разрешение Admin SDK обнаруживать учетную запись службы. Этот метод можно использовать в средах, управляемых Google, таких как Google Cloud Functions и App Engine. Возможно, вам придется настроить некоторые дополнительные разрешения через Google Cloud Console.
  • Использование идентификатора учетной записи службы . При использовании в среде, управляемой Google, этот метод будет подписывать токены с использованием ключа указанной учетной записи службы. Однако он использует удаленную веб-службу, и вам может потребоваться настроить дополнительные разрешения для этой учетной записи службы через Google Cloud Console.

Использование 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, присутствующую в этой среде, для подписи пользовательских токенов. Служба идентификации приложения подписывает данные, используя учетную запись службы, предоставленную для вашего приложения Google App Engine.

  • Если ваш код развернут в какой-либо другой управляемой среде (например, Google Cloud Functions, Google Compute Engine), Firebase Admin SDK может автоматически обнаруживать строку идентификатора сервисного аккаунта на локальном сервере метаданных . Обнаруженный идентификатор учетной записи службы затем используется вместе со службой IAM для удаленной подписи маркеров.

Чтобы использовать эти методы подписи, инициализируйте SDK с учетными данными приложения Google по умолчанию и не указывайте строку идентификатора учетной записи службы:

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:

Как и в случае с явно указанными идентификаторами учетных записей служб, автоматически обнаруженные идентификаторы учетных записей служб должны иметь разрешение iam.serviceAccounts.signBlob , чтобы создание пользовательского токена работало. Возможно, вам придется использовать раздел IAM и администратора в Google Cloud Console, чтобы предоставить учетным записям службы по умолчанию необходимые разрешения. Подробнее см. в разделе «Устранение неполадок» ниже.

Использование идентификатора учетной записи службы

Чтобы поддерживать согласованность между различными частями вашего приложения, вы можете указать идентификатор служебного аккаунта, ключи которого будут использоваться для подписи токенов при работе в среде, управляемой Google. Это может сделать политики IAM более простыми и безопасными, а также избежать включения JSON-файла служебной учетной записи в ваш код.

Идентификатор учетной записи службы можно найти в Google Cloud Console или в поле 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+

Цель-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.FirebaseUser newUser = task.Result;
  Debug.LogFormat("User signed in successfully: {0} ({1})",
      newUser.DisplayName, newUser.UserId);
});

С++

firebase::Future<firebase::auth::User*> result =
    auth->SignInWithCustomToken(custom_token);

Интернет

firebase.auth().signInWithCustomToken(token)
  .then((userCredential) => {
    // Signed in
    var user = userCredential.user;
    // ...
  })
  .catch((error) => {
    var errorCode = error.code;
    var errorMessage = error.message;
    // ...
  });

Если аутентификация прошла успешно, ваш пользователь теперь войдет в ваше клиентское приложение с учетной записью, указанной uid пользователя, включенным в настраиваемый токен. Если эта учетная запись ранее не существовала, будет создана запись для этого пользователя.

Как и в случае с другими методами входа (например, signInWithEmailAndPassword() и signInWithCredential() ), объект auth в ваших правилах базы данных реального времени и объект request.auth в ваших правилах безопасности облачного хранилища будут заполнены 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) или request.auth.token (облачное хранилище) в ваших правилах:

Правила базы данных

{
  "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 до 36 символов.
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. См. примеры кода выше, чтобы узнать, как это сделать.

Исправление проблем

В этом разделе описаны некоторые распространенные проблемы, с которыми разработчики могут столкнуться при создании пользовательских токенов, и способы их решения.

IAM API не включен

Если вы указываете идентификатор учетной записи службы для подписи токенов, вы можете получить сообщение об ошибке, подобное следующему:

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 :

  1. Откройте страницу IAM и администратора в Google Cloud Console.
  2. Выберите свой проект и нажмите «Продолжить».
  3. Щелкните значок редактирования, соответствующий учетной записи службы, которую вы хотите обновить.
  4. Нажмите «Добавить еще одну роль».
  5. Введите «Создание токена сервисной учетной записи» в фильтр поиска и выберите его из результатов.
  6. Нажмите «Сохранить», чтобы подтвердить предоставление роли.

Дополнительные сведения об этом процессе см. в документации 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.