Thêm tính năng xác thực đa yếu tố vào ứng dụng iOS

Nếu đã nâng cấp lên Firebase Authentication with Identity Platform, bạn có thể thêm tính năng xác thực đa yếu tố qua SMS vào ứng dụng iOS của bạn.

Tính năng xác thực đa yếu tố giúp tăng cường độ bảo mật cho ứng dụng của bạn. Trong khi kẻ tấn công thường xâm phạm mật khẩu và tài khoản mạng xã hội, nên việc chặn tin nhắn văn bản là khó hơn.

Trước khi bắt đầu

  1. Bật ít nhất một nhà cung cấp hỗ trợ xác thực đa yếu tố. Mọi nhà cung cấp đều hỗ trợ tính năng MFA, ngoại trừ tính năng xác thực qua điện thoại, xác thực ẩn danh và Trung tâm trò chơi của Apple.

  2. Đảm bảo rằng ứng dụng của bạn đang xác minh email của người dùng. Tính năng MFA yêu cầu xác minh email. Thao tác này ngăn đối tượng độc hại đăng ký dịch vụ bằng email chúng không sở hữu và sau đó khoá chủ sở hữu thực sự bằng cách thêm một giây .

Bật tính năng xác thực đa yếu tố

  1. Mở trang Xác thực > Phương thức đăng nhập của bảng điều khiển Firebase.

  2. Trong phần Nâng cao, bật Xác thực đa yếu tố SMS.

    Bạn cũng nên nhập số điện thoại dùng để thử nghiệm ứng dụng. Mặc dù không bắt buộc, nhưng bạn nên đăng ký số điện thoại thử nghiệm để tránh việc điều tiết trong quá trình phát triển.

  3. Nếu bạn chưa uỷ quyền miền của ứng dụng, hãy thêm miền đó vào danh sách cho phép trên danh sách Xác thực > Cài đặt của bảng điều khiển Firebase.

Xác minh ứng dụng

Firebase cần xác minh rằng các yêu cầu SMS đến từ . Bạn có thể thực hiện việc này theo 2 cách:

  • Thông báo APN im lặng: Khi bạn đăng nhập người dùng lần đầu tiên, Firebase có thể gửi thông báo đẩy im lặng đến thiết bị. Quy trình xác thực có thể tiếp tục nếu ứng dụng nhận được thông báo. Lưu ý rằng kể từ iOS 8.0, bạn không cần yêu cầu người dùng cho phép đẩy để sử dụng phương thức này.

  • Xác minh reCAPTCHA: Nếu bạn không thể gửi thông báo im lặng (cho ví dụ: do người dùng đã tắt tính năng làm mới trong nền hoặc bạn đang thử nghiệm ứng dụng của bạn trong trình mô phỏng iOS), thì bạn có thể dùng reCAPTCHA. Trong nhiều trường hợp, reCAPTCHA sẽ tự động giải quyết mà không cần người dùng tương tác.

Sử dụng thông báo ở chế độ im lặng

Cách bật thông báo APN để dùng với Firebase:

  1. Trong Xcode, hãy bật thông báo đẩy cho dự án của bạn.

  2. Tải khoá xác thực APN lên bằng Bảng điều khiển Firebase (các thay đổi của bạn) sẽ tự động chuyển sang Google Cloud Firebase). Nếu bạn chưa có khoá xác thực APN, hãy xem Định cấu hình APN bằng FCM để tìm hiểu cách có được huy hiệu này.

    1. Mở Bảng điều khiển của Firebase.

    2. Chuyển đến phần Cài đặt dự án.

    3. Chọn thẻ Gửi thông báo qua đám mây.

    4. Trong mục khoá xác thực AAP, ở phần Cấu hình ứng dụng iOS hãy nhấp vào Tải lên.

    5. Chọn khoá của bạn.

    6. Thêm mã khoá cho khoá. Bạn có thể tìm thấy mã khoá trong Chứng chỉ, giá trị nhận dạng và Hồ sơ trong Trung tâm thành viên dành cho nhà phát triển của Apple.

    7. Nhấp vào Tải lên.

Nếu đã có chứng chỉ APN, bạn có thể tải chứng chỉ lên.

Sử dụng phương thức xác minh bằng reCAPTCHA

Cách bật SDK ứng dụng khách sử dụng reCAPTCHA:

  1. Mở cấu hình dự án của bạn trong Xcode.

  2. Nhấp đúp vào tên dự án trong chế độ xem dạng cây bên trái.

  3. Chọn ứng dụng của bạn trong phần Targets (Mục tiêu).

  4. Chọn thẻ Thông tin.

  5. Mở rộng mục Loại URL.

  6. Nhấp vào nút +.

  7. Nhập mã ứng dụng khách bị đảo ngược vào trường Lược đồ URL. Bạn có thể tìm thấy giá trị này được liệt kê trong tệp cấu hình GoogleService-Info.plist dưới dạng REVERSED_CLIENT_ID

Sau khi hoàn tất, cấu hình của bạn sẽ có dạng như sau:

Giao thức tuỳ chỉnh

Nếu muốn, bạn có thể tuỳ chỉnh cách ứng dụng trình bày SFSafariViewController hoặc UIWebView khi hiển thị reCAPTCHA. Việc cần làm thao tác này, hãy tạo một lớp tuỳ chỉnh phù hợp với giao thức FIRAuthUIDelegate, rồi truyền đến verifyPhoneNumber:UIDelegate:completion:.

Chọn một mẫu đăng ký

Bạn có thể chọn xem ứng dụng của bạn có cần xác thực đa yếu tố hay không, cũng như và thời điểm đăng ký người dùng. Một số mẫu phổ biến bao gồm:

  • Đăng ký yếu tố thứ hai của người dùng trong quá trình đăng ký. Sử dụng bản thảo này nếu ứng dụng của bạn yêu cầu xác thực đa yếu tố đối với tất cả người dùng. Xin lưu ý rằng tài khoản phải có địa chỉ email đã xác minh để có thể đăng ký gói thuê bao thứ hai nên quy trình đăng ký của bạn sẽ phải thích ứng với điều này.

  • Cung cấp lựa chọn có thể bỏ qua để đăng ký yếu tố thứ hai trong quá trình đăng ký. Chiến dịch Quảng cáo ứng dụng muốn khuyến khích nhưng không bắt buộc, xác thực đa yếu tố có thể thích phương pháp này hơn.

  • Cho phép thêm yếu tố thứ hai từ tài khoản hoặc hồ sơ của người dùng thay vì màn hình đăng ký. Việc này giúp giảm thiểu rào cản trong quá trình đăng ký, trong khi vẫn thực hiện xác thực đa yếu tố dành cho người dùng nhạy cảm về bảo mật.

  • Yêu cầu thêm dần yếu tố thứ hai khi người dùng muốn truy cập có yêu cầu bảo mật cao hơn.

Đăng ký yếu tố thứ hai

Cách đăng ký một yếu tố phụ mới cho người dùng:

  1. Xác thực lại người dùng.

  2. Yêu cầu người dùng nhập số điện thoại của họ.

  3. Nhận một phiên đa yếu tố cho người dùng:

    Swift

    authResult.user.multiFactor.getSessionWithCompletion() { (session, error) in
      // ...
    }
    

    Objective-C

    [authResult.user.multiFactor
      getSessionWithCompletion:^(FIRMultiFactorSession * _Nullable session,
                                NSError * _Nullable error) {
        // ...
    }];
    
  4. Gửi tin nhắn xác minh đến điện thoại của người dùng. Đảm bảo số điện thoại là được định dạng bằng dấu + ở đầu và không có dấu câu hay khoảng trắng nào khác (cho ví dụ: +15105551234)

    Swift

    // Send SMS verification code.
    PhoneAuthProvider.provider().verifyPhoneNumber(
      phoneNumber,
      uiDelegate: nil,
      multiFactorSession: session) { (verificationId, error) in
        // verificationId will be needed for enrollment completion.
    }
    

    Objective-C

    // 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.
    }];
    

    Mặc dù không bắt buộc nhưng tốt nhất bạn nên thông báo trước cho người dùng rằng họ sẽ nhận được tin nhắn SMS và có áp dụng cước phí tiêu chuẩn.

    Phương thức verifyPhoneNumber() bắt đầu quy trình xác minh ứng dụng trong ở chế độ nền bằng cách sử dụng thông báo đẩy im lặng. Nếu thông báo đẩy im lặng được không có, thay vào đó, hệ thống sẽ đưa ra một thử thách reCAPTCHA.

  5. Sau khi gửi mã qua SMS, hãy yêu cầu người dùng xác minh mã. Sau đó, hãy sử dụng để tạo PhoneAuthCredential:

    Swift

    // Ask user for the verification code. Then:
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId,
      verificationCode: verificationCode)
    

    Objective-C

    // Ask user for the SMS verification code. Then:
    FIRPhoneAuthCredential *credential = [FIRPhoneAuthProvider.provider
                                           credentialWithVerificationID:verificationID
                                           verificationCode:kPhoneSecondFactorVerificationCode];
    
  6. Khởi động một đối tượng câu nhận định:

    Swift

    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    

    Objective-C

    FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
    
  7. Hoàn tất quá trình đăng ký. Nếu muốn, bạn có thể chỉ định tên hiển thị cho yếu tố thứ hai. Điều này hữu ích đối với những người dùng có nhiều yếu tố thứ hai, vì số điện thoại được che giấu trong quy trình xác thực (ví dụ: ví dụ: +1******1234).

    Swift

    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    user.multiFactor.enroll(with: assertion, displayName: displayName) { (error) in
      // ...
    }
    

    Objective-C

    // 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) {
        // ...
    }];
    

Đoạn mã dưới đây trình bày ví dụ hoàn chỉnh về cách đăng ký yếu tố thứ hai:

Swift

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
      // ...
    }
  }
})

Objective-C

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) {
            // ...
        }];
    }];
}];

Xin chúc mừng! Bạn đã đăng ký thành công yếu tố xác thực thứ hai cho một người dùng.

Đăng nhập người dùng bằng yếu tố thứ hai

Cách đăng nhập người dùng bằng tính năng xác minh hai yếu tố qua SMS:

  1. Đăng nhập người dùng bằng yếu tố đầu tiên, sau đó phát hiện lỗi cho biết bắt buộc phải xác thực đa yếu tố. Lỗi này chứa trình phân giải, gợi ý về các yếu tố thứ hai đã đăng ký và phiên cơ bản chứng minh người dùng được xác thực thành công bằng yếu tố đầu tiên.

    Ví dụ: nếu yếu tố đầu tiên của người dùng là email và mật khẩu:

    Swift

    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.
      }
    }
    

    Objective-C

    [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.
        }
    }];
    

    Nếu yếu tố đầu tiên của người dùng là một nhà cung cấp đã liên kết, chẳng hạn như OAuth, hãy nắm bắt lỗi sau khi gọi getCredentialWith().

  2. Nếu người dùng đã đăng ký nhiều yếu tố phụ, hãy hỏi họ xem yếu tố nào để sử dụng. Bạn có thể lấy số điện thoại được che giấu bằng resolver.hints[selectedIndex].phoneNumber và tên hiển thị có resolver.hints[selectedIndex].displayName

    Swift

    // 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.
    }
    

    Objective-C

    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.
    }
    
  3. Gửi tin nhắn xác minh đến điện thoại của người dùng:

    Swift

    // 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.
    }
    

    Objective-C

    // 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.
        }
    }];
    
  4. Sau khi gửi mã qua SMS, hãy yêu cầu người dùng xác minh mã và sử dụng mã đó để tạo một PhoneAuthCredential:

    Swift

    // Ask user for the verification code. Then:
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId!,
      verificationCode: verificationCodeFromUser)
    

    Objective-C

    // Ask user for the SMS verification code. Then:
    FIRPhoneAuthCredential *credential =
        [FIRPhoneAuthProvider.provider
          credentialWithVerificationID:verificationID
                      verificationCode:verificationCodeFromUser];
    
  5. Khởi động đối tượng xác nhận bằng thông tin xác thực:

    Swift

    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    

    Objective-C

    FIRMultiFactorAssertion *assertion =
        [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
    
  6. Xử lý quy trình đăng nhập. Sau đó, bạn có thể truy cập vào kết quả đăng nhập ban đầu bao gồm dữ liệu tiêu chuẩn dành riêng cho nhà cung cấp và thông tin xác thực:

    Swift

    // 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.
    }
    

    Objective-C

    // 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.
        }
    }];
    

Đoạn mã dưới đây cho thấy một ví dụ hoàn chỉnh về việc đăng nhập vào tài khoản người dùng đa yếu tố:

Swift

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.
        }
      }
    }
  }
}

Objective-C

[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.
                }
            }];
        }];
    }
}];

Xin chúc mừng! Bạn đã đăng nhập thành công vào một người dùng bằng tính năng đa yếu tố xác thực.

Bước tiếp theo