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 tính năng Xác thực Firebase bằng Nền tảng nhận dạng, 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.

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. Mặc dù 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, nhưng việc chặn tin nhắn văn bản còn khó khăn 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 điện thoại, tính năng 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 các đối tượng độc hại đăng ký một dịch vụ bằng email mà họ không sở hữu, sau đó khoá chủ sở hữu thực sự bằng cách thêm yếu tố thứ hai.

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 trong bảng điều khiển của 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 tình trạng đ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 này 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 của Firebase.

Xác minh ứng dụng

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ể thực hiện việc 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 đến thiết bị của người dùng. 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 thông báo đẩy sử dụng phương thức này.

  • Xác minh reCAPTCHA: Nếu 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 trong nền hoặc bạn đang kiểm thử ứng dụng trong trình mô phỏng iOS), thì 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 ở chế độ im lặng

Cách bật thông báo APN để sử 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 của 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 phần Định cấu hình APN bằng FCM để tìm hiểu cách lấy khoá.

    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 phần Khoá xác thực ANG, ở mục 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 phần Chứng chỉ, giá trị nhận dạng và hồ sơ trên 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. Để thực hiện việc này, hãy tạo một lớp tuỳ chỉnh phù hợp với giao thức FIRAuthUIDelegate và truyền lớp đó đến verifyPhoneNumber:UIDelegate:completion:.

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

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 trong quá trình đăng ký. Sử dụng phương thức 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 để đă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 lựa 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, có thể ưu tiên phương pháp này bằng tính năng xác thực đa yếu tố.

  • Cho phép thêm yếu tố thứ hai từ trang quản lý 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 sự phiền hà trong quá trình đăng ký, trong khi vẫn cung cấp tính năng xác thực đa yếu tố cho những người dùng nhạy cảm về bảo mật.

  • Yêu cầu thêm tăng dần yếu tố thứ hai khi người dùng muốn truy cập vào 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

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 rằng số điện thoại có định dạng là + ở đầu và không có dấu câu hay khoảng trắng nào khác (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à mức cước tiêu chuẩn đó sẽ được áp dụng.

    Phương thức verifyPhoneNumber() bắt đầu quy 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ì hệ thống sẽ đưa ra 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 phản hồi của chú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 sẽ được che giấu trong quy trình xác thực (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) {
        // ...
    }];
    

Mã bên dưới là 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 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 cần phải xác thực đa yếu tố. Lỗi này chứa một trình phân giải, các gợi ý về các yếu tố thứ hai đã đăng ký và một phiên cơ bản chứng minh người dùng đã 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 được liên kết, chẳng hạn như OAuth, hãy phát hiệ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ọ chọn 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.

    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ã SMS, hãy yêu cầu người dùng xác minh mã và sử dụng mã đó để tạo 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 xác thực đa yếu tố.

Bước tiếp theo