يمنحك Firebase تحكمًا كاملاً في المصادقة من خلال السماح لك بمصادقة المستخدمين أو الأجهزة باستخدام رموز الويب JSON الآمنة (JWTs). يمكنك إنشاء هذه الرموز المميزة على الخادم الخاص بك ، وتمريرها مرة أخرى إلى جهاز عميل ، ثم استخدامها للمصادقة عبر طريقة signInWithCustomToken()
.
لتحقيق ذلك ، يجب عليك إنشاء نقطة نهاية خادم تقبل بيانات اعتماد تسجيل الدخول - مثل اسم المستخدم وكلمة المرور - وإذا كانت بيانات الاعتماد صالحة ، فتُرجع JWT مخصصًا. يمكن بعد ذلك استخدام JWT المخصص الذي تم إرجاعه من الخادم الخاص بك بواسطة جهاز عميل للمصادقة مع Firebase ( iOS + ، Android ، الويب ). بمجرد المصادقة ، سيتم استخدام هذه الهوية عند الوصول إلى خدمات Firebase الأخرى ، مثل Firebase Realtime Database و 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 Console.
- باستخدام معرّف حساب الخدمة - عند استخدامها في بيئة تديرها Google ، ستعمل هذه الطريقة على تسجيل الرموز المميزة باستخدام مفتاح حساب الخدمة المحدد. ومع ذلك ، فإنه يستخدم خدمة ويب بعيدة ، وقد تضطر إلى تكوين أذونات إضافية لحساب الخدمة هذا عبر Google Cloud Console.
استخدام ملف JSON لحساب الخدمة
تحتوي ملفات JSON لحساب الخدمة على جميع المعلومات المقابلة لحسابات الخدمة (بما في ذلك المفتاح الخاص RSA). يمكن تنزيلها من وحدة تحكم Firebase. اتبع تعليمات إعداد Admin SDK للحصول على مزيد من المعلومات حول كيفية تهيئة Admin SDK باستخدام ملف JSON لحساب الخدمة.
تعد طريقة التهيئة هذه مناسبة لمجموعة كبيرة من عمليات نشر Admin SDK. كما أنه يمكّن Admin SDK من إنشاء الرموز المميزة المخصصة وتوقيعها محليًا ، دون إجراء أي استدعاءات لواجهة برمجة التطبيقات عن بُعد. العيب الرئيسي لهذا الأسلوب هو أنه يتطلب منك حزم ملف JSON لحساب الخدمة مع التعليمات البرمجية الخاصة بك. لاحظ أيضًا أن المفتاح الخاص في ملف JSON لحساب الخدمة هو معلومات حساسة ، ويجب توخي الحذر بشكل خاص للحفاظ على سريتها. على وجه التحديد ، الامتناع عن إضافة ملفات JSON لحساب الخدمة إلى التحكم في الإصدار العام.
السماح لـ Admin SDK باكتشاف حساب الخدمة
إذا تم نشر الرمز الخاص بك في بيئة تديرها Google ، فيمكن أن تحاول SDK للمشرف الاكتشاف التلقائي لوسيلة لتوقيع الرموز المميزة المخصصة:
إذا تم نشر التعليمات البرمجية الخاصة بك في بيئة App Engine القياسية لـ Java أو Python أو Go ، فيمكن لـ Admin SDK استخدام خدمة هوية التطبيق الموجودة في تلك البيئة لتوقيع الرموز المميزة المخصصة. تسجّل خدمة App Identity البيانات باستخدام حساب خدمة مخصص لتطبيقك بواسطة Google App Engine.
إذا تم نشر الرمز الخاص بك في بعض البيئة المدارة الأخرى (مثل Google Cloud Functions ، Google Compute Engine) ، يمكن أن يكتشف Firebase Admin SDK تلقائيًا سلسلة معرّف حساب الخدمة من خادم البيانات الوصفية المحلي. يتم بعد ذلك استخدام معرف حساب الخدمة المكتشف جنبًا إلى جنب مع خدمة IAM لتوقيع الرموز المميزة عن بُعد.
للاستفادة من طرق التوقيع هذه ، قم بتهيئة SDK باستخدام بيانات اعتماد Google Application Default ولا تحدد سلسلة معرّف حساب الخدمة:
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 Console لمنح حسابات الخدمة الافتراضية الأذونات اللازمة. راجع قسم استكشاف الأخطاء وإصلاحها أدناه للحصول على مزيد من التفاصيل.
باستخدام معرف حساب الخدمة
للحفاظ على التناسق بين أجزاء مختلفة من تطبيقك ، يمكنك تحديد معرف حساب الخدمة الذي سيتم استخدام مفاتيحه لتسجيل الرموز المميزة عند التشغيل في بيئة تديرها 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 خدمة عن بُعد. علاوة على ذلك ، يجب عليك أيضًا التأكد من أن حساب الخدمة الذي تستخدمه 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()
:
iOS +
ج موضوعية
[[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);
واجهة برمجة تطبيقات Web namespaced
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 API لتسجيل الرموز المميزة. يشير هذا الخطأ إلى أن واجهة برمجة تطبيقات IAM غير ممكَّنة حاليًا لمشروع 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
:
- افتح IAM وصفحة المسؤول في Google Cloud Console.
- حدد مشروعك وانقر على "متابعة".
- انقر فوق رمز التحرير المقابل لحساب الخدمة الذي ترغب في تحديثه.
- انقر فوق "إضافة دور آخر".
- اكتب "مُنشئ حساب الخدمة" في فلتر البحث ، وحدده من النتائج.
- انقر على "حفظ" لتأكيد منح الدور.
راجع وثائق 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.