ב-Firebase אתם יכולים לאמת משתמשים או מכשירים באמצעות אסימוני אינטרנט מאובטחים מסוג JSON (JWT), וכך אתם מקבלים שליטה מלאה על האימות. אתם יוצרים את האסימונים האלה בשרת, מעבירים אותם בחזרה למכשיר הלקוח ומשתמשים בהם כדי לאמת באמצעות השיטה signInWithCustomToken().
כדי לעשות את זה, צריך ליצור נקודת קצה בשרת שמקבלת פרטי כניסה, כמו שם משתמש וסיסמה, ואם פרטי הכניסה תקינים, מחזירה אסימון JWT בהתאמה אישית. לאחר מכן, מכשיר לקוח יכול להשתמש ב-JWT המותאם אישית שהוחזר מהשרת כדי לבצע אימות באמצעות Firebase (iOS+, Android, אינטרנט). אחרי האימות, הזהות הזו תשמש לגישה לשירותים אחרים של Firebase, כמו Firebase Realtime Database ו-Cloud Storage. בנוסף, התוכן של ה-JWT יהיה זמין באובייקט auth ב-Realtime Database Security Rules ובאובייקט request.auth ב-Cloud Storage Security Rules.
אתם יכולים ליצור אסימון בהתאמה אישית באמצעות ה-SDK של Firebase לאדמינים, או להשתמש בספריית JWT של צד שלישי אם השרת שלכם כתוב בשפה ש-Firebase לא תומכת בה באופן מקורי.
לפני שמתחילים
טוקנים בהתאמה אישית הם אסימוני JWT חתומים, שבהם המפתח הפרטי שמשמש לחתימה שייך לחשבון שירות של Google. יש כמה דרכים לציין את חשבון השירות של Google שבו ה-SDK של Firebase לאדמינים צריך להשתמש כדי לחתום על טוקנים בהתאמה אישית:
- זיהוי של חשבון שירות באמצעות SDK לאדמינים – אפשר להשתמש בשיטה הזו בסביבות שמנוהלות על ידי Google, כמו Cloud Functions ו-App Engine. יכול להיות שתצטרכו להגדיר הרשאות נוספות במסוף Google Cloud.
- שימוש במזהה של חשבון שירות – בשיטה הזו, המערכת חותמת על טוקנים באמצעות המפתחות של חשבון השירות שצוין, שמנוהלים על ידי Google. כך אתם לא צריכים לטפל במפתחות פרטיים בעצמכם. הוא משתמש בשירות אינטרנט מרוחק, ויכול להיות שתצטרכו להגדיר הרשאות נוספות לחשבון השירות הזה דרך מסוף Google Cloud.
- שימוש בקובץ מפתח של חשבון שירות – השתמשו בשיטה הזו רק אם הקוד שלכם פועל מחוץ לסביבה שמנוהלת על ידי Google, ואתם לא יכולים להשתמש באחת מהשיטות שלמעלה. כדי להשתמש בו, צריך ליצור, לאחסן ולהגן על מפתח פרטי לטווח ארוך.
אנחנו ממליצים על שתי השיטות הראשונות כי הן לא דורשות הורדה או ניהול של מפתחות לחשבונות שירות.
איך מאפשרים ל-SDK לאדמינים לגלות חשבון שירות
אם הקוד שלכם פרוס בסביבה שמנוהלת על ידי Google, ה-SDK לאדמינים יכול לנסות לגלות באופן אוטומטי אמצעי לחתימה על טוקנים בהתאמה אישית:
אם הקוד שלכם פרוס בסביבה הרגילה של App Engine ל-Java, ל-Python או ל-Go, ה-SDK לאדמינים יכול להשתמש בשירות App Identity שקיים בסביבה הזו כדי לחתום על טוקנים בהתאמה אישית. שירות App Identity חותם על נתונים באמצעות חשבון שירות שהוקצה לאפליקציה על ידי Google App Engine.
אם הקוד שלכם פרוס בסביבה מנוהלת אחרת (למשל, Google Cloud Functions או Google Compute Engine), ה-SDK של Firebase לאדמינים יכול לגלות באופן אוטומטי מחרוזת של מזהה חשבון שירות משרת המטא-נתונים המקומי. מזהה חשבון השירות שנמצא משמש בשילוב עם שירות IAM כדי לחתום על אסימונים מרחוק.
כדי להשתמש בשיטות החתימה האלה, צריך להפעיל את ה-SDK עם פרטי הכניסה שמוגדרים כברירת מחדל של האפליקציה ב-Google, ולא לציין מחרוזת של מזהה חשבון שירות:
Node.js
initializeApp();
Java
FirebaseApp.initializeApp();
Python
default_app = firebase_admin.initialize_app()
Go
app, err := firebase.NewApp(context.Background(), nil)
if err != nil {
log.Fatalf("error initializing app: %v\n", err)
}
C#
FirebaseApp.Create();
כדי לבדוק את אותו קוד באופן מקומי בלי להוריד מפתח של חשבון שירות, נכנסים עם פרטי הכניסה של המשתמש באמצעות Google Cloud CLI:
gcloud auth application-default login
במחשב המקומי אין שרת מטא-נתונים, ולכן ה-SDK לאדמינים לא יכול לגלות אוטומטית את מזהה חשבון השירות. במקום זאת, ציינו מזהה חשבון שירות כשאתם מאתחלים את ה-SDK, וודאו שלחשבון המשתמש שלכם יש את התפקיד יצירת אסימונים בחשבון שירות באותו חשבון שירות.
אם ה-SDK של Firebase לאדמינים צריך לגלות מחרוזת של מזהה חשבון שירות, הוא עושה את זה כשהקוד יוצר טוקן בהתאמה אישית בפעם הראשונה. התוצאה נשמרת במטמון ומשמשת לפעולות הבאות של חתימת טוקנים. מזהה חשבון השירות שמתגלה באופן אוטומטי הוא בדרך כלל אחד מחשבונות השירות שמוגדרים כברירת מחדל ומסופקים על ידי Google Cloud:
כמו במקרה של מזהי חשבונות שירות שצוינו במפורש, למזהי חשבונות שירות שמתגלים באופן אוטומטי צריכה להיות ההרשאה iam.serviceAccounts.signBlob כדי שיצירת הטוקן בהתאמה אישית תפעל. יכול להיות שתצטרכו להשתמש בקטע IAM and admin במסוף Google Cloud כדי לתת לחשבונות השירות שמוגדרים כברירת מחדל את ההרשאות הנדרשות. מידע נוסף זמין בסעיף הבא, שעוסק בפתרון בעיות.
שימוש במזהה חשבון שירות
כדי לשמור על עקביות בין החלקים השונים של האפליקציה, אתם יכולים לציין מזהה של חשבון שירות שהמפתחות שלו, שמנוהלים על ידי Google, ישמשו לחתימה על אסימונים. כך אפשר לפשט את מדיניות ה-IAM ולהפוך אותה למאובטחת יותר, וגם להימנע מהצורך להוריד, לאחסן או לפרוס מפתח של חשבון שירות.
מזהה חשבון השירות מופיע במסוף Google Cloud.
המזהים של חשבונות השירות הם כתובות אימייל בפורמט הבא: <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)
Go
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",
});
מזהי חשבונות שירות הם לא מידע רגיש, ולכן החשיפה שלהם לא משמעותית. עם זאת, כדי לחתום על טוקנים בהתאמה אישית באמצעות חשבון השירות שצוין, ה-SDK של Firebase לאדמינים צריך להפעיל שירות מרוחק.
בנוסף, צריך לוודא שלזהות שבה משתמש ה-SDK לאדמינים כדי לבצע את הקריאה הזו – בדרך כלל {project-name}@appspot.gserviceaccount.com בסביבת הייצור, או חשבון המשתמש שלכם במהלך הפיתוח המקומי – יש את ההרשאה iam.serviceAccounts.signBlob.
מידע נוסף זמין בסעיף הבא, שעוסק בפתרון בעיות.
שימוש בקובץ מפתח של חשבון שירות
אם הקוד פועל בסביבה מחוץ ל-Google Cloud שבה אף אחת מהשיטות שלמעלה לא זמינה, אתם יכולים להפעיל את SDK לאדמינים באמצעות קובץ מפתח של חשבון שירות. פרטים נוספים זמינים בהוראות להגדרת SDK לאדמינים. בשיטה הזו, ה-SDK לאדמינים חותם על טוקנים בהתאמה אישית באופן מקומי באמצעות המפתח הפרטי בקובץ, בלי לבצע קריאות ל-API מרחוק.
מפתחות של חשבונות שירות הם פרטי כניסה לטווח ארוך. אם מפתח דולף, כל מי שיש לו את המפתח יכול לפעול כחשבון השירות עד שהמפתח נמחק. אף פעם אל תשלחו קובצי מפתחות לניהול גרסאות, ואם הסביבה שלכם תומכת באיחוד שירותי אימות הזהות של עומסי עבודה, השתמשו בו במקום במפתחות. חשוב גם לדעת שבחלק מהארגונים משתמשים באילוצים של מדיניות הארגון כדי למנוע יצירה של מפתחות לחשבונות שירות. במקרה כזה, צריך להשתמש באחת מהשיטות האחרות.
יצירת טוקנים בהתאמה אישית באמצעות SDK של Firebase לאדמינים
ב-SDK של Firebase לאדמינים יש שיטה מובנית ליצירת טוקנים בהתאמה אישית. לכל הפחות, צריך לספק 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)
Go
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)
Go
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) {
// ...
}];
Swift
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);
}
}
});
Unity
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 לאדמינים, אתם עדיין יכולים ליצור אסימונים בהתאמה אישית באופן ידני. קודם כל, מוצאים ספריית JWT של צד שלישי בשפה שלכם. לאחר מכן, משתמשים בספריית ה-JWT הזו כדי ליצור JWT שכולל את ההצהרות הבאות:
| הצהרות טוקן בהתאמה אישית | ||
|---|---|---|
alg |
אלגוריתם | "RS256" |
iss |
המנפיק | כתובת האימייל של חשבון השירות של הפרויקט |
sub |
נושא | כתובת האימייל של חשבון השירות של הפרויקט |
aud |
קהל | "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit" |
iat |
שעת ההנפקה | השעה הנוכחית, בשניות מאז ראשית זמן יוניקס (Unix epoch) |
exp |
מועד תפוגה |
הזמן, בשניות מאז ראשית זמן יוניקס (Unix epoch), שבו פג תוקף האסימון. הוא יכול להיות עד 3,600 שניות אחרי iat.
הערה: ההגדרה הזו קובעת רק את הזמן שבו יפוג התוקף של האסימון בהתאמה אישית עצמו. אבל אחרי שמשתמש נכנס באמצעות signInWithCustomToken(), הוא נשאר מחובר למכשיר עד שהסשן שלו מבוטל או עד שהוא יוצא מהחשבון.
|
uid |
המזהה הייחודי של המשתמש המחובר חייב להיות מחרוזת באורך של 1 עד 128 תווים, כולל. ככל ש-uid קצר יותר,
הביצועים טובים יותר.
|
|
ֶclaims (אופציונלי) |
הצהרות אופציונליות בהתאמה אישית שייכללו במשתנים auth / request.auth של כללי האבטחה
|
|
בהמשך מופיעות כמה דוגמאות להטמעות של יצירת אסימונים בהתאמה אישית במגוון שפות שלא נתמכות ב-SDK של Firebase לאדמינים. בדוגמאות האלה, האסימונים נחתמים באופן מקומי באמצעות מפתח פרטי של חשבון שירות, ולכן חלות הנחיות האבטחה לקובצי מפתחות של חשבונות שירות. כדי להימנע מניהול של מפתח פרטי, אתם יכולים לבקש מ-Google לחתום על המטען הייעודי (payload) של האסימון מרחוק, באמצעות קריאה ל-method signJwt של IAM Service Account Credentials API. מבצע הקריאה החוזרת צריך את התפקיד יצירת אסימונים בחשבון שירות בחשבון השירות.
PHP
באמצעות php-jwt:
// Requires: composer require firebase/php-jwt
use Firebase\JWT\JWT;
// Placeholder values. In production, load your service account's email
// address and private key from a secure secret store. Never hard-code
// private keys in source code.
$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
באמצעות ruby-jwt:
require "jwt"
# Placeholder values. In production, load your service account's email
# address and private key from a secure secret store. Never hard-code
# private keys in source code.
$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.
ה-SDK של Firebase לאדמינים משתמש ב-API של IAM כדי לחתום על טוקנים. השגיאה הזו מציינת ש-IAM API לא מופעל כרגע בפרויקט ב-Firebase. פותחים את הקישור שבהודעת השגיאה בדפדפן אינטרנט ולוחצים על הכפתור Enable API כדי להפעיל אותו בפרויקט.
לחשבון השירות אין את ההרשאות הנדרשות
אם לחשבון השירות שבו פועל ה-SDK של Firebase לאדמינים אין את ההרשאה iam.serviceAccounts.signBlob, יכול להיות שתופיע הודעת שגיאה כמו זו:
Permission iam.serviceAccounts.signBlob is required to perform this operation
on service account projects/-/serviceAccounts/{your-service-account-id}.
כדי לפתור את הבעיה, צריך להקצות את התפקיד יצירת אסימונים בחשבון שירות ב-IAM לחשבון השירות המתאים. חשבון השירות שמוגדר כברירת מחדל משתנה בהתאם לסביבה ולגרסה של Cloud Functions:
- Cloud Functions (דור ראשון): משתמש בחשבון השירות שמוגדר כברירת מחדל ב-App Engine.
- Cloud Functions (דור שני): משתמש בחשבון השירות שמוגדר כברירת מחדל ב-Compute Engine.
במסוף Google Cloud, נכנסים לדף IAM and Admin.
לוחצים על סמל העריכה של חשבון השירות שרוצים לעדכן.
לוחצים על הוספת תפקיד.
מקלידים
"Service Account Token Creator"במסנן החיפוש ובוחרים את האפשרות הזו מהתוצאות.לוחצים על Save כדי לאשר את הקצאת התפקיד.
למידע נוסף על התהליך הזה, אפשר לעיין במסמכי התיעוד של IAM או ללמוד איך לעדכן תפקידים באמצעות כלי שורת הפקודה של Google Cloud.
לא הצלחנו לזהות את חשבון השירות
אם מופיעה הודעת שגיאה דומה לזו, סימן שה-SDK של Firebase לאדמינים לא הופעל כמו שצריך.
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 עם שרת מטא-נתונים. אחרת, הקפידו לציין מזהה של חשבון שירות באתחול ה-SDK, או בסביבות שאינן של Google, אתחלו את ה-SDK באמצעות קובץ מפתח של חשבון שירות.