צור אסימונים מותאמים אישית

Firebase נותן לך שליטה מלאה על האימות בכך שהוא מאפשר לך לאמת משתמשים או מכשירים באמצעות אסימוני אינטרנט מאובטחים של JSON (JWT). אתה יוצר את האסימונים האלה בשרת שלך, מעביר אותם בחזרה למכשיר לקוח ואז משתמש בהם כדי לאמת באמצעות שיטת signInWithCustomToken() .

כדי להשיג זאת, עליך ליצור נקודת קצה של שרת שמקבלת אישורי כניסה - כגון שם משתמש וסיסמה - ואם האישורים תקפים, מחזירה JWT מותאם אישית. לאחר מכן, ה-JWT המותאם אישית שהוחזר מהשרת שלך יכול לשמש על ידי מכשיר לקוח לאימות עם Firebase ( iOS+ , Android , אינטרנט ). לאחר האימות, זהות זו תשמש בעת גישה לשירותי Firebase אחרים, כגון Firebase Realtime Database ו-Cloud Storage. יתרה מזאת, התוכן של ה-JWT יהיה זמין באובייקט auth בכללי האבטחה של מסד הנתונים האמיתי שלך ובאובייקט request.auth בכללי האבטחה של Cloud Storage .

אתה יכול ליצור אסימון מותאם אישית עם Firebase Admin SDK, או שאתה יכול להשתמש בספריית JWT של צד שלישי אם השרת שלך כתוב בשפה שבה Firebase לא תומך באופן טבעי.

לפני שאתה מתחיל

אסימונים מותאמים אישית הם JWTs חתומים כאשר המפתח הפרטי המשמש לחתימה שייך לחשבון שירות 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), ה-SDK של Firebase Admin יכול לגלות אוטומטית מחרוזת מזהה חשבון שירות משרת המטא נתונים המקומי. מזהה חשבון השירות שהתגלה משמש לאחר מכן יחד עם שירות IAM כדי לחתום על אסימונים מרחוק.

כדי לעשות שימוש בשיטות החתימה האלה, אתחל את ה-SDK עם אישורי ברירת המחדל של Google Application ואל תציין מחרוזת מזהה חשבון שירות:

Node.js

initializeApp();

Java

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 כך שיצביע עליו.

אם ה-SDK של Firebase Admin חייב לגלות מחרוזת מזהה חשבון שירות, הוא עושה זאת כאשר הקוד שלך יוצר אסימון מותאם אישית בפעם הראשונה. התוצאה נשמרת במטמון ועושה שימוש חוזר עבור פעולות חתימת אסימונים עוקבות. מזהה חשבון השירות שהתגלה אוטומטית הוא בדרך כלל אחד מחשבונות השירות המוגדרים כברירת מחדל שמסופקים על ידי Google Cloud:

בדיוק כמו עם מזהי חשבון שירות שצוינו במפורש, מזהי חשבון שירות לגילוי אוטומטי חייבים להיות בעלי הרשאת 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',
});

Java

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

C#

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

מזהי חשבון שירות אינם מידע רגיש ולכן חשיפתם אינה משמעותית. עם זאת, כדי לחתום על אסימונים מותאמים אישית עם חשבון השירות שצוין, ה-SDK של Firebase Admin חייב להפעיל שירות מרוחק. יתרה מכך, עליך גם לוודא שלחשבון השירות ש-SDK ה-Admin משתמש כדי לבצע קריאה זו - בדרך כלל {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);
  });

Java

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

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

פִּיתוֹן

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+

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

C++

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

API עם מרחב שמות אינטרנט

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

API מודולרי אינטרנט

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 בכללי האבטחה של מסד הנתונים בזמן אמת והאובייקט request.auth בכללי האבטחה של Cloud Storage יאוכלסו ב- 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 (אופציונלי) תביעות אופציונליות מותאמות אישית שיש לכלול בכללי האבטחה משתני auth / request.auth

להלן כמה יישומים לדוגמה כיצד ליצור אסימונים מותאמים אישית במגוון שפות ש-SDK Admin של Firebase אינו תומך:

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 כדי לחתום על אסימונים. שגיאה זו מציינת שה-API של IAM אינו מופעל כעת עבור פרויקט Firebase שלך. פתח את הקישור בהודעת השגיאה בדפדפן אינטרנט ולחץ על כפתור "הפעל API" כדי להפעיל אותו עבור הפרויקט שלך.

לחשבון השירות אין הרשאות נדרשות

אם לחשבון השירות ש-SDK של Firebase Admin פועל כמו אין הרשאת 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.
  2. בחר את הפרויקט שלך ולחץ על "המשך".
  3. לחץ על סמל העריכה המתאים לחשבון השירות שברצונך לעדכן.
  4. לחץ על "הוסף תפקיד נוסף".
  5. הקלד "יוצר חשבון שירות אסימון" במסנן החיפוש ובחר אותו מהתוצאות.
  6. לחץ על "שמור" כדי לאשר את הענקת התפקיד.

עיין בתיעוד של IAM לפרטים נוספים על תהליך זה, או למד כיצד לבצע תפקידי עדכון באמצעות כלי שורת הפקודה gcloud.

קביעת חשבון השירות נכשלה

אם אתה מקבל הודעת שגיאה דומה להלן, ה-SDK של Firebase Admin לא אותחל כהלכה.

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.