تتيح لك دوال الحظر تنفيذ رمز مخصّص يعدّل نتيجة تسجيل المستخدم أو تسجيل دخوله إلى تطبيقك. على سبيل المثال، يمكنك منع المستخدم من المصادقة إذا لم يستوفِ معايير معيّنة، أو تعديل معلومات المستخدم قبل إعادتها إلى تطبيق العميل.
قبل البدء
لاستخدام دوال الحظر، عليك ترقية مشروع Firebase إلى Firebase Authentication with Identity Platform. إذا لم تكن قد أجريت الترقية بعد، عليك إجراؤها أولاً.
فهم دوال الحظر
يمكنك تسجيل دوال الحظر للأحداث التالية:
قبل إنشاء المستخدم: يتم تشغيل هذا الحدث قبل حفظ مستخدم جديد في قاعدة بيانات Firebase Authentication وقبل إرجاع رمز مميّز إلى تطبيق العميل.
قبل تسجيل دخول المستخدم: يتم تفعيل هذه الدالة بعد التحقّق من بيانات اعتماد المستخدم، ولكن قبل أن تعرض Firebase Authentication رمز تعريف مميّزًا لتطبيق العميل. إذا كان تطبيقك يستخدم المصادقة المتعدّدة العوامل، يتم تفعيل الدالة بعد أن يتحقّق المستخدم من العامل الثاني. يُرجى العِلم أنّ إنشاء مستخدم جديد يؤدي أيضًا إلى بدء هذين الحدثَين.
قبل إرسال رسالة إلكترونية (Node.js فقط): يتم تفعيل هذا الحدث قبل إرسال رسالة إلكترونية (مثل رسالة تسجيل الدخول أو إعادة ضبط كلمة المرور) إلى المستخدم.
قبل إرسال رسالة SMS (Node.js فقط): يتم تشغيلها قبل إرسال رسالة SMS إلى مستخدم، في حالات مثل المصادقة المتعدّدة العوامل.
يُرجى مراعاة ما يلي عند استخدام دوال الحظر:
يجب أن تستجيب الدالة في غضون 7 ثوانٍ. بعد 7 ثوانٍ، تعرض Firebase Authentication خطأً، وتتعذّر عملية العميل.
يتم تمرير رموز استجابة HTTP بخلاف
200إلى تطبيقات العميل. تأكَّد من أنّ رمز العميل يعالج أي أخطاء يمكن أن تعرضها الدالة.تنطبق الدوال على جميع المستخدمين في مشروعك، بما في ذلك أي مستخدمين في مستأجر. توفّر Firebase Authentication معلومات عن المستخدمين لدالّتك، بما في ذلك أي مستأجرين ينتمون إليهم، لكي تتمكّن من الردّ وفقًا لذلك.
يؤدي ربط موفّر هوية آخر بحساب إلى إعادة تفعيل أي دوال
beforeUserSignedInمسجَّلة.لا تؤدي المصادقة المجهولة والمخصّصة إلى تفعيل دوال الحظر.
نشر دالة حظر
لإدراج الرمز المخصّص في مسارات مصادقة المستخدمين، يجب نشر دوال الحظر. بعد نشر دوال الحظر، يجب أن يكتمل الرمز المخصّص بنجاح لإتمام عملية المصادقة وإنشاء المستخدم.
يمكنك نشر دالة حظر بالطريقة نفسها التي تنشر بها أي دالة. (يمكنك الاطّلاع على صفحة Cloud Functions البدء لمعرفة التفاصيل). وباختصار:
اكتب دالة تعالج الحدث المستهدف.
على سبيل المثال، للبدء، يمكنك إضافة دالة بدون تشغيل مثل التالي إلى المصدر:
Node.js
import { beforeUserCreated, } from "firebase-functions/v2/identity"; export const beforecreated = beforeUserCreated((event) => { // TODO return; });Python
@identity_fn.before_user_created() def created_noop( event: identity_fn.AuthBlockingEvent, ) -> identity_fn.BeforeCreateResponse | None: returnلم يتضمّن المثال أعلاه تنفيذ منطق المصادقة المخصّص. يمكنك الاطّلاع على الأقسام التالية لمعرفة كيفية تنفيذ دوال الحظر والسيناريوهات الشائعة للحصول على أمثلة محدّدة.
انشر الدوال باستخدام واجهة سطر أوامر Firebase:
firebase deploy --only functionsيجب إعادة نشر الدوال في كل مرة يتم تعديلها.
الحصول على معلومات المستخدم والسياق
توفّر أحداث الحظر كائن AuthBlockingEvent
يحتوي على معلومات عن المستخدم الذي يسجّل الدخول. استخدِم هذه القيم في الرمز البرمجي لتحديد ما إذا كان سيتم السماح بمواصلة العملية.
يحتوي الكائن على السمات التالية:
| الاسم | الوصف | مثال |
|---|---|---|
locale |
تمثّل هذه السمة لغة التطبيق. يمكنك ضبط اللغة باستخدام حزمة تطوير البرامج (SDK) للعميل أو من خلال تمرير عنوان اللغة في واجهة برمجة التطبيقات REST API. | fr أو sv-SE |
ipAddress
| عنوان IP للجهاز الذي يسجّل المستخدم النهائي منه أو يسجّل الدخول منه | 114.14.200.1 |
userAgent
| وكيل المستخدم الذي يشغّل وظيفة الحظر | Mozilla/5.0 (X11; Linux x86_64) |
eventId
| المعرّف الفريد للحدث | rWsyPtolplG2TBFoOkkgyg |
eventType
|
نوع الحدث يوفّر ذلك معلومات عن اسم الحدث، مثل beforeSignIn أو beforeCreate، وطريقة تسجيل الدخول المرتبطة به، مثل Google أو البريد الإلكتروني/كلمة المرور.
|
providers/cloud.auth/eventTypes/user.beforeSignIn:password
|
authType
| دائمًا USER |
USER
|
resource
| مشروع أو مستأجر Firebase Authentication |
projects/project-id/tenants/tenant-id
|
timestamp
| وقت بدء الحدث، بتنسيق سلسلة RFC 3339 | Tue, 23 Jul 2019 21:10:57 GMT
|
additionalUserInfo
| عنصر يحتوي على معلومات عن المستخدم |
AdditionalUserInfo
|
credential
| عنصر يحتوي على معلومات حول بيانات اعتماد المستخدم |
AuthCredential
|
حظر التسجيل أو تسجيل الدخول
لحظر محاولة تسجيل أو تسجيل دخول، يمكنك استخدام HttpsError في
الدالة. على سبيل المثال:
Node.js
import { HttpsError } from "firebase-functions/v2/identity";
throw new HttpsError('invalid-argument');
Python
raise https_fn.HttpsError(
code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT)
يمكنك أيضًا تحديد رسالة خطأ مخصّصة:
Node.js
throw new HttpsError('permission-denied', 'Unauthorized request origin!');
Python
raise https_fn.HttpsError(
code=https_fn.FunctionsErrorCode.PERMISSION_DENIED,
message="Unauthorized request origin!"
)
يوضّح المثال التالي كيفية حظر المستخدمين غير التابعين لنطاق معيّن من التسجيل في تطبيقك:
Node.js
export const beforecreated = beforeUserCreated((event) => {
const user = event.data;
// (If the user is authenticating within a tenant context, the tenant ID can be determined from
// user.tenantId or from event.resource, e.g. 'projects/project-id/tenant/tenant-id-1')
// Only users of a specific domain can sign up.
if (!user?.email?.includes('@acme.com')) {
throw new HttpsError('invalid-argument', "Unauthorized email");
}
});
Python
# Block account creation with any non-acme email address.
@identity_fn.before_user_created()
def validatenewuser(
event: identity_fn.AuthBlockingEvent,
) -> identity_fn.BeforeCreateResponse | None:
# User data passed in from the CloudEvent.
user = event.data
# Only users of a specific domain can sign up.
if user.email is None or "@acme.com" not in user.email:
# Return None so that Firebase Auth rejects the account creation.
raise https_fn.HttpsError(
code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,
message="Unauthorized email",
)
بغض النظر عمّا إذا كنت تستخدم رسالة تلقائية أو مخصّصة، يغلّف Cloud Functions الخطأ ويعيده إلى العميل كخطأ داخلي. على سبيل المثال:
Node.js
throw new HttpsError('invalid-argument', "Unauthorized email");
Python
# Only users of a specific domain can sign up.
if user.email is None or "@acme.com" not in user.email:
# Return None so that Firebase Auth rejects the account creation.
raise https_fn.HttpsError(
code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,
message="Unauthorized email",
)
يجب أن يرصد تطبيقك الخطأ ويتعامل معه وفقًا لذلك. على سبيل المثال:
JavaScript
import { getAuth, createUserWithEmailAndPassword } from 'firebase/auth';
// Blocking functions can also be triggered in a multi-tenant context before user creation.
// firebase.auth().tenantId = 'tenant-id-1';
const auth = getAuth();
try {
const result = await createUserWithEmailAndPassword(auth)
const idTokenResult = await result.user.getIdTokenResult();
console.log(idTokenResult.claim.admin);
} catch(error) {
if (error.code !== 'auth/internal-error' && error.message.indexOf('Cloud Function') !== -1) {
// Display error.
} else {
// Registration succeeds.
}
}
تعديل مستخدم
بدلاً من حظر محاولة تسجيل أو تسجيل دخول، يمكنك السماح بمواصلة
العملية، ولكن مع تعديل عنصر User الذي يتم حفظه في
قاعدة بيانات Firebase Authentication وإعادته إلى العميل.
لتعديل مستخدم، يجب عرض عنصر من معالج الأحداث يحتوي على الحقول المطلوب تعديلها. يمكنك تعديل الحقول التالية:
displayNamedisabledemailVerifiedphotoURLcustomClaimssessionClaims(beforeUserSignedInفقط)
باستثناء sessionClaims، يتم حفظ جميع الحقول المعدَّلة في قاعدة بيانات Firebase Authentication، ما يعني أنّها مضمَّنة في الرمز المميّز للاستجابة وتظل محفوظة بين جلسات المستخدمين.
يوضّح المثال التالي كيفية ضبط اسم معروض تلقائي:
Node.js
export const beforecreated = beforeUserCreated((event) => {
return {
// If no display name is provided, set it to "Guest".
displayName: event.data.displayName || 'Guest'
};
});
Python
@identity_fn.before_user_created()
def setdefaultname(
event: identity_fn.AuthBlockingEvent,
) -> identity_fn.BeforeCreateResponse | None:
return identity_fn.BeforeCreateResponse(
# If no display name is provided, set it to "Guest".
display_name=event.data.display_name if event.data.display_name is not None else "Guest"
)
في حال تسجيل معالج أحداث لكل من beforeUserCreated وbeforeUserSignedIn،
يُرجى العِلم أنّ beforeUserSignedIn يتم تنفيذه بعد beforeUserCreated. تظهر حقول المستخدمين التي تم تعديلها في beforeUserCreated في beforeUserSignedIn. إذا ضبطت حقلاً غير
sessionClaims في كلا معالجَي الأحداث، ستؤدي القيمة المحدّدة في beforeUserSignedIn
إلى استبدال القيمة المحدّدة في beforeUserCreated. بالنسبة إلى sessionClaims فقط، يتم نشرها في مطالبات الرموز المميزة للجلسة الحالية، ولكن لا يتم الاحتفاظ بها أو تخزينها في قاعدة البيانات.
على سبيل المثال، إذا تم ضبط أي sessionClaims، ستعرضها beforeUserSignedIn مع أي مطالبات beforeUserCreated، وسيتم دمجها. عند دمجهما، إذا تطابق مفتاح sessionClaims مع مفتاح في customClaims، سيتم استبدال customClaims المطابق في مطالبات الرمز المميّز بمفتاح sessionClaims. ومع ذلك، سيظل مفتاح customClaims الذي تم استبداله محفوظًا في
قاعدة البيانات للطلبات المستقبلية.
بيانات اعتماد OAuth والبيانات المتوافقة
يمكنك تمرير بيانات اعتماد OAuth والبيانات إلى دوال الحظر من مختلف موفّري الهوية. يعرض الجدول التالي بيانات الاعتماد والبيانات المتوافقة مع كل موفِّر هوية:
| موفِّر الهوية | رمز التعريف المميّز | رمز الدخول | وقت انتهاء الصلاحية: | الرمز المميّز السرّي | الرمز المميّز لإعادة التحميل | مطالبات تسجيل الدخول |
|---|---|---|---|---|---|---|
| نعم | نعم | نعم | لا | نعم | لا | |
| لا | نعم | نعم | لا | لا | لا | |
| لا | نعم | لا | نعم | لا | لا | |
| GitHub | لا | نعم | لا | لا | لا | لا |
| Microsoft | نعم | نعم | نعم | لا | نعم | لا |
| لا | نعم | نعم | لا | لا | لا | |
| Yahoo | نعم | نعم | نعم | لا | نعم | لا |
| Apple | نعم | نعم | نعم | لا | نعم | لا |
| SAML | لا | لا | لا | لا | لا | نعم |
| OIDC | نعم | نعم | نعم | لا | نعم | نعم |
رموز OAuth المميّزة
لاستخدام رمز مميّز للمعرّف أو رمز مميّز للوصول أو رمز مميّز لإعادة التحميل في دالة حظر، عليك أولاً وضع علامة في مربّع الاختيار في صفحة دوال الحظر ضمن وحدة تحكّم Firebase (انتقِل إلى الأمان > المصادقة > علامة التبويب الإعدادات).
لن يعرض أي من موفّري الهوية الرموز المميزة لإعادة التحميل عند تسجيل الدخول مباشرةً باستخدام بيانات اعتماد OAuth، مثل الرمز المميز للمعرّف أو رمز الدخول. في هذه الحالة، سيتم تمرير بيانات اعتماد OAuth نفسها من جهة العميل إلى دالة الحظر.
توضّح الأقسام التالية كل نوع من أنواع موفّري الهوية وبيانات الاعتماد والبيانات المتوافقة معها.
مقدّمو خدمات OIDC العامة
عندما يسجّل مستخدم الدخول باستخدام موفّر OIDC عام، سيتم تمرير بيانات الاعتماد التالية:
- رمز التعريف المميّز: يتم توفيره في حال اختيار مسار
id_token. - رمز الدخول: يتم توفيره في حال اختيار تدفق الرمز. يُرجى العِلم أنّ مسار الرمز متاح حاليًا فقط من خلال REST API.
- الرمز المميّز لإعادة التحميل: يتم توفيره في حال اختيار
نطاق
offline_access.
مثال:
const provider = new firebase.auth.OAuthProvider('oidc.my-provider');
provider.addScope('offline_access');
firebase.auth().signInWithPopup(provider);
عندما يسجّل مستخدم الدخول باستخدام حساب Google، سيتم تمرير بيانات الاعتماد التالية:
- الرمز المميّز للمعرّف
- رمز الدخول
- الرمز المميز لإعادة التحميل: لا يتم توفيره إلا في حال طلب المَعلمات المخصّصة التالية:
access_type=offlineprompt=consent، إذا وافق المستخدم سابقًا ولم يتم طلب نطاق جديد
مثال:
import { getAuth, signInWithPopup, GoogleAuthProvider } from 'firebase/auth';
const auth = getAuth();
const provider = new GoogleAuthProvider();
provider.setCustomParameters({
'access_type': 'offline',
'prompt': 'consent'
});
signInWithPopup(auth, provider);
مزيد من المعلومات عن الرموز المميّزة لإعادة التحميل من Google
عندما يسجّل مستخدم الدخول باستخدام Facebook، سيتم تمرير بيانات الاعتماد التالية:
- رمز الدخول: يتم عرض رمز دخول يمكن استبداله برمز دخول آخر. يمكنك الاطّلاع على مزيد من المعلومات عن الأنواع المختلفة من رموز الدخول المتوافقة مع Facebook وكيفية استبدالها برموز مميّزة طويلة الأجل.
GitHub
عندما يسجّل مستخدم الدخول باستخدام GitHub، سيتم تمرير بيانات الاعتماد التالية:
- رمز الدخول: لا تنتهي صلاحيته ما لم يتم إبطاله.
Microsoft
عندما يسجّل مستخدم الدخول باستخدام Microsoft، سيتم تمرير بيانات الاعتماد التالية:
- الرمز المميّز للمعرّف
- رمز الدخول
- الرمز المميز لإعادة التحميل: يتم تمريره إلى دالة الحظر في حال اختيار
نطاق
offline_access.
مثال:
import { getAuth, signInWithPopup, OAuthProvider } from 'firebase/auth';
const auth = getAuth();
const provider = new OAuthProvider('microsoft.com');
provider.addScope('offline_access');
signInWithPopup(auth, provider);
Yahoo
عندما يسجّل مستخدم الدخول باستخدام Yahoo، سيتم تمرير بيانات الاعتماد التالية بدون أي مَعلمات مخصّصة أو نطاقات:
- الرمز المميّز للمعرّف
- رمز الدخول
- الرمز المميّز لإعادة التحميل
عندما يسجّل مستخدم الدخول باستخدام LinkedIn، سيتم تمرير بيانات الاعتماد التالية:
- رمز الدخول
Apple
عندما يسجّل مستخدم الدخول باستخدام Apple، سيتم تمرير بيانات الاعتماد التالية بدون أي مَعلمات مخصّصة أو نطاقات:
- الرمز المميّز للمعرّف
- رمز الدخول
- الرمز المميّز لإعادة التحميل
السيناريوهات الشائعة
توضّح الأمثلة التالية بعض حالات الاستخدام الشائعة لدوال الحظر:
السماح بالتسجيل من نطاق محدّد فقط
يوضّح المثال التالي كيفية منع المستخدمين غير التابعين لنطاق example.com من التسجيل في تطبيقك:
Node.js
export const beforecreated = beforeUserCreated((event) => {
const user = event.data;
if (!user?.email?.includes('@example.com')) {
throw new HttpsError(
'invalid-argument', 'Unauthorized email');
}
});
Python
@identity_fn.before_user_created()
def validatenewuser(
event: identity_fn.AuthBlockingEvent,
) -> identity_fn.BeforeCreateResponse | None:
# User data passed in from the CloudEvent.
user = event.data
# Only users of a specific domain can sign up.
if user.email is None or "@example.com" not in user.email:
# Return None so that Firebase Auth rejects the account creation.
raise https_fn.HttpsError(
code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,
message="Unauthorized email",
)
حظر تسجيل المستخدمين الذين لديهم عناوين بريد إلكتروني لم يتم التحقّق منها
يوضّح المثال التالي كيفية منع المستخدمين الذين لديهم عناوين بريد إلكتروني لم يتم التحقّق منها من التسجيل في تطبيقك:
Node.js
export const beforecreated = beforeUserCreated((event) => {
const user = event.data;
if (user.email && !user.emailVerified) {
throw new HttpsError(
'invalid-argument', 'Unverified email');
}
});
Python
@identity_fn.before_user_created()
def requireverified(
event: identity_fn.AuthBlockingEvent,
) -> identity_fn.BeforeCreateResponse | None:
if event.data.email is not None and not event.data.email_verified:
raise https_fn.HttpsError(
code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,
message="You must register using a trusted provider.",
)
اعتبار بعض عناوين البريد الإلكتروني لموفّري الهوية على أنّها تم التحقّق منها
يوضّح المثال التالي كيفية التعامل مع عناوين البريد الإلكتروني للمستخدمين من موفّري هوية معيّنين على أنّها تم التحقّق منها:
Node.js
export const beforecreated = beforeUserCreated((event) => {
const user = event.data;
if (user.email && !user.emailVerified && event.eventType.includes(':facebook.com')) {
return {
emailVerified: true,
};
}
});
Python
@identity_fn.before_user_created()
def markverified(
event: identity_fn.AuthBlockingEvent,
) -> identity_fn.BeforeCreateResponse | None:
if event.data.email is not None and "@facebook.com" in event.data.email:
return identity_fn.BeforeSignInResponse(email_verified=True)
حظر تسجيل الدخول من عناوين IP معيّنة
يوضّح المثال التالي كيفية حظر تسجيل الدخول من نطاقات عناوين IP معيّنة:
Node.js
export const beforesignedin = beforeUserSignedIn((event) => {
if (isSuspiciousIpAddress(event.ipAddress)) {
throw new HttpsError(
'permission-denied', 'Unauthorized access!');
}
});
Python
@identity_fn.before_user_signed_in()
def ipban(
event: identity_fn.AuthBlockingEvent,
) -> identity_fn.BeforeSignInResponse | None:
if is_suspicious(event.ip_address):
raise https_fn.HttpsError(
code=https_fn.FunctionsErrorCode.PERMISSION_DENIED, message="IP banned."
)
ضبط المطالبات المخصّصة ومطالبات الجلسات
يوضّح المثال التالي كيفية ضبط المطالبات المخصّصة ومطالبات الجلسة:
Node.js
export const beforecreated = beforeUserCreated((event) => {
if (event.credential &&
event.credential.claims &&
event.credential.providerId === "saml.my-provider-id") {
return {
// Employee ID does not change so save in persistent claims (stored in
// Auth DB).
customClaims: {
eid: event.credential.claims.employeeid,
},
};
}
});
export const beforesignin = beforeUserSignedIn((event) => {
if (event.credential &&
event.credential.claims &&
event.credential.providerId === "saml.my-provider-id") {
return {
// Copy role and groups to token claims. These will not be persisted.
sessionClaims: {
role: event.credential.claims.role,
groups: event.credential.claims.groups,
},
};
}
});
Python
@identity_fn.before_user_created()
def setemployeeid(
event: identity_fn.AuthBlockingEvent,
) -> identity_fn.BeforeCreateResponse | None:
if (
event.credential is not None
and event.credential.claims is not None
and event.credential.provider_id == "saml.my-provider-id"
):
return identity_fn.BeforeCreateResponse(
custom_claims={"eid": event.credential.claims["employeeid"]}
)
@identity_fn.before_user_signed_in()
def copyclaimstosession(
event: identity_fn.AuthBlockingEvent,
) -> identity_fn.BeforeSignInResponse | None:
if (
event.credential is not None
and event.credential.claims is not None
and event.credential.provider_id == "saml.my-provider-id"
):
return identity_fn.BeforeSignInResponse(
session_claims={
"role": event.credential.claims["role"],
"groups": event.credential.claims["groups"],
}
)
تتبُّع عناوين IP لمراقبة النشاط المريب
يمكنك منع سرقة الرموز المميّزة من خلال تتبُّع عنوان IP الذي يسجّل المستخدم الدخول منه، ومقارنته بعنوان IP في الطلبات اللاحقة. إذا بدا الطلب مريبًا، على سبيل المثال، إذا كانت عناوين IP من مناطق جغرافية مختلفة، يمكنك أن تطلب من المستخدم تسجيل الدخول مرة أخرى.
استخدِم مطالبات الجلسة لتتبُّع عنوان IP الذي يسجّل المستخدم الدخول من خلاله:
Node.js
export const beforesignedin = beforeUserSignedIn((event) => { return { sessionClaims: { signInIpAddress: event.ipAddress, }, }; });Python
@identity_fn.before_user_signed_in() def logip( event: identity_fn.AuthBlockingEvent, ) -> identity_fn.BeforeSignInResponse | None: return identity_fn.BeforeSignInResponse(session_claims={"signInIpAddress": event.ip_address})عندما يحاول مستخدم الوصول إلى الموارد التي تتطلب المصادقة باستخدام Firebase Authentication، تتم مقارنة عنوان IP في الطلب بعنوان IP المستخدَم لتسجيل الدخول:
Node.js
app.post('/getRestrictedData', (req, res) => { // Get the ID token passed. const idToken = req.body.idToken; // Verify the ID token, check if revoked and decode its payload. admin.auth().verifyIdToken(idToken, true).then((claims) => { // Get request IP address const requestIpAddress = req.connection.remoteAddress; // Get sign-in IP address. const signInIpAddress = claims.signInIpAddress; // Check if the request IP address origin is suspicious relative to // the session IP addresses. The current request timestamp and the // auth_time of the ID token can provide additional signals of abuse, // especially if the IP address suddenly changed. If there was a sudden // geographical change in a short period of time, then it will give // stronger signals of possible abuse. if (!isSuspiciousIpAddressChange(signInIpAddress, requestIpAddress)) { // Suspicious IP address change. Require re-authentication. // You can also revoke all user sessions by calling: // admin.auth().revokeRefreshTokens(claims.sub). res.status(401).send({error: 'Unauthorized access. Please login again!'}); } else { // Access is valid. Try to return data. getData(claims).then(data => { res.end(JSON.stringify(data); }, error => { res.status(500).send({ error: 'Server error!' }) }); } }); });Python
from firebase_admin import auth, initialize_app import flask initialize_app() flask_app = flask.Flask(__name__) @flask_app.post() def get_restricted_data(req: flask.Request): # Get the ID token passed. id_token = req.json().get("idToken") # Verify the ID token, check if revoked, and decode its payload. try: claims = auth.verify_id_token(id_token, check_revoked=True) except: return flask.Response(status=500) # Get request IP address. request_ip = req.remote_addr # Get sign-in IP address. signin_ip = claims["signInIpAddress"] # Check if the request IP address origin is suspicious relative to # the session IP addresses. The current request timestamp and the # auth_time of the ID token can provide additional signals of abuse, # especially if the IP address suddenly changed. If there was a sudden # geographical change in a short period of time, then it will give # stronger signals of possible abuse. if is_suspicious_change(signin_ip, request_ip): # Suspicious IP address change. Require re-authentication. # You can also revoke all user sessions by calling: # auth.revoke_refresh_tokens(claims["sub"]) return flask.Response(status=401, response="Unauthorized access. Sign in again!") else: # Access is valid. Try to return data. return data_from_claims(claims)
فحص صور المستخدمين
يوضّح المثال التالي كيفية تنظيف صور الملفات الشخصية للمستخدمين:
Node.js
export const beforecreated = beforeUserCreated((event) => {
const user = event.data;
if (user.photoURL) {
return isPhotoAppropriate(user.photoURL)
.then((status) => {
if (!status) {
// Sanitize inappropriate photos by replacing them with guest photos.
// Users could also be blocked from sign-up, disabled, etc.
return {
photoURL: PLACEHOLDER_GUEST_PHOTO_URL,
};
}
});
});
Python
@identity_fn.before_user_created()
def sanitizeprofilephoto(
event: identity_fn.AuthBlockingEvent,
) -> identity_fn.BeforeCreateResponse | None:
if event.data.photo_url is not None:
score = analyze_photo_with_ml(event.data.photo_url)
if score > THRESHOLD:
return identity_fn.BeforeCreateResponse(photo_url=PLACEHOLDER_URL)
لمزيد من المعلومات حول كيفية رصد الصور وتنظيفها، يُرجى الاطّلاع على مستندات Cloud Vision.
الوصول إلى بيانات اعتماد OAuth الخاصة بموفّر الهوية للمستخدم
يوضّح المثال التالي كيفية الحصول على رمز مميز لإعادة التحميل لمستخدم سجّل الدخول باستخدام حساب Google، واستخدامه لطلب واجهات برمجة تطبيقات "تقويم Google". يتم تخزين الرمز المميز لإعادة التحميل للوصول بلا إنترنت.
Node.js
const {OAuth2Client} = require('google-auth-library');
const {google} = require('googleapis');
// ...
// Initialize Google OAuth client.
const keys = require('./oauth2.keys.json');
const oAuth2Client = new OAuth2Client(
keys.web.client_id,
keys.web.client_secret
);
export const beforecreated = beforeUserCreated((event) => {
const user = event.data;
if (event.credential &&
event.credential.providerId === 'google.com') {
// Store the refresh token for later offline use.
// These will only be returned if refresh tokens credentials are included
// (enabled by Cloud console).
return saveUserRefreshToken(
user.uid,
event.credential.refreshToken,
'google.com'
)
.then(() => {
// Blocking the function is not required. The function can resolve while
// this operation continues to run in the background.
return new Promise((resolve, reject) => {
// For this operation to succeed, the appropriate OAuth scope should be requested
// on sign in with Google, client-side. In this case:
// https://www.googleapis.com/auth/calendar
// You can check granted_scopes from within:
// event.additionalUserInfo.profile.granted_scopes (space joined list of scopes).
// Set access token/refresh token.
oAuth2Client.setCredentials({
access_token: event.credential.accessToken,
refresh_token: event.credential.refreshToken,
});
const calendar = google.calendar('v3');
// Setup Onboarding event on user's calendar.
const event = {/** ... */};
calendar.events.insert({
auth: oauth2client,
calendarId: 'primary',
resource: event,
}, (err, event) => {
// Do not fail. This is a best effort approach.
resolve();
});
});
})
}
});
Python
@identity_fn.before_user_created()
def savegoogletoken(
event: identity_fn.AuthBlockingEvent,
) -> identity_fn.BeforeCreateResponse | None:
"""During sign-up, save the Google OAuth2 access token and queue up a task
to schedule an onboarding session on the user's Google Calendar.
You will only get an access token if you enabled it in your project's blocking
functions settings in the Firebase console:
https://console.firebase.google.com/project/_/authentication/settings
"""
if event.credential is not None and event.credential.provider_id == "google.com":
print(f"Signed in with {event.credential.provider_id}. Saving access token.")
firestore_client: google.cloud.firestore.Client = firestore.client()
doc_ref = firestore_client.collection("user_info").document(event.data.uid)
doc_ref.set({"calendar_access_token": event.credential.access_token}, merge=True)
tasks_client = google.cloud.tasks_v2.CloudTasksClient()
task_queue = tasks_client.queue_path(
params.PROJECT_ID.value, options.SupportedRegion.US_CENTRAL1.value, "scheduleonboarding"
)
target_uri = get_function_url("scheduleonboarding")
calendar_task = google.cloud.tasks_v2.Task(
http_request={
"http_method": google.cloud.tasks_v2.HttpMethod.POST,
"url": target_uri,
"headers": {"Content-type": "application/json"},
"body": json.dumps({"data": {"uid": event.data.uid}}).encode(),
},
schedule_time=datetime.now() + timedelta(minutes=1),
)
tasks_client.create_task(parent=task_queue, task=calendar_task)
تجاوز قرار reCAPTCHA Enterprise لعملية المستخدم
يوضّح المثال التالي كيفية إلغاء قرار reCAPTCHA Enterprise لمسارات المستخدمين المتوافقة.
يُرجى الرجوع إلى مقالة تفعيل reCAPTCHA Enterprise لمزيد من المعلومات حول دمج reCAPTCHA Enterprise مع ميزة "مصادقة Firebase".
يمكن استخدام دوال الحظر للسماح بالعمليات أو حظرها استنادًا إلى عوامل مخصّصة، وبالتالي إلغاء النتيجة التي تقدّمها reCAPTCHA Enterprise.
Node.js
const { beforeSmsSent } = require("firebase-functions/v2/identity");
exports.beforesmssentv2 = beforeSmsSent((event) => {
if (
event.smsType === "SIGN_IN_OR_SIGN_UP" &&
event.additionalUserInfo.phoneNumber.includes('+91')
) {
return {
recaptchaActionOverride: "ALLOW",
};
}
// Allow users to sign in with recaptcha score greater than 0.5
if (event.additionalUserInfo.recaptchaScore > 0.5) {
return {
recaptchaActionOverride: 'ALLOW',
};
}
// Block all others.
return {
recaptchaActionOverride: 'BLOCK',
}
});