אם שדרגת ל-Firebase Authentication with Identity Platform, יש לך אפשרות להוסיף אימות רב-שלבי באמצעות SMS לאפליקציה ל-iOS.
אימות רב-שלבי מגביר את אבטחת האפליקציה. בזמן שתוקפים לעיתים קרובות מתפשרים על סיסמאות ועל חשבונות ברשתות חברתיות, וליירט הודעות טקסט עוד יותר קשה.
לפני שמתחילים
להפעיל לפחות ספק אחד שתומך באימות רב-שלבי. כל ספק תומך ב-MFA, למעט אימות טלפון, אימות אנונימי מרכז המשחקים של Apple.
מוודאים שהאפליקציה מאמתת את כתובות האימייל של המשתמשים. כדי להשתמש בשיטת אימות דו-שלבי, צריך לאמת את כתובת האימייל. כך אפשר למנוע מגורמים זדוניים להירשם לשירות באמצעות כתובת אימייל שאינה בבעלות שלהם, ולאחר מכן לנעול את הבעלים האמיתי על ידי הוספת גורם אימות שני.
הפעלת אימות רב-גורמי
פותחים את אימות > שיטת הכניסה במסוף Firebase.
בקטע מתקדם, מפעילים את האפשרות אימות רב-שלבי ב-SMS.
צריך להזין גם את מספרי הטלפון שישמשו לבדיקה של האפליקציה. אמנם לא חובה, אבל מומלץ מאוד לרשום מספרי טלפון לבדיקה להימנע מויסות נתונים (throttle) במהלך הפיתוח.
אם עדיין לא אישרת את הדומיין של האפליקציה, צריך להוסיף אותו להרשאה ברשימה אימות > הגדרות במסוף Firebase.
אימות האפליקציה
Firebase צריך לאמת שבקשות SMS מגיעות מאת אפליקציה. אפשר לעשות זאת בשתי דרכים:
התראות APN שקטות: כשנכנסים למשתמש בפעם הראשונה, Firebase יכול לשלוח התראה שקטה למכשיר של המשתמש במכשיר. האימות יכול להמשיך אם האפליקציה מקבלת את ההתראה. לתשומת ליבכם: החל מגרסה iOS 8.0, אין צורך לבקש מהמשתמש לאפשר דחיפה התראות כדי להשתמש בשיטה הזו.
אימות מסוג reCAPTCHA: אם אי אפשר לשלוח התראה שקטה (למשל, כי המשתמש השבית את הרענון ברקע או שאתם בודקים את האפליקציה בסימולטור של iOS), אפשר להשתמש ב-reCAPTCHA. במקרים רבים, reCAPTCHA יפתור את עצמו באופן אוטומטי ללא אינטראקציה של המשתמש.
שימוש בהתראות שקטות
כדי להפעיל התראות APN לשימוש עם Firebase:
ב-Xcode, מפעילים את ההתראות לפרויקט שלך.
העלאת מפתח האימות של נקודות ה-APN באמצעות מסוף Firebase (השינויים יועבר אוטומטית אל Google Cloud Firebase). אם עדיין אין לכם מפתח אימות של נקודות APN, יש לעיין במאמר הגדרת נקודות APN באמצעות FCM ללמוד איך לקבל אותו.
פותחים את מסוף Firebase.
עוברים אל Project Settings (הגדרות הפרויקט).
בוחרים את הכרטיסייה העברת הודעות בענן.
בקטע APNs authentication key בקטע iOS app configuration, לוחצים על Upload.
בוחרים את המפתח.
מוסיפים את מזהה המפתח. מזהה המפתח מופיע בקטע Certificates, Identifiers & Profiles בApple Developer Member Center.
לוחצים על Upload (העלאה).
אם כבר יש לכם אישור APNs, תוכלו להעלות את האישור במקום זאת.
שימוש באימות reCAPTCHA
כדי לאפשר ל-Client SDK להשתמש ב-reCAPTCHA:
פותחים את הגדרות הפרויקט ב-Xcode.
לוחצים לחיצה כפולה על שם הפרויקט בתצוגת העץ השמאלית.
בוחרים את האפליקציה בקטע יעדים.
בוחרים בכרטיסייה מידע.
מרחיבים את הקטע סוגי כתובות URL.
לוחצים על הלחצן +.
מזינים את מזהה הלקוח ההפוך בשדה סכימות של כתובות URL. טיפים נוספים לאופטימיזציה מפורטים הערך הזה שרשום בקובץ התצורה
GoogleService-Info.plist
הואREVERSED_CLIENT_ID
.
בסיום, ההגדרה אמורה להיראות כך:
אפשר גם להתאים אישית את האופן שבו האפליקציה מציגה את SFSafariViewController
או את UIWebView
כשהיא מציגה את reCAPTCHA. לבצע
היא ליצור מחלקה מותאמת אישית שתואמת לפרוטוקול FIRAuthUIDelegate
,
ומעבירים אותו אל verifyPhoneNumber:UIDelegate:completion:
.
בחירת דפוס ההרשמה
תוכלו לבחור אם האפליקציה תדרוש אימות רב-שלבי ואיך ומתי לרשום את המשתמשים. דוגמאות לדפוסים נפוצים:
רישום הגורם השני של המשתמש כחלק מהרישום. כדאי להשתמש בשיטה הזו אם האפליקציה שלכם דורשת אימות רב-שלבי לכל המשתמשים. חשוב לשים לב: כדי לרשום חשבון שני, צריכה להיות לחשבון גם כתובת אימייל מאומתת לכן תהליך הרישום שלך יצטרך להביא זאת בחשבון.
להציע אפשרות שניתן לדלג עליה כדי לרשום שלב שני במהלך הרישום. באפליקציות שרוצות לעודד את השימוש באימות רב-שלבי, אבל לא מחייבות אותו, הגישה הזו עשויה להתאים יותר.
לספק את היכולת להוסיף שלב שני מהחשבון או מהפרופיל של המשתמש לניהול הדף, במקום במסך ההרשמה. כך אפשר לצמצם את החיכוך בתהליך ההרשמה, ועדיין להציע אימות רב-שלבי למשתמשים שמתעניינים באבטחה.
מחייב להוסיף גורם נוסף באופן מצטבר כשהמשתמש רוצה לגשת לתכונות עם דרישות אבטחה מחמירות יותר.
הוספת שלב שני
כדי לרשום גורם משני חדש עבור משתמש:
מבצעים אימות מחדש של המשתמש.
מבקשים מהמשתמש להזין את מספר הטלפון שלו.
יצירת סשן רב-שלבי למשתמש:
authResult.user.multiFactor.getSessionWithCompletion() { (session, error) in // ... }
[authResult.user.multiFactor getSessionWithCompletion:^(FIRMultiFactorSession * _Nullable session, NSError * _Nullable error) { // ... }];
שולחים הודעת אימות לטלפון של המשתמש. חשוב לוודא שמספר הטלפון בפורמט עם
+
בתחילתו, ללא סימני פיסוק או רווחים אחרים (לדוגמה:+15105551234
)// Send SMS verification code. PhoneAuthProvider.provider().verifyPhoneNumber( phoneNumber, uiDelegate: nil, multiFactorSession: session) { (verificationId, error) in // verificationId will be needed for enrollment completion. }
// Send SMS verification code. [FIRPhoneAuthProvider.provider verifyPhoneNumber:phoneNumber UIDelegate:nil multiFactorSession:session completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) { // verificationId will be needed for enrollment completion. }];
אין חובה לעשות זאת, אבל מומלץ להודיע למשתמשים מראש שהם יקבלו הודעת SMS ושחלות עליהם התעריפים הרגילים.
השיטה
verifyPhoneNumber()
מתחילה את תהליך אימות האפליקציות בשלב ברקע באמצעות התראה שקטה. אם התראת דחיפה שקטה לא זמינה, במקום זאת תופיע בקשה לפתרון אתגר reCAPTCHA.אחרי שליחת קוד ה-SMS, מבקשים מהמשתמש לאמת את הקוד. אחר כך משתמשים תגובה לפיתוח
PhoneAuthCredential
:// Ask user for the verification code. Then: let credential = PhoneAuthProvider.provider().credential( withVerificationID: verificationId, verificationCode: verificationCode)
// Ask user for the SMS verification code. Then: FIRPhoneAuthCredential *credential = [FIRPhoneAuthProvider.provider credentialWithVerificationID:verificationID verificationCode:kPhoneSecondFactorVerificationCode];
מאתחלים אובייקט טענת נכוֹנוּת (assertion):
let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
משלימים את תהליך ההרשמה. אפשר גם לציין שם לתצוגה עבור גורם שני. זה שימושי למשתמשים שיש להם הרבה גורמים שמשפיעים על השנייה, במקרה של אנונימיזציה של מספר הטלפון בתהליך האימות (במקרה של לדוגמה, +1******1234).
// Complete enrollment. This will update the underlying tokens // and trigger ID token change listener. user.multiFactor.enroll(with: assertion, displayName: displayName) { (error) in // ... }
// Complete enrollment. This will update the underlying tokens // and trigger ID token change listener. [authResult.user.multiFactor enrollWithAssertion:assertion displayName:nil completion:^(NSError * _Nullable error) { // ... }];
הקוד הבא מציג דוגמה מלאה לרישום של גורם שני:
let user = Auth.auth().currentUser
user?.multiFactor.getSessionWithCompletion({ (session, error) in
// Send SMS verification code.
PhoneAuthProvider.provider().verifyPhoneNumber(
phoneNumber,
uiDelegate: nil,
multiFactorSession: session
) { (verificationId, error) in
// verificationId will be needed for enrollment completion.
// Ask user for the verification code.
let credential = PhoneAuthProvider.provider().credential(
withVerificationID: verificationId!,
verificationCode: phoneSecondFactorVerificationCode)
let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
// Complete enrollment. This will update the underlying tokens
// and trigger ID token change listener.
user?.multiFactor.enroll(with: assertion, displayName: displayName) { (error) in
// ...
}
}
})
FIRUser *user = FIRAuth.auth.currentUser;
[user.multiFactor getSessionWithCompletion:^(FIRMultiFactorSession * _Nullable session,
NSError * _Nullable error) {
// Send SMS verification code.
[FIRPhoneAuthProvider.provider
verifyPhoneNumber:phoneNumber
UIDelegate:nil
multiFactorSession:session
completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
// verificationId will be needed for enrollment completion.
// Ask user for the verification code.
// ...
// Then:
FIRPhoneAuthCredential *credential =
[FIRPhoneAuthProvider.provider credentialWithVerificationID:verificationID
verificationCode:kPhoneSecondFactorVerificationCode];
FIRMultiFactorAssertion *assertion =
[FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
// Complete enrollment. This will update the underlying tokens
// and trigger ID token change listener.
[user.multiFactor enrollWithAssertion:assertion
displayName:displayName
completion:^(NSError * _Nullable error) {
// ...
}];
}];
}];
כל הכבוד! רשמת בהצלחה גורם אימות שני עבור משתמש.
הכנסת משתמשים באמצעות שלב שני
כדי להיכנס משתמש באמצעות אימות דו-שלבי באמצעות SMS:
מכניסים את המשתמש באמצעות הגורם הראשון, ואז מזהים שגיאה נדרש אימות רב-שלבי. השגיאה הזו מכילה מקודד, רמזים לגבי הגורמים המשניים הרשומים, וסשן בסיסי הוכחה לכך שהמשתמש אומת בהצלחה באמצעות הגורם הראשון.
לדוגמה, אם הגורם הראשון שגרם למשתמש הוא כתובת אימייל וסיסמה:
Auth.auth().signIn( withEmail: email, password: password ) { (result, error) in let authError = error as NSError if authError?.code == AuthErrorCode.secondFactorRequired.rawValue { // The user is a multi-factor user. Second factor challenge is required. let resolver = authError!.userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver // ... } else { // Handle other errors such as wrong password. } }
[FIRAuth.auth signInWithEmail:email password:password completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) { if (error == nil || error.code != FIRAuthErrorCodeSecondFactorRequired) { // User is not enrolled with a second factor and is successfully signed in. // ... } else { // The user is a multi-factor user. Second factor challenge is required. } }];
אם הגורם הראשון של המשתמש הוא ספק מאוחד, כמו OAuth, צריך לתפוס את השגיאה אחרי הקריאה ל-
getCredentialWith()
.אם למשתמש יש כמה גורמים משניים רשומים, שואלים אותו איזה לשימוש. אפשר לראות את מספר הטלפון מוסתר עם
resolver.hints[selectedIndex].phoneNumber
והשם המוצג עםresolver.hints[selectedIndex].displayName
.// Ask user which second factor to use. Then: if resolver.hints[selectedIndex].factorID == PhoneMultiFactorID { // User selected a phone second factor. // ... } else if resolver.hints[selectedIndex].factorID == TotpMultiFactorID { // User selected a TOTP second factor. // ... } else { // Unsupported second factor. }
FIRMultiFactorResolver *resolver = (FIRMultiFactorResolver *) error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey]; // Ask user which second factor to use. Then: FIRPhoneMultiFactorInfo *hint = (FIRPhoneMultiFactorInfo *) resolver.hints[selectedIndex]; if (hint.factorID == FIRPhoneMultiFactorID) { // User selected a phone second factor. // ... } else if (hint.factorID == FIRTOTPMultiFactorID) { // User selected a TOTP second factor. // ... } else { // Unsupported second factor. }
שליחת הודעת אימות לטלפון של המשתמש:
// Send SMS verification code. let hint = resolver.hints[selectedIndex] as! PhoneMultiFactorInfo PhoneAuthProvider.provider().verifyPhoneNumber( with: hint, uiDelegate: nil, multiFactorSession: resolver.session ) { (verificationId, error) in // verificationId will be needed for sign-in completion. }
// Send SMS verification code [FIRPhoneAuthProvider.provider verifyPhoneNumberWithMultiFactorInfo:hint UIDelegate:nil multiFactorSession:resolver.session completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) { if (error != nil) { // Failed to verify phone number. } }];
לאחר שליחת קוד ה-SMS, בקשו מהמשתמש לאמת את הקוד ולהשתמש בו כדי יצירה של
PhoneAuthCredential
:// Ask user for the verification code. Then: let credential = PhoneAuthProvider.provider().credential( withVerificationID: verificationId!, verificationCode: verificationCodeFromUser)
// Ask user for the SMS verification code. Then: FIRPhoneAuthCredential *credential = [FIRPhoneAuthProvider.provider credentialWithVerificationID:verificationID verificationCode:verificationCodeFromUser];
מאתחלים אובייקט טענת נכוֹנוּת עם פרטי הכניסה:
let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
מתקנים את תהליך הכניסה. אחרי זה תוכלו לגשת לתוצאת הכניסה המקורית. הדוח כולל את הנתונים הסטנדרטיים של ספק מסוים ופרטי כניסה לאימות:
// Complete sign-in. This will also trigger the Auth state listeners. resolver.resolveSignIn(with: assertion) { (authResult, error) in // authResult 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, // authResult.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. }
// Complete sign-in. [resolver resolveSignInWithAssertion:assertion completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) { if (error != nil) { // User successfully signed in with the second factor phone number. } }];
הקוד הבא מציג דוגמה מלאה לכניסה לחשבון של משתמש רב-שלבי:
Auth.auth().signIn(
withEmail: email,
password: password
) { (result, error) in
let authError = error as NSError?
if authError?.code == AuthErrorCode.secondFactorRequired.rawValue {
let resolver =
authError!.userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver
// Ask user which second factor to use.
// ...
// Then:
let hint = resolver.hints[selectedIndex] as! PhoneMultiFactorInfo
// Send SMS verification code
PhoneAuthProvider.provider().verifyPhoneNumber(
with: hint,
uiDelegate: nil,
multiFactorSession: resolver.session
) { (verificationId, error) in
if error != nil {
// Failed to verify phone number.
}
// Ask user for the SMS verification code.
// ...
// Then:
let credential = PhoneAuthProvider.provider().credential(
withVerificationID: verificationId!,
verificationCode: verificationCodeFromUser)
let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
// Complete sign-in.
resolver.resolveSignIn(with: assertion) { (authResult, error) in
if error != nil {
// User successfully signed in with the second factor phone number.
}
}
}
}
}
[FIRAuth.auth signInWithEmail:email
password:password
completion:^(FIRAuthDataResult * _Nullable authResult,
NSError * _Nullable error) {
if (error == nil || error.code != FIRAuthErrorCodeSecondFactorRequired) {
// User is not enrolled with a second factor and is successfully signed in.
// ...
} else {
FIRMultiFactorResolver *resolver =
(FIRMultiFactorResolver *) error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey];
// Ask user which second factor to use.
// ...
// Then:
FIRPhoneMultiFactorInfo *hint = (FIRPhoneMultiFactorInfo *) resolver.hints[selectedIndex];
// Send SMS verification code
[FIRPhoneAuthProvider.provider
verifyPhoneNumberWithMultiFactorInfo:hint
UIDelegate:nil
multiFactorSession:resolver.session
completion:^(NSString * _Nullable verificationID,
NSError * _Nullable error) {
if (error != nil) {
// Failed to verify phone number.
}
// Ask user for the SMS verification code.
// ...
// Then:
FIRPhoneAuthCredential *credential =
[FIRPhoneAuthProvider.provider
credentialWithVerificationID:verificationID
verificationCode:kPhoneSecondFactorVerificationCode];
FIRMultiFactorAssertion *assertion =
[FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
// Complete sign-in.
[resolver resolveSignInWithAssertion:assertion
completion:^(FIRAuthDataResult * _Nullable authResult,
NSError * _Nullable error) {
if (error != nil) {
// User successfully signed in with the second factor phone number.
}
}];
}];
}
}];
כל הכבוד! בוצעה כניסה של משתמש באמצעות כמה גורמים אימות.
המאמרים הבאים
- ניהול משתמשים רב-גורמי באופן פרוגרמטי באמצעות Admin SDK.