פונקציות חסימה מאפשרות להריץ קוד בהתאמה אישית שמשנה את התוצאה של רישום משתמש או כניסה לאפליקציה. לדוגמה, אפשר למנוע ממשתמש לבצע אימות אם הוא לא עומד בקריטריונים מסוימים, או לעדכן את פרטי המשתמש לפני שהם מוחזרים לאפליקציית הלקוח.
לפני שמתחילים
כדי להשתמש בפונקציות חסימה, צריך לשדרג את פרויקט 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 תחילת העבודה). בקצרה:
כותבים פונקציה שמטפלת באירוע המטורגט.
לדוגמה, כדי להתחיל, אתם יכולים להוסיף פונקציה ללא פעולה (no-op) כמו זו לקוד המקור:
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בדוגמה שלמעלה הושמטה ההטמעה של לוגיקת אימות מותאמת אישית. בקטעים הבאים מוסבר איך להטמיע את פונקציות החסימה, ובקטע תרחישים נפוצים מפורטות דוגמאות ספציפיות.
פורסים את הפונקציות באמצעות ה-CLI של Firebase:
firebase deploy --only functionsצריך לפרוס מחדש את הפונקציות בכל פעם שמעדכנים אותן.
קבלת מידע על המשתמש ועל ההקשר
אירועי חסימה מספקים אובייקט AuthBlockingEvent שמכיל מידע על המשתמש שנכנס לחשבון. אפשר להשתמש בערכים האלה בקוד כדי לקבוע אם לאפשר לפעולה להמשיך.
האובייקט מכיל את המאפיינים הבאים:
| שם | תיאור | דוגמה |
|---|---|---|
locale |
הלוקאל של האפליקציה. אפשר להגדיר את הלוקאל באמצעות ה-SDK של הלקוח, או על ידי העברת הכותרת של הלוקאל ב-API בארכיטקטורת REST. | 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
| הפרויקט או הדייר (tenant) ב-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 ומוחזר ללקוח.
כדי לשנות משתמש, מחזירים אובייקט מהגורם המטפל באירועים שמכיל את השדות שרוצים לשנות. אפשר לשנות את השדות הבאים:
displayNamedisabledemailVerifiedphotoURLcustomClaims-
sessionClaims(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 לפונקציות חסימה מספקי זהויות שונים. בטבלה הבאה מפורטים פרטי הכניסה והנתונים שנתמכים בכל ספק זהויות:
| ספק זהות (IdP) | טוקן של מזהה | טוקן גישה | מועד תפוגה | סוד הטוקן | טוקן רענון | הצהרות כניסה |
|---|---|---|---|---|---|---|
| כן | כן | כן | לא | כן | לא | |
| לא | כן | כן | לא | לא | לא | |
| לא | כן | לא | כן | לא | לא | |
| GitHub | לא | כן | לא | לא | לא | לא |
| Microsoft | כן | כן | כן | לא | כן | לא |
| לא | כן | כן | לא | לא | לא | |
| Yahoo | כן | כן | כן | לא | כן | לא |
| Apple | כן | כן | כן | לא | כן | לא |
| SAML | לא | לא | לא | לא | לא | כן |
| OIDC | כן | כן | כן | לא | כן | כן |
טוקנים של OAuth
כדי להשתמש בטוקן של מזהה, בטוקן גישה או בטוקן רענון בפונקציית חסימה, קודם צריך לסמן את תיבת הסימון בדף Blocking functions במסוף Firebase (עוברים אל Security > Authentication > הכרטיסייה Settings).
ספקי זהויות לא יחזירו טוקני רענון כשנכנסים ישירות באמצעות פרטי כניסה של OAuth, כמו טוקן של מזהה או טוקן גישה. במצב כזה, אותם פרטי כניסה של OAuth בצד הלקוח יועברו לפונקציית החסימה.
בקטעים הבאים מתוארים כל סוגי ספקי הזהויות, ופרטי הכניסה והנתונים שהם תומכים בהם.
ספקי OIDC גנריים
כשמשתמש נכנס באמצעות ספק OIDC גנרי, פרטי הכניסה הבאים מועברים:
- טוקן של מזהה: מסופק אם נבחר התהליך
id_token. - טוקן גישה: מסופק אם נבחר תהליך הקוד. שימו לב שכרגע יש תמיכה בתהליך הקוד רק דרך ה-API בארכיטקטורת REST.
- טוקן רענון: מסופק אם נבחר ההיקף
offline_access.
דוגמה:
const provider = new firebase.auth.OAuthProvider('oidc.my-provider');
provider.addScope('offline_access');
firebase.auth().signInWithPopup(provider);
כשמשתמש נכנס באמצעות חשבון Google, פרטי הכניסה הבאים מועברים:
- טוקן של מזהה
- טוקן גישה
- טוקן רענון: מסופק רק אם מבקשים את הפרמטרים המותאמים אישית הבאים:
access_type=offline-
prompt=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
כשמשתמש נכנס באמצעות פייסבוק, פרטי הכניסה הבאים מועברים:
- טוקן גישה: מוחזר טוקן גישה שאפשר להחליף בטוקן גישה אחר. מידע נוסף על סוגי טוקני גישה שונים שנתמכים על ידי פייסבוק, ואיך להחליף אותם באסימונים לטווח ארוך.
GitHub
כשמשתמש נכנס באמצעות GitHub, פרטי הכניסה הבאים מועברים:
- טוקן גישה: לא פג תוקף אלא אם הוא מבוטל.
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, ולהשתמש בו כדי לקרוא לממשקי ה-API של יומן 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',
}
});