創建自定義令牌

Firebase 允許您使用安全的 JSON 網絡令牌 (JWT) 對用戶或設備進行身份驗證,從而讓您完全控制身份驗證。您生成您的服務器上的這些令牌,將它們傳遞回客戶端設備,然後用它們通過認證signInWithCustomToken()方法。

為此,您必須創建一個接受登錄憑據(例如用戶名和密碼)的服務器端點,並且如果憑據有效,則返回自定義 JWT。從服務器返回的自定義JWT然後可以通過客戶端設備可以使用火力地堡(驗證的iOS安卓網絡)。通過身份驗證後,該身份將在訪問其他 Firebase 服務(例如 Firebase 實時數據庫和雲存儲)時使用。此外,智威湯遜的內容將在提供auth在你的對象實時數據庫規則request.auth在你的對象雲存儲安全規則

您可以使用 Firebase Admin SDK 創建自定義令牌,或者如果您的服務器是用 Firebase 本身不支持的語言編寫的,則您可以使用第三方 JWT 庫。

在你開始之前

自定義令牌是簽名的 JWT,其中用於簽名的私鑰屬於 Google 服務帳戶。有多種方法可以指定 Firebase Admin SDK 用於簽署自定義令牌的 Google 服務帳戶:

  • 使用服務帳戶JSON文件-這種方法可以在任何環境中使用,但需要你與你的代碼一起打包服務帳戶JSON文件。必須特別注意確保服務帳戶 JSON 文件不會暴露給外部各方。
  • 讓管理員SDK發現服務帳戶-此方法可在由谷歌託管如谷歌的雲功能和App Engine的環境中使用。您可能需要通過 Google Cloud Console 配置一些額外的權限。
  • 使用服務帳戶ID -在谷歌的管理環境中使用這種方法將簽署使用指定的服務帳戶的主要標記。但是,它使用遠程 Web 服務,您可能必須通過 Google Cloud Console 為該服務帳戶配置其他權限。

使用服務帳號 JSON 文件

服務帳戶 JSON 文件包含與服務帳戶對應的所有信息(包括 RSA 私鑰)。它們可以從 Firebase 控制台下載。按照管理SDK設置說明,了解如何與服務帳戶JSON文件初始化管理SDK的詳細信息。

這種初始化方法適用於廣泛的 Admin SDK 部署。此外,它還使 Admin SDK 能夠在本地創建和簽署自定義令牌,而無需進行任何遠程 API 調用。這種方法的主要缺點是它需要您將服務帳戶 JSON 文件與您的代碼一起打包。另請注意,服務帳戶 JSON 文件中的私鑰是敏感信息,必須特別注意保密。具體來說,不要將服務帳戶 JSON 文件添加到公共版本控制中。

讓 Admin SDK 發現服務帳號

如果您的代碼部署在由 Google 管理的環境中,則 Admin SDK 可以嘗試自動發現對自定義令牌進行簽名的方法:

  • 如果您的代碼部署在為Java,Python或轉至App Engine的標準環境,管理SDK可以使用該應用程序標識服務存在於環境中簽署的自定義標記。 App Identity 服務使用 Google App Engine 為您的應用程序配置的服務帳戶對數據進行簽名。

  • 如果您的代碼部署在一些其他的託管環境(如谷歌的雲功能,谷歌計算引擎),在火力地堡管理SDK可自動發現從本地服務帳戶ID字符串元數據服務器。然後將發現的服務帳戶 ID 與 IAM 服務結合使用以遠程簽署令牌。

要使用這些簽名方法,請使用 Google 應用程序默認憑據初始化 SDK,並且不要指定服務帳戶 ID 字符串:

節點.js

admin.initializeApp();

爪哇

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 必鬚髮現服務帳號 ID 字符串,它會在您的代碼首次創建自定義令牌時這樣做。結果被緩存並重用於後續的令牌簽名操作。自動發現的服務帳號 ID 通常是 GCP 提供的默認服務帳號之一:

就像有明確指定的服務帳戶ID,自動discoverd服務帳戶ID必須在iam.serviceAccounts.signBlob自定義令牌創建工作許可。您可能需要使用IAM和管理的谷歌雲端控制台的部分授予默認服務帳戶所需的權限。有關更多詳細信息,請參閱下面的故障排除部分。

使用服務帳號 ID

為了保持應用程序各個部分之間的一致性,您可以指定一個服務帳號 ID,在 Google 管理的環境中運行時,該 ID 的密鑰將用於對令牌進行簽名。這可以使 IAM 策略更簡單、更安全,並避免在您的代碼中包含服務帳號 JSON 文件。

服務帳戶ID可以在發現谷歌雲端控制台,或在client_email下載的服務帳戶JSON文件的領域。服務帳戶ID是具有以下格式的電子郵件地址: <client-id>@<project-id>.iam.gserviceaccount.com 。它們唯一標識 Firebase 和 Google Cloud 項目中的服務帳號。

要使用單獨的服務帳戶 ID 創建自定義令牌,請按如下所示初始化 SDK:

節點.js

admin.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);

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",
});

服務帳戶 ID 不是敏感信息,因此它們的暴露無關緊要。但是,要使用指定的服務帳號簽署自定義令牌,Firebase Admin SDK 必須調用遠程服務。此外,還必須確保服務帳戶管理SDK使用撥打這通電話-通常{project-name}@appspot.gserviceaccount.com -有iam.serviceAccounts.signBlob權限。有關更多詳細信息,請參閱下面的故障排除部分。

使用 Firebase Admin SDK 創建自定義令牌

Firebase Admin SDK 具有用於創建自定義令牌的內置方法。至少,你需要提供一個uid ,它可以是任何字符串,但要識別您的身份進行身份驗證的用戶或設備。這些令牌在一小時後過期。

節點.js

const uid = 'some-uid';

admin
  .auth()
  .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

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在你的安全規則的對象:

節點.js

const userId = 'some-uid';
const additionalClaims = {
  premiumAccount: true,
};

admin
  .auth()
  .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

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

目標-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);
});

C++

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 (火力地堡實時數據庫)或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

如果您指定用於簽名令牌的服務帳戶 ID,您可能會收到類似於以下內容的錯誤:

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.

在火力地堡管理員SDK使用IAM API來標誌標記。此錯誤表示您的 Firebase 項目當前未啟用 IAM API。在 Web 瀏覽器中打開錯誤消息中的鏈接,然後單擊“啟用 API”按鈕為您的項目啟用它。

服務帳戶沒有所需的權限

如果服務帳戶管理員火力地堡的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和管理在谷歌雲端控制台頁面。
  2. 選擇您的項目,然後單擊“繼續”。
  3. 單擊與您要更新的服務帳戶對應的編輯圖標。
  4. 單擊“添加另一個角色”。
  5. 在搜索過濾器中輸入“Service Account Token Creator”,然後從結果中選擇它。
  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 自動發現服務帳號 ID,請確保代碼部署在具有元數據服務器的託管 Google 環境中。否則,請務必在 SDK 初始化時指定服務帳號 JSON 文件或服務帳號 ID。