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

Nếu đã nâng cấp lên Xác thực Firebase với Nền tảng nhận dạng, bạn có thể thêm xác thực đa yếu tố SMS vào ứng dụng iOS của mình.

Xác thực đa yếu tố tăng tính bảo mật cho ứng dụng của bạn. Trong khi những 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 thì việc chặn tin nhắn văn bản lại khó khăn hơn.

Trước khi bắt đầu

  1. Kích hoạ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ợ MFA, ngoại trừ xác thực qua điện thoại, xác thực ẩn danh và Apple Game Center.

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

Kích hoạt 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ố qua SMS .

    Bạn cũng nên nhập số điện thoại mà bạn sẽ dùng để thử nghiệm ứng dụng của mình. Mặc dù là tùy chọn nhưng bạn nên đăng ký số điện thoại thử nghiệm để tránh bị hạn chế trong quá trình phát triển.

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

Đang xác minh ứng dụng của bạn

Firebase cần xác minh rằng các yêu cầu SMS đến từ ứng dụng của bạn. Bạn có thể làm điều này theo hai 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 tới thiết bị của người dùng. Quá 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 bắt đầu với iOS 8.0, bạn không cần yêu cầu người dùng cho phép thông báo đẩy sử dụng phương pháp này.

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

Sử dụng thông báo im lặng

Để bật thông báo APN để sử dụng với Firebase:

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

  2. Tải khóa xác thực APN của bạn 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ó khóa xác thực APN, hãy xem Định cấu hình APN bằng FCM để tìm hiểu cách lấy khóa.

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

    2. Điều hướng đến Cài đặt dự án .

    3. Chọn tab Nhắn tin qua đám mây .

    4. Trong khóa xác thực APN , trong phần cấu hình ứng dụng iOS , hãy nhấp vào Tải lên .

    5. Chọn chìa khóa của bạn.

    6. Thêm ID khóa cho khóa. Bạn có thể tìm thấy ID khóa trong Chứng chỉ, Số nhận dạng & Hồ sơ trong Trung tâm Thành viên 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 xác minh reCAPTCHA

Để cho phép SDK khách sử dụng reCAPTCHA:

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

  2. Bấm đú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 từ phần Mục tiêu .

  4. Chọn tab Thông tin .

  5. Mở rộng phần Loại URL .

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

  7. Nhập ID khách hàng đã đảo ngược của bạn 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 .

Khi hoàn tất, cấu hình của bạn sẽ trông giống như sau:

Lược đồ tùy chỉnh

Theo tùy chọn, bạn có thể tùy chỉnh cách ứng dụng của mình trình bày SFSafariViewController hoặc UIWebView khi hiển thị reCAPTCHA. Để thực hiện việc này, hãy tạo một lớp tùy chỉnh tuân theo giao thức FIRAuthUIDelegate và chuyển nó tới verifyPhoneNumber:UIDelegate:completion: .

Lựa chọn hình thức tuyển sinh

Bạn có thể chọn xem ứng dụng của mình có yêu cầu xác thực đa yếu tố hay không cũng như cách thức 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 như một phần của quá trình đăng ký. Sử dụng phương pháp này nếu ứng dụng của bạn yêu cầu xác thực đa yếu tố cho tất cả người dùng. Lưu ý rằng tài khoản phải có địa chỉ email đã được xác minh để đăng ký yếu tố thứ hai, vì vậy quy trình đăng ký của bạn sẽ phải đáp ứng điều này.

  • Cung cấp tùy chọn có thể bỏ qua để đăng ký yếu tố thứ hai trong quá trình đăng ký. Các ứng dụng muốn khuyến khích nhưng không yêu cầu xác thực đa yếu tố có thể thích phương pháp này.

  • Cung cấp khả năng thêm yếu tố thứ hai từ trang quản lý hồ sơ hoặc tài khoản của người dùng, thay vì màn hình đăng ký. Điều này giảm thiểu xung đột trong quá trình đăng ký, trong khi vẫn cung cấp xác thực đa yếu tố cho người dùng nhạy cảm về bảo mật.

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

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

Để đă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 phiên đa yếu tố cho người dùng:

    Nhanh

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

    Mục tiêu-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 được định dạng bằng dấu + ở đầu và không có dấu câu hoặc khoảng trắng nào khác (ví dụ: +15105551234 )

    Nhanh

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

    Mục tiêu-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 cách tốt nhất là thông báo trước cho người dùng rằng họ sẽ nhận được tin nhắn SMS và áp dụng mức giá tiêu chuẩn.

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

  5. Sau khi mã SMS được gửi, hãy yêu cầu người dùng xác minh mã. Sau đó, sử dụng phản hồi của họ để tạo PhoneAuthCredential :

    Nhanh

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

    Mục tiêu-C

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

    Nhanh

    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    

    Mục tiêu-C

    FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
    
  7. Hoàn thành việc đăng ký. Tùy chọ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 cho người dùng có nhiều yếu tố thứ hai vì số điện thoại bị che trong quá trình xác thực (ví dụ: +1******1234).

    Nhanh

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

    Mục tiêu-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) {
        // ...
    }];
    

Mã bên dưới hiển thị một ví dụ hoàn chỉnh về việc đăng ký yếu tố thứ hai:

Nhanh

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

Mục tiêu-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) {
            // ...
        }];
    }];
}];

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

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

Để đăng nhập người dùng bằng xác minh SMS hai yếu tố:

  1. Đăng nhập người dùng bằng yếu tố đầu tiên của họ, sau đó gặp lỗi cho biết cần phải xác thực đa yếu tố. Lỗi này chứa trình giải quyết, gợi ý về yếu tố thứ hai đã đăng ký và phiên cơ bản chứng minh người dùng đã xác thực thành công với 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:

    Nhanh

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

    Mục tiêu-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à nhà cung cấp liên kết, chẳng hạn như OAuth, hãy nhận 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ọ nên sử dụng yếu tố nào. 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ị bằng resolver.hints[selectedIndex].displayName .

    Nhanh

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

    Mục tiêu-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 tới điện thoại của người dùng:

    Nhanh

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

    Mục tiêu-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 mã SMS được gửi, hãy yêu cầu người dùng xác minh mã và sử dụng mã đó để tạo PhoneAuthCredential :

    Nhanh

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

    Mục tiêu-C

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

    Nhanh

    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    

    Mục tiêu-C

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

    Nhanh

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

    Mục tiêu-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.
        }
    }];
    

Mã bên dưới hiển thị một ví dụ hoàn chỉnh về việc đăng nhập người dùng đa yếu tố:

Nhanh

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

Mục tiêu-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.
                }
            }];
        }];
    }
}];

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

Cái gì tiếp theo