قم بإنشاء رموز مخصصة

يمنحك Firebase تحكمًا كاملاً في المصادقة من خلال السماح لك بمصادقة المستخدمين أو الأجهزة باستخدام JSON Web Tokens (JWTs) الآمنة. يمكنك إنشاء هذه الرموز المميزة على الخادم الخاص بك، وتمريرها مرة أخرى إلى جهاز عميل، ثم استخدامها للمصادقة عبر طريقة signInWithCustomToken() .

لتحقيق ذلك، يجب عليك إنشاء نقطة نهاية خادم تقبل بيانات اعتماد تسجيل الدخول - مثل اسم المستخدم وكلمة المرور - وإذا كانت بيانات الاعتماد صالحة، تقوم بإرجاع JWT مخصص. يمكن بعد ذلك استخدام JWT المخصص الذي تم إرجاعه من الخادم الخاص بك بواسطة جهاز عميل للمصادقة مع Firebase ( iOS+ و Android و web ). بمجرد المصادقة، سيتم استخدام هذه الهوية عند الوصول إلى خدمات Firebase الأخرى، مثل قاعدة بيانات Firebase Realtime وCloud Storage. علاوة على ذلك، ستكون محتويات JWT متاحة في كائن auth في قواعد أمان قاعدة بيانات Realtime وكائن request.auth في قواعد أمان التخزين السحابي لديك.

يمكنك إنشاء رمز مميز باستخدام 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 من إنشاء الرموز المميزة المخصصة وتوقيعها محليًا، دون إجراء أي استدعاءات لواجهة برمجة التطبيقات عن بعد. العيب الرئيسي لهذا الأسلوب هو أنه يتطلب منك حزم ملف JSON لحساب الخدمة مع الكود الخاص بك. لاحظ أيضًا أن المفتاح الخاص في ملف JSON لحساب الخدمة عبارة عن معلومات حساسة، ويجب توخي الحذر بشكل خاص للحفاظ على سريتها. على وجه التحديد، امتنع عن إضافة ملفات JSON الخاصة بحساب الخدمة إلى التحكم في الإصدار العام.

السماح لـ Admin SDK باكتشاف حساب الخدمة

إذا تم نشر التعليمات البرمجية الخاصة بك في بيئة تديرها Google، فيمكن أن تحاول Admin SDK الاكتشاف التلقائي لوسيلة لتوقيع الرموز المميزة:

  • إذا تم نشر التعليمات البرمجية الخاصة بك في بيئة App Engine القياسية لـ Java أو Python أو Go، فيمكن لـ Admin SDK استخدام خدمة معرف التطبيق الموجودة في تلك البيئة لتوقيع الرموز المميزة المخصصة. تقوم خدمة هوية التطبيق بتسجيل البيانات باستخدام حساب الخدمة المقدم لتطبيقك بواسطة 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 لمنح حسابات الخدمة الافتراضية الأذونات اللازمة. راجع قسم استكشاف الأخطاء وإصلاحها أدناه لمزيد من التفاصيل.

استخدام معرف حساب الخدمة

للحفاظ على الاتساق بين الأجزاء المختلفة لتطبيقك، يمكنك تحديد معرف حساب الخدمة الذي سيتم استخدام مفاتيحه لتوقيع الرموز المميزة عند التشغيل في بيئة تديرها 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() :

دائرة الرقابة الداخلية +

ج موضوعية
[[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);
});

سي ++

firebase::Future<firebase::auth::AuthResult> 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;
    // ...
  });

واجهة برمجة تطبيقات الويب المعيارية

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 وكائن 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 Realtime) أو كائن 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 تابعة لجهة خارجية

إذا كانت الواجهة الخلفية لديك بلغة لا تحتوي على حزمة SDK رسمية لمسؤول Firebase، فلا يزال بإمكانك إنشاء رموز مخصصة يدويًا. أولاً، ابحث عن مكتبة 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 الخاصة بقواعد الأمان

فيما يلي بعض الأمثلة على تطبيقات كيفية إنشاء الرموز المميزة في مجموعة متنوعة من اللغات التي لا يدعمها Firebase Admin SDK:

بي أتش بي

باستخدام 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 غير ممكّنة

إذا كنت تحدد معرف حساب الخدمة لتوقيع الرموز المميزة، فقد تحصل على خطأ مشابه لما يلي:

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 لتوقيع الرموز المميزة. يشير هذا الخطأ إلى أن IAM API غير ممكّن حاليًا لمشروع Firebase الخاص بك. افتح الرابط الموجود في رسالة الخطأ في متصفح الويب، ثم انقر فوق الزر "تمكين واجهة برمجة التطبيقات" لتمكينه لمشروعك.

حساب الخدمة ليس لديه الأذونات المطلوبة

إذا كان حساب الخدمة الذي يعمل عليه 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.
  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 لاكتشاف معرف حساب الخدمة تلقائيًا، فتأكد من نشر الرمز في بيئة Google المُدارة باستخدام خادم بيانات التعريف. بخلاف ذلك، تأكد من تحديد ملف JSON لحساب الخدمة أو معرف حساب الخدمة عند تهيئة SDK.