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

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

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

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

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

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

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

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

הפעלת אימות דו-שלבי באמצעות TOTP

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

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

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

    יש תמיכה בשיטת האימות הדו-שלבי באמצעות TOTP רק ב-SDK של Firebase Admin עבור Node.js בגרסה 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, מ-0 עד 10. ברירת המחדל היא חמש.

      המפתחות החד-פעמיים מבוססי 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: מספר הפסקות בחלון הזמן, מ-0 עד 10. ערך ברירת המחדל הוא 5.

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

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

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

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

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

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

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

רישום משתמשים ב-TOTP MFA

אחרי שמפעילים את אימות הגורם הנוסף מסוג 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 Authenticator ומעבירים אותו אל 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. קוראים לאחת מה-methods 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. בתהליך האימות הדו-שלבי של האפליקציה, המשתמש צריך לבחור קודם את הגורם השני שבו הוא רוצה להשתמש. כדי לקבל רשימה של גורמים שניים נתמכים, אפשר לבדוק את המאפיין 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
    }
    

ביטול הרשמה ל-TOTP MFA

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

אם משתמש נרשם לכמה אפשרויות של אימות דו-שלבי, והוא מבטל את ההרשמה מהאפשרות שהופעל לאחרונה, הוא יקבל הודעת 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.
}

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