אם שדרגת ל-Firebase Authentication with Identity Platform, יש לך אפשרות להוסיף אימות רב-שלבי באמצעות SMS לאפליקציית האינטרנט.
אימות רב-שלבי מגביר את אבטחת האפליקציה. בזמן שתוקפים לעיתים קרובות מתפשרים על סיסמאות ועל חשבונות ברשתות חברתיות, וליירט הודעות טקסט לקשה יותר.
לפני שמתחילים
להפעיל לפחות ספק אחד שתומך באימות רב-שלבי. כל ספק תומך ב-MFA, למעט אימות טלפון, אימות אנונימי מרכז המשחקים של Apple.
מפעילים את האזורים שבהם מתכננים להשתמש באימות באמצעות SMS. המדיניות Firebase משתמשת בחסימה מלאה של מדיניות אזורי SMS, עוזר ליצור פרויקטים במצב מאובטח יותר כברירת מחדל.
לוודא שהאפליקציה מאמתת כתובות אימייל של משתמשים. MFA מחייב אימות אימייל. זה מונע מגורמים זדוניים להירשם לשירות באמצעות אימייל שלא בבעלותם, ואז נועלים את הבעלים האמיתי על ידי הוספת נכס בשקלול.
שימוש בריבוי דיירים (multi-tenancy)
אם אתם מפעילים אימות רב-שלבי לשימוש multi-tenant, צריך לוודא לביצוע השלבים הבאים (בנוסף לשאר הוראות במסמך זה):
במסוף Google Cloud, בוחרים את הדייר שאיתו רוצים לעבוד.
בקוד, מגדירים את השדה
tenantId
במכונהAuth
למזהה של הדייר. לדוגמה:Web
import { getAuth } from "firebase/auth"; const auth = getAuth(app); auth.tenantId = "myTenantId1";
Web
firebase.auth().tenantId = 'myTenantId1';
הפעלת אימות רב-שלבי
פותחים את אימות > שיטת הכניסה במסוף Firebase.
בקטע מתקדם, מפעילים את האפשרות אימות רב-שלבי ב-SMS.
צריך להזין גם את מספרי הטלפון שישמשו לבדיקה של האפליקציה. אמנם לא חובה, אבל מומלץ מאוד לרשום מספרי טלפון לבדיקה להימנע מויסות נתונים (throttle) במהלך הפיתוח.
אם עדיין לא אישרת את הדומיין של האפליקציה, צריך להוסיף אותו להרשאה ברשימה אימות > הגדרות במסוף Firebase.
בחירת דפוס הרישום
תוכלו לבחור אם האפליקציה תדרוש אימות רב-שלבי ואיך ומתי לרשום את המשתמשים. דוגמאות לדפוסים נפוצים:
רישום הגורם השני של המשתמש כחלק מהרישום. שימוש בטיוטה הזו אם האפליקציה שלכם דורשת אימות רב-שלבי לכל המשתמשים.
להציע אפשרות שניתן לדלג עליה כדי לרשום שלב שני במהלך הרישום. קמפיינים לקידום אפליקציות שרוצים לעודד, אך לא לדרוש, אימות רב-שלבי אני רוצה להשתמש בשיטה הזאת.
לספק את היכולת להוסיף שלב שני מהחשבון או מהפרופיל של המשתמש לניהול הדף, במקום במסך ההרשמה. כך ניתן לצמצם את החיכוך במהלך את תהליך הרישום, ועדיין לבצע אימות רב-שלבי שזמינות למשתמשים רגישים מבחינת אבטחה.
דרישה להוסיף גורם שני באופן מצטבר כשהמשתמש רוצה לגשת תכונות עם דרישות אבטחה מוגברות.
הגדרת המאמת של reCAPTCHA
לפני שתוכלו לשלוח קודי SMS, תצטרכו להגדיר מאמת reCAPTCHA. חברת Firebase משתמשת ב-reCAPTCHA כדי למנוע ניצול לרעה על ידי כך שהיא מבטיחה בקשות לאימות מספר טלפון מגיעות מאחד מהדומיינים המורשים של האפליקציה שלך.
אין צורך להגדיר באופן ידני לקוח reCAPTCHA. את ערכת ה-SDK של הלקוח
אובייקט RecaptchaVerifier
יוצר ומאתחל באופן אוטומטי כל דבר נחוץ
וגם סודות של לקוחות.
שימוש ב-reCAPTCHA בלתי נראה
האובייקט RecaptchaVerifier
תומך ב-reCAPTCHA לא נראה, שבדרך כלל יכול לאמת את המשתמש בלי צורך באינטראקציה. כדי להשתמש ב-reCAPTCHA בלתי נראה, יוצרים RecaptchaVerifier
עם הפרמטר size
שמוגדר ל-invisible
, ומציינים את המזהה של רכיב ממשק המשתמש שמתחיל את ההרשמה עם אימות רב-גורמי:
Web
import { RecaptchaVerifier } from "firebase/auth";
const recaptchaVerifier = new RecaptchaVerifier("sign-in-button", {
"size": "invisible",
"callback": function(response) {
// reCAPTCHA solved, you can proceed with
// phoneAuthProvider.verifyPhoneNumber(...).
onSolvedRecaptcha();
}
}, auth);
Web
var recaptchaVerifier = new firebase.auth.RecaptchaVerifier('sign-in-button', {
'size': 'invisible',
'callback': function(response) {
// reCAPTCHA solved, you can proceed with phoneAuthProvider.verifyPhoneNumber(...).
onSolvedRecaptcha();
}
});
שימוש בווידג'ט של reCAPTCHA
כדי להשתמש בווידג'ט גלוי של reCAPTCHA, צריך ליצור רכיב HTML שיכלול
הווידג'ט, ואז יוצרים אובייקט RecaptchaVerifier
עם המזהה של ממשק המשתמש
מאגר תגים. אפשר גם להגדיר קריאות חוזרות (callbacks) שמופעלות כאשר
ה-reCAPTCHA נפתרה או שהתוקף שלו פג:
Web
import { RecaptchaVerifier } from "firebase/auth";
const recaptchaVerifier = new RecaptchaVerifier(
"recaptcha-container",
// Optional reCAPTCHA parameters.
{
"size": "normal",
"callback": function(response) {
// reCAPTCHA solved, you can proceed with
// phoneAuthProvider.verifyPhoneNumber(...).
onSolvedRecaptcha();
},
"expired-callback": function() {
// Response expired. Ask user to solve reCAPTCHA again.
// ...
}
}, auth
);
Web
var recaptchaVerifier = new firebase.auth.RecaptchaVerifier(
'recaptcha-container',
// Optional reCAPTCHA parameters.
{
'size': 'normal',
'callback': function(response) {
// reCAPTCHA solved, you can proceed with phoneAuthProvider.verifyPhoneNumber(...).
// ...
onSolvedRecaptcha();
},
'expired-callback': function() {
// Response expired. Ask user to solve reCAPTCHA again.
// ...
}
});
עיבוד מראש של ה-reCAPTCHA
לחלופין, ניתן לעבד מראש את ה-reCAPTCHA לפני שמתחילים בשני שלבים הרשמה:
Web
recaptchaVerifier.render()
.then(function (widgetId) {
window.recaptchaWidgetId = widgetId;
});
Web
recaptchaVerifier.render()
.then(function(widgetId) {
window.recaptchaWidgetId = widgetId;
});
לאחר תיקון הבעיה render()
, תקבלו את מזהה הווידג'ט של reCAPTCHA, שבו תוכלו להשתמש.
כדי לבצע שיחות
reCAPTCHA API:
var recaptchaResponse = grecaptcha.getResponse(window.recaptchaWidgetId);
באמצעות השיטה verify, RecaptchaVerifier מסיר את הלוגיקה הזו מהקוד, כך שאין צורך לטפל במשתנה grecaptcha
ישירות.
רישום של גורם שני
כדי להירשם לגורם אימות משני חדש למשתמש:
מאמתים מחדש את המשתמש.
מבקשים מהמשתמש להזין את מספר הטלפון שלו.
מפעילים את מאמת reCAPTCHA כפי שמוצג בקטע הקודם. דלגו על השלב הזה אם כבר מוגדר מופע של ReCAPTCHAVerifier:
Web
import { RecaptchaVerifier } from "firebase/auth"; const recaptchaVerifier = new RecaptchaVerifier('recaptcha-container-id', undefined, auth);
Web
var recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container-id');
יצירת סשן רב-שלבי למשתמש:
Web
import { multiFactor } from "firebase/auth"; multiFactor(user).getSession().then(function (multiFactorSession) { // ... });
Web
user.multiFactor.getSession().then(function(multiFactorSession) { // ... })
מאתחלים אובייקט
PhoneInfoOptions
עם מספר הטלפון של המשתמש והסשן עם אימות רב-גורמי:Web
// Specify the phone number and pass the MFA session. const phoneInfoOptions = { phoneNumber: phoneNumber, session: multiFactorSession };
Web
// Specify the phone number and pass the MFA session. var phoneInfoOptions = { phoneNumber: phoneNumber, session: multiFactorSession };
שליחת הודעת אימות לטלפון של המשתמש:
Web
import { PhoneAuthProvider } from "firebase/auth"; const phoneAuthProvider = new PhoneAuthProvider(auth); phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier) .then(function (verificationId) { // verificationId will be needed to complete enrollment. });
Web
var phoneAuthProvider = new firebase.auth.PhoneAuthProvider(); // Send SMS verification code. return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier) .then(function(verificationId) { // verificationId will be needed for enrollment completion. })
אמנם זו לא חובה, אבל מומלץ להודיע למשתמשים מראש על כך הם יקבלו הודעת SMS, ויחולו התעריפים הרגילים.
אם הבקשה נכשלת, צריך לאפס את reCAPTCHA ולאחר מכן לחזור על השלב הקודם כדי שהמשתמש יוכל לנסות שוב. הערה:
verifyPhoneNumber()
תאפס את ה-reCAPTCHA באופן אוטומטי כשהוא יקפיץ הודעת שגיאה, כמו אסימוני reCAPTCHA הם לשימוש חד-פעמי בלבד.Web
recaptchaVerifier.clear();
Web
recaptchaVerifier.clear();
אחרי שליחת קוד ה-SMS, מבקשים מהמשתמש לאמת את הקוד:
Web
// Ask user for the verification code. Then: const cred = PhoneAuthProvider.credential(verificationId, verificationCode);
Web
// Ask user for the verification code. Then: var cred = firebase.auth.PhoneAuthProvider.credential(verificationId, verificationCode);
מאתחלים אובייקט
MultiFactorAssertion
באמצעותPhoneAuthCredential
:Web
import { PhoneMultiFactorGenerator } from "firebase/auth"; const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred);
Web
var multiFactorAssertion = firebase.auth.PhoneMultiFactorGenerator.assertion(cred);
משלימים את תהליך ההרשמה. אפשר גם לציין שם לתצוגה עבור גורם שני. זה שימושי למשתמשים שיש להם הרבה גורמים שמשפיעים על השנייה, במקרה של אנונימיזציה של מספר הטלפון בתהליך האימות (במקרה של לדוגמה, +1******1234).
Web
// Complete enrollment. This will update the underlying tokens // and trigger ID token change listener. multiFactor(user).enroll(multiFactorAssertion, "My personal phone number");
Web
// Complete enrollment. This will update the underlying tokens // and trigger ID token change listener. user.multiFactor.enroll(multiFactorAssertion, 'My personal phone number');
הקוד הבא מציג דוגמה מלאה לרישום של גורם שני:
Web
import {
multiFactor, PhoneAuthProvider, PhoneMultiFactorGenerator,
RecaptchaVerifier
} from "firebase/auth";
const recaptchaVerifier = new RecaptchaVerifier('recaptcha-container-id', undefined, auth);
multiFactor(user).getSession()
.then(function (multiFactorSession) {
// Specify the phone number and pass the MFA session.
const phoneInfoOptions = {
phoneNumber: phoneNumber,
session: multiFactorSession
};
const phoneAuthProvider = new PhoneAuthProvider(auth);
// Send SMS verification code.
return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier);
}).then(function (verificationId) {
// Ask user for the verification code. Then:
const cred = PhoneAuthProvider.credential(verificationId, verificationCode);
const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred);
// Complete enrollment.
return multiFactor(user).enroll(multiFactorAssertion, mfaDisplayName);
});
Web
var recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container-id');
user.multiFactor.getSession().then(function(multiFactorSession) {
// Specify the phone number and pass the MFA session.
var phoneInfoOptions = {
phoneNumber: phoneNumber,
session: multiFactorSession
};
var phoneAuthProvider = new firebase.auth.PhoneAuthProvider();
// Send SMS verification code.
return phoneAuthProvider.verifyPhoneNumber(
phoneInfoOptions, recaptchaVerifier);
})
.then(function(verificationId) {
// Ask user for the verification code.
var cred = firebase.auth.PhoneAuthProvider.credential(verificationId, verificationCode);
var multiFactorAssertion = firebase.auth.PhoneMultiFactorGenerator.assertion(cred);
// Complete enrollment.
return user.multiFactor.enroll(multiFactorAssertion, mfaDisplayName);
});
כל הכבוד! רשמת בהצלחה גורם אימות שני עבור משתמש.
הכנסת משתמשים באמצעות שלב שני
כדי להיכנס למשתמש באמצעות אימות דו-שלבי באמצעות SMS:
מכניסים את המשתמש עם הגורם הראשון, ואז מקבלים את שגיאה אחת (
auth/multi-factor-auth-required
). השגיאה הזו מכילה פותר, רמזים לגבי הגורמים השניים המאומתים וסשן בסיסי שמוכיח שהמשתמש אומת בהצלחה באמצעות הגורם הראשון.לדוגמה, אם הגורם הראשון שגרם למשתמש הוא כתובת אימייל וסיסמה:
Web
import { getAuth, getMultiFactorResolver} from "firebase/auth"; const auth = getAuth(); signInWithEmailAndPassword(auth, email, password) .then(function (userCredential) { // User successfully signed in and is not enrolled with a second factor. }) .catch(function (error) { if (error.code == 'auth/multi-factor-auth-required') { // The user is a multi-factor user. Second factor challenge is required. resolver = getMultiFactorResolver(auth, error); // ... } else if (error.code == 'auth/wrong-password') { // Handle other errors such as wrong password. } });
Web
firebase.auth().signInWithEmailAndPassword(email, password) .then(function(userCredential) { // User successfully signed in and is not enrolled with a second factor. }) .catch(function(error) { if (error.code == 'auth/multi-factor-auth-required') { // The user is a multi-factor user. Second factor challenge is required. resolver = error.resolver; // ... } else if (error.code == 'auth/wrong-password') { // Handle other errors such as wrong password. } ... });
אם הגורם הראשון של המשתמש הוא ספק מאוחד, כמו OAuth, SAML או OIDC, צריך לזהות את השגיאה אחרי הקריאה ל-
signInWithPopup()
או ל-signInWithRedirect()
.אם למשתמש יש כמה גורמים משניים רשומים, שואלים אותו איזה כדי להשתמש:
Web
// Ask user which second factor to use. // You can get the masked phone number via resolver.hints[selectedIndex].phoneNumber // You can get the display name via resolver.hints[selectedIndex].displayName if (resolver.hints[selectedIndex].factorId === PhoneMultiFactorGenerator.FACTOR_ID) { // User selected a phone second factor. // ... } else if (resolver.hints[selectedIndex].factorId === TotpMultiFactorGenerator.FACTOR_ID) { // User selected a TOTP second factor. // ... } else { // Unsupported second factor. }
Web
// Ask user which second factor to use. // You can get the masked phone number via resolver.hints[selectedIndex].phoneNumber // You can get the display name via resolver.hints[selectedIndex].displayName if (resolver.hints[selectedIndex].factorId === firebase.auth.PhoneMultiFactorGenerator.FACTOR_ID) { // User selected a phone second factor. // ... } else if (resolver.hints[selectedIndex].factorId === firebase.auth.TotpMultiFactorGenerator.FACTOR_ID) { // User selected a TOTP second factor. // ... } else { // Unsupported second factor. }
מפעילים את מאמת reCAPTCHA כפי שמוצג בקטע הקודם. דלגו על השלב הזה אם כבר מוגדר מופע של ReCAPTCHAVerifier:
Web
import { RecaptchaVerifier } from "firebase/auth"; recaptchaVerifier = new RecaptchaVerifier('recaptcha-container-id', undefined, auth);
Web
var recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container-id');
אתחול אובייקט
PhoneInfoOptions
עם מספר הטלפון של המשתמש וגם את הסשן הרב-שלבי. הערכים האלה נמצאים בפונקציהresolver
האובייקט הועבר לשגיאהauth/multi-factor-auth-required
:Web
const phoneInfoOptions = { multiFactorHint: resolver.hints[selectedIndex], session: resolver.session };
Web
var phoneInfoOptions = { multiFactorHint: resolver.hints[selectedIndex], session: resolver.session };
שליחת הודעת אימות לטלפון של המשתמש:
Web
// Send SMS verification code. const phoneAuthProvider = new PhoneAuthProvider(auth); phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier) .then(function (verificationId) { // verificationId will be needed for sign-in completion. });
Web
var phoneAuthProvider = new firebase.auth.PhoneAuthProvider(); // Send SMS verification code. return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier) .then(function(verificationId) { // verificationId will be needed for sign-in completion. })
אם הבקשה נכשלת, מאפסים את ה-reCAPTCHA ואז חוזרים על השלב הקודם כדי שהמשתמש יוכל לנסות שוב:
Web
recaptchaVerifier.clear();
Web
recaptchaVerifier.clear();
לאחר שליחת קוד ה-SMS, מבקשים מהמשתמש לאמת את הקוד:
Web
const cred = PhoneAuthProvider.credential(verificationId, verificationCode);
Web
// Ask user for the verification code. Then: var cred = firebase.auth.PhoneAuthProvider.credential(verificationId, verificationCode);
מאתחלים אובייקט
MultiFactorAssertion
באמצעותPhoneAuthCredential
:Web
const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred);
Web
var multiFactorAssertion = firebase.auth.PhoneMultiFactorGenerator.assertion(cred);
צריך להפעיל את
resolver.resolveSignIn()
כדי להשלים את האימות המשני. אחרי זה תוכלו לגשת לתוצאת הכניסה המקורית, שכוללת את נתונים ופרטי כניסה לאימות שספציפיים לספק רגיל:Web
// Complete sign-in. This will also trigger the Auth state listeners. resolver.resolveSignIn(multiFactorAssertion) .then(function (userCredential) { // userCredential will also contain the user, additionalUserInfo, optional // credential (null for email/password) associated with the first factor sign-in. // For example, if the user signed in with Google as a first factor, // userCredential.additionalUserInfo will contain data related to Google // provider that the user signed in with. // - user.credential contains the Google OAuth credential. // - user.credential.accessToken contains the Google OAuth access token. // - user.credential.idToken contains the Google OAuth ID token. });
Web
// Complete sign-in. This will also trigger the Auth state listeners. resolver.resolveSignIn(multiFactorAssertion) .then(function(userCredential) { // userCredential will also contain the user, additionalUserInfo, optional // credential (null for email/password) associated with the first factor sign-in. // For example, if the user signed in with Google as a first factor, // userCredential.additionalUserInfo will contain data related to Google provider that // the user signed in with. // user.credential contains the Google OAuth credential. // user.credential.accessToken contains the Google OAuth access token. // user.credential.idToken contains the Google OAuth ID token. });
הקוד הבא מציג דוגמה מלאה לכניסה לחשבון של משתמש רב-שלבי:
Web
import {
getAuth,
getMultiFactorResolver,
PhoneAuthProvider,
PhoneMultiFactorGenerator,
RecaptchaVerifier,
signInWithEmailAndPassword
} from "firebase/auth";
const recaptchaVerifier = new RecaptchaVerifier('recaptcha-container-id', undefined, auth);
const auth = getAuth();
signInWithEmailAndPassword(auth, email, password)
.then(function (userCredential) {
// User is not enrolled with a second factor and is successfully
// signed in.
// ...
})
.catch(function (error) {
if (error.code == 'auth/multi-factor-auth-required') {
const resolver = getMultiFactorResolver(auth, error);
// Ask user which second factor to use.
if (resolver.hints[selectedIndex].factorId ===
PhoneMultiFactorGenerator.FACTOR_ID) {
const phoneInfoOptions = {
multiFactorHint: resolver.hints[selectedIndex],
session: resolver.session
};
const phoneAuthProvider = new PhoneAuthProvider(auth);
// Send SMS verification code
return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier)
.then(function (verificationId) {
// Ask user for the SMS verification code. Then:
const cred = PhoneAuthProvider.credential(
verificationId, verificationCode);
const multiFactorAssertion =
PhoneMultiFactorGenerator.assertion(cred);
// Complete sign-in.
return resolver.resolveSignIn(multiFactorAssertion)
})
.then(function (userCredential) {
// User successfully signed in with the second factor phone number.
});
} else if (resolver.hints[selectedIndex].factorId ===
TotpMultiFactorGenerator.FACTOR_ID) {
// Handle TOTP MFA.
// ...
} else {
// Unsupported second factor.
}
} else if (error.code == 'auth/wrong-password') {
// Handle other errors such as wrong password.
}
});
Web
var resolver;
firebase.auth().signInWithEmailAndPassword(email, password)
.then(function(userCredential) {
// User is not enrolled with a second factor and is successfully signed in.
// ...
})
.catch(function(error) {
if (error.code == 'auth/multi-factor-auth-required') {
resolver = error.resolver;
// Ask user which second factor to use.
if (resolver.hints[selectedIndex].factorId ===
firebase.auth.PhoneMultiFactorGenerator.FACTOR_ID) {
var phoneInfoOptions = {
multiFactorHint: resolver.hints[selectedIndex],
session: resolver.session
};
var phoneAuthProvider = new firebase.auth.PhoneAuthProvider();
// Send SMS verification code
return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier)
.then(function(verificationId) {
// Ask user for the SMS verification code.
var cred = firebase.auth.PhoneAuthProvider.credential(
verificationId, verificationCode);
var multiFactorAssertion =
firebase.auth.PhoneMultiFactorGenerator.assertion(cred);
// Complete sign-in.
return resolver.resolveSignIn(multiFactorAssertion)
})
.then(function(userCredential) {
// User successfully signed in with the second factor phone number.
});
} else if (resolver.hints[selectedIndex].factorId ===
firebase.auth.TotpMultiFactorGenerator.FACTOR_ID) {
// Handle TOTP MFA.
// ...
} else {
// Unsupported second factor.
}
} else if (error.code == 'auth/wrong-password') {
// Handle other errors such as wrong password.
} ...
});
כל הכבוד! התחברתם בהצלחה למשתמש באמצעות אימות רב-גורמי.
המאמרים הבאים
- ניהול משתמשים רב-גורמי באופן פרוגרמטי באמצעות Admin SDK.