Google致力於提高黑人社區的種族平等。 怎麼看。
本頁面由 Cloud Translation API 翻譯而成。
Switch to English

創建自定義令牌

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

為此,您必須創建一個服務器端點,該端點接受登錄憑據(例如用戶名和密碼),並且如果憑據有效,則返回自定義JWT。然後,客戶端設備可以使用從服務器返回的自定義JWT對Firebase( iOSAndroidWeb )進行身份驗證。身份驗證後,將在訪問其他Firebase服務(例如Firebase實時數據庫和雲存儲)時使用此身份。此外,JWT的內容將在Firebase實時數據庫安全規則中的auth對象和Cloud Storage安全規則中request.auth像中提供。

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

在你開始之前

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

  • 使用服務帳戶JSON文件 -此方法可以在任何環境中使用,但是需要您將服務帳戶JSON文件與代碼打包在一起。必須格外小心,以確保服務帳戶JSON文件不會暴露給外部各方。
  • 讓Admin SDK發現服務帳戶 -此方法可以在Google管理的環境中使用,例如Google Cloud Functions和Google App Engine。您可能必須通過Google Cloud Platform控制台配置一些其他權限。
  • 使用服務帳戶ID-在Google託管的環境中使用時,此方法將使用指定的服務帳戶的密鑰對令牌進行簽名。但是,它使用遠程Web服務,您可能必須通過Google Cloud Platform控制台為此服務帳戶配置其他權限。

使用服務帳戶JSON文件

服務帳戶JSON文件包含與服務帳戶相對應的所有信息(包括RSA私鑰)。可以從Firebase控制台下載它們。請遵循Admin SDK設置說明 ,以獲取有關如何使用服務帳戶JSON文件初始化Admin SDK的更多信息。

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

讓Admin SDK發現服務帳戶

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

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

  • 如果您的代碼部署在其他託管環境(例如Google Cloud Functions,Google Compute Engine)中,則Firebase Admin SDK可以從本地元數據服務器自動發現服務帳戶ID字符串。然後,將發現的服務帳戶ID與IAM服務結合使用,以對令牌進行遠程簽名。

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

Node.js

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

C#

 FirebaseApp.Create();
 

要在本地測試相同的代碼,請下載服務帳戶JSON文件,然後將GOOGLE_APPLICATION_CREDENTIALS環境變量設置為指向它。

如果Firebase Admin SDK必鬚髮現服務帳戶ID字符串,則在您的代碼首次創建自定義令牌時會這樣做。結果被緩存並重新用於後續的令牌簽名操作。自動發現的服務帳戶ID通常是Google Cloud Platform提供的默認服務帳戶之一:

就像使用明確指定的服務帳戶ID一樣,自動發現的服務帳戶ID必須具有iam.serviceAccounts.signBlob權限,才能創建自定義令牌。您可能必須使用Google Cloud Platform Console的IAM和admin部分來授予默認服務帳戶必要的權限。有關更多詳細信息,請參見下面的故障排除部分。

使用服務帳號ID

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

服務帳戶ID可以在Google Cloud Platform控制台中找到,也可以在下載的服務帳戶JSON文件的client_email字段中找到。服務帳戶ID是具有以下格式的電子郵件地址: <client-id>@<project-id>.iam.gserviceaccount.com 。他們在Firebase和Google Cloud Platform項目中唯一標識服務帳戶。

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

Node.js

 admin.initializeApp({
  serviceAccountId: 'my-client-id@my-project-id.iam.gserviceaccount.com',
});
 

爪哇

 FirebaseOptions options = new 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)
} 

C#

 FirebaseApp.Create(new AppOptions()
{
    Credential = GoogleCredential.GetApplicationDefault(),
    ServiceAccountId = "my-client-id@my-project-id.iam.gserviceaccount.com",
});
 

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

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

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

Node.js

 let uid = 'some-uid';

admin.auth().createCustomToken(uid)
  .then(function(customToken) {
    // Send token back to client
  })
  .catch(function(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) 

C#

 var uid = "some-uid";

string customToken = await FirebaseAuth.DefaultInstance.CreateCustomTokenAsync(uid);
// Send token back to client
 

您還可以選擇指定要包含在自定義令牌中的其他聲明。例如,下面,在自定義令牌中添加了一個premiumAccount字段,該字段將在“安全規則”的auth / request.auth對像中提供:

Node.js

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

admin.auth().createCustomToken(userId, additionalClaims)
  .then(function(customToken) {
    // Send token back to client
  })
  .catch(function(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) 

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).catch(function(error) {
  // Handle Errors here.
  var errorCode = error.code;
  var errorMessage = error.message;
  // ...
});
 

如果身份驗證成功,您的用戶現在將使用自定義令牌中包含的uid指定的帳戶登錄到您的客戶端應用程序。如果該帳戶以前不存在,將為該用戶創建一條記錄。

與其他登錄方法(例如signInWithEmailAndPassword()signInWithCredential() )相同,您的Firebase實時數據庫安全規則中auth對象和Cloud Storage安全規則中request.auth對象將使用用戶的uid 。在這種情況下, 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時期以來的時間(以秒為單位),令牌終止的時間。 最多iat 晚3600秒
注意:這僅控制自定義令牌本身過期的時間。但是,一旦您使用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.

Firebase Admin SDK使用IAM API對令牌進行簽名。此錯誤表明您的Firebase項目當前未啟用IAM API。在Web瀏覽器中打開錯誤消息中的鏈接,然後單擊“啟用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. 在Google Cloud Platform控制台中打開IAM和管理頁面。
  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來自動發現服務帳戶ID,請確保將代碼部署在具有元數據服務器的託管Google環境中。否則,請確保在SDK初始化時指定服務帳戶JSON文件或服務帳戶ID。