הוספת אימות רב-גורמי מסוג TOTP לאפליקציה ל-iOS

אם שדרגתם ל-Firebase Authentication with Identity Platform, אתם יכולים להוסיף לאפליקציה אימות רב-שלבי (MFA) באמצעות סיסמה חד-פעמית מבוססת-זמן (TOTP).

‫Firebase Authentication with Identity Platform מאפשר להשתמש ב-TOTP כגורם נוסף לאימות רב-שלבי (MFA). כשמפעילים את התכונה הזו, משתמשים שמנסים להיכנס לאפליקציה רואים בקשה להזין TOTP. כדי ליצור את הקוד, הם צריכים להשתמש באפליקציה לאימות חשבונות שיכולה ליצור קודי TOTP תקינים, כמו מאמת החשבונות של Google.

לפני שמתחילים

  1. מפעילים לפחות ספק אחד שתומך באימות רב-שלבי. שימו לב שכל הספקים תומכים באימות רב-שלבי, חוץ מהספקים הבאים:

    • אימות באמצעות הטלפון
    • אימות אנונימי
    • טוקנים מותאמים אישית לאימות
    • Apple Game Center
  2. מוודאים שהאפליקציה מאמתת את כתובות האימייל של המשתמשים. MFA דורש אימות אימייל. כך גורמים זדוניים לא יכולים להירשם לשירות באמצעות כתובת אימייל שלא שייכת להם, ואז לנעול את הבעלים האמיתיים של כתובת האימייל על ידי הוספת גורם שני.

  3. אם עדיין לא עשיתם זאת, התקינו את Firebase Apple SDK.

    אימות TOTP MFA נתמך רק ב-Apple SDK מגרסה 10.12.0 ואילך, ורק ב-iOS.

הפעלת אימות TOTP MFA

כדי להפעיל את TOTP כגורם אימות שני, משתמשים ב-Admin SDK או שולחים קריאה לנקודת הקצה של REST להגדרת הפרויקט.

כדי להשתמש ב-Admin SDK:

  1. אם עדיין לא עשיתם זאת, התקינו את Firebase Admin Node.js SDK.

    אימות TOTP MFA נתמך רק ב-Firebase Admin Node.js SDK בגרסה 11.6.0 ואילך.

  2. מריצים את הפקודה הבאה:

    import { getAuth } from 'firebase-admin/auth';
    
    getAuth().projectConfigManager().updateProjectConfig(
    {
          multiFactorConfig: {
              providerConfigs: [{
                  state: "ENABLED",
                  totpProviderConfig: {
                      adjacentIntervals: NUM_ADJ_INTERVALS
                  }
              }]
          }
    })
    

    מחליפים את מה שכתוב בשדות הבאים:

    • ‫NUM_ADJ_INTERVALS: מספר המרווחים הסמוכים של חלונות הזמן שמהם אפשר לקבל סיסמאות TOTP, מאפס עד עשר. ערך ברירת המחדל הוא 5.

      כדי ש-TOTP יפעל, צריך לוודא ששני הצדדים (המאמת והמאומת) יוצרים סיסמאות חד-פעמיות באותו חלון זמן (בדרך כלל 30 שניות), כך שהם יוצרים את אותה סיסמה. עם זאת, כדי להתאים את עצמכם לסטיית השעון בין הצדדים ולזמן התגובה של המשתמשים, אתם יכולים להגדיר את שירות ה-TOTP כך שיקבל גם TOTP מחלונות סמוכים.

כדי להפעיל אימות TOTP MFA באמצעות API בארכיטקטורת REST, מריצים את הפקודה הבאה:

curl -X PATCH "https://identitytoolkit.googleapis.com/admin/v2/projects/PROJECT_ID/config?updateMask=mfa" \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    -H "Content-Type: application/json" \
    -H "X-Goog-User-Project: PROJECT_ID" \
    -d \
    '{
        "mfa": {
          "providerConfigs": [{
            "state": "ENABLED",
            "totpProviderConfig": {
              "adjacentIntervals": NUM_ADJ_INTERVALS
            }
          }]
       }
    }'

מחליפים את מה שכתוב בשדות הבאים:

  • ‫PROJECT_ID: מזהה הפרויקט.
  • NUM_ADJ_INTERVALS: מספר המרווחים של חלונות הזמן, מאפס עד עשר. ברירת המחדל היא חמש.

    כדי ש-TOTP יפעל, צריך לוודא ששני הצדדים (המאמת והמאומת) יוצרים סיסמאות חד-פעמיות באותו חלון זמן (בדרך כלל 30 שניות), כך שהם יוצרים את אותה סיסמה. עם זאת, כדי להתאים את עצמכם לסטיית השעון בין הצדדים ולזמן התגובה של המשתמשים, אתם יכולים להגדיר את שירות ה-TOTP כך שיקבל גם TOTP מחלונות סמוכים.

בחירת דפוס הרשמה

אתם יכולים לבחור אם האפליקציה תדרוש אימות רב-שלבי, ואיך ומתי לרשום את המשתמשים. דוגמאות לדפוסים נפוצים:

  • רושמים את הגורם השני של המשתמש כחלק מההרשמה. כדאי להשתמש בשיטה הזו אם האפליקציה דורשת אימות רב-שלבי לכל המשתמשים.

  • להציע אפשרות שניתן לדלג עליה כדי לרשום גורם שני במהלך ההרשמה. אם אתם רוצים לעודד אימות רב-שלבי באפליקציה, אבל לא לחייב אותו, אתם יכולים להשתמש בגישה הזו.

  • אפשרו להוסיף גורם שני מדף ניהול החשבון או הפרופיל של המשתמש, במקום ממסך ההרשמה. כך אפשר לצמצם את החיכוך במהלך תהליך הרישום, ועדיין להציע אימות רב-שלבי למשתמשים שרגישים לאבטחה.

  • לדרוש הוספה של גורם אימות שני באופן הדרגתי כשהמשתמש רוצה לגשת לתכונות עם דרישות אבטחה מחמירות יותר.

רישום משתמשים לאימות רב-שלבי (MFA) באמצעות סיסמה חד-פעמית מבוססת-זמן (TOTP)

אחרי שמפעילים אימות TOTP כגורם אימות שני באפליקציה, מטמיעים לוגיקה בצד הלקוח כדי לרשום משתמשים לאימות TOTP:

  1. לאמת מחדש את המשתמש.

  2. יוצרים סוד TOTP למשתמש המאומת:

    // Generate a TOTP secret.
    guard let mfaSession = try? await currentUser.multiFactor.session() else { return }
    guard let totpSecret = try? await TOTPMultiFactorGenerator.generateSecret(with: mfaSession) else { return }
    
    // Display the secret to the user and prompt them to enter it into their
    // authenticator app. (See the next step.)
    
  3. מציגים את הסוד למשתמש ומבקשים ממנו להזין אותו באפליקציה לאימות חשבונות:

    // Display this key:
    let secret = totpSecret.sharedSecretKey()
    

    בנוסף להצגת המפתח הסודי, אתם יכולים לנסות להוסיף אותו באופן אוטומטי לאפליקציית מאמת החשבונות שמוגדרת כברירת מחדל במכשיר. כדי לעשות את זה, יוצרים URI של מפתח שתואם למאמת החשבונות של Google ומעבירים אותו אל openInOTPApp(withQRCodeURL:):

    let otpAuthUri = totpSecret.generateQRCodeURL(
        withAccountName: currentUser.email ?? "default account",
        issuer: "Your App Name")
    totpSecret.openInOTPApp(withQRCodeURL: otpAuthUri)
    

    אחרי שהמשתמשים מוסיפים את הסוד לאפליקציה לאימות חשבונות, היא מתחילה ליצור סיסמאות TOTP.

  4. מבקשים מהמשתמש להקליד את ה-TOTP שמוצג באפליקציה לאימות החשבונות, ומשתמשים בו כדי להשלים את ההרשמה ל-MFA:

    // Ask the user for a verification code from the authenticator app.
    let verificationCode = // Code from user input.
    
    // Finalize the enrollment.
    let multiFactorAssertion = TOTPMultiFactorGenerator.assertionForEnrollment(
        with: totpSecret,
        oneTimePassword: verificationCode)
    do {
        try await currentUser.multiFactor.enroll(
            with: multiFactorAssertion,
            displayName: "TOTP")
    } catch {
        // Wrong or expired OTP. Re-prompt the user.
    }
    

כניסת משתמשים באמצעות גורם אימות שני

כדי לאפשר למשתמשים להיכנס באמצעות TOTP MFA, משתמשים בקוד הבא:

  1. מפעילים את אחת מהשיטות signIn(with...:)- כמו שהייתם עושים אם לא הייתם משתמשים ב-MFA (לדוגמה, signIn(withEmail:password:)). אם השיטה מחזירה שגיאה עם הקוד secondFactorRequired, מתחילים את תהליך ה-MFA של האפליקציה.

    do {
        let authResult = try await Auth.auth().signIn(withEmail: email, password: password)
    
        // If the user is not enrolled with a second factor and provided valid
        // credentials, sign-in succeeds.
    
        // (If your app requires MFA, this could be considered an error
        // condition, which you would resolve by forcing the user to enroll a
        // second factor.)
    
        // ...
    } catch let error as AuthErrorCode where error.code == .secondFactorRequired {
        // Initiate your second factor sign-in flow. (See next step.)
        // ...
    } catch {
        // Other auth error.
        throw error
    }
    
  2. בתהליך ה-MFA באפליקציה, קודם כל צריך לבקש מהמשתמש לבחור את הגורם השני שבו הוא רוצה להשתמש. כדי לקבל רשימה של הגורמים המשניים הנתמכים, בודקים את המאפיין hints של מופע MultiFactorResolver:

    let mfaKey = AuthErrorUserInfoMultiFactorResolverKey
    guard let resolver = error.userInfo[mfaKey] as? MultiFactorResolver else { return }
    let enrolledFactors = resolver.hints.map(\.displayName)
    
  3. אם המשתמש בוחר להשתמש ב-TOTP, מבקשים ממנו להקליד את ה-TOTP שמוצג באפליקציה לאימות חשבונות ולהשתמש בו כדי להיכנס:

    let multiFactorInfo = resolver.hints[selectedIndex]
    switch multiFactorInfo.factorID {
    case TOTPMultiFactorID:
        let otpFromAuthenticator = // OTP typed by the user.
        let assertion = TOTPMultiFactorGenerator.assertionForSignIn(
            withEnrollmentID: multiFactorInfo.uid,
            oneTimePassword: otpFromAuthenticator)
        do {
            let authResult = try await resolver.resolveSignIn(with: assertion)
        } catch {
            // Wrong or expired OTP. Re-prompt the user.
        }
    default:
        return
    }
    

ביטול ההרשמה ל-MFA באמצעות TOTP

בקטע הזה מוסבר איך לטפל במשתמש שמבטל את ההרשמה ל-MFA עם TOTP.

אם משתמש נרשם לכמה אפשרויות של MFA, והוא מבטל את ההרשמה לאפשרות שהופעלה לאחרונה, הוא מקבל auth/user-token-expired והמערכת מוציאה אותו מהחשבון. המשתמש צריך להיכנס שוב ולאמת את פרטי הכניסה הקיימים שלו, למשל כתובת אימייל וסיסמה.

כדי לבטל את ההרשמה של המשתמש, לטפל בשגיאה ולהפעיל אימות מחדש, משתמשים בקוד הבא:

guard let currentUser = Auth.auth().currentUser else { return }

// Prompt the user to select a factor to unenroll, from this array:
currentUser.multiFactor.enrolledFactors

// ...

// Unenroll the second factor.
let multiFactorInfo = currentUser.multiFactor.enrolledFactors[selectedIndex]
do {
    try await currentUser.multiFactor.unenroll(with: multiFactorInfo)
} catch let error as AuthErrorCode where error.code == .invalidUserToken {
    // Second factor unenrolled, but the user was signed out. Re-authenticate
    // them.
}

המאמרים הבאים