Xác thực bằng Apple và C++

Bạn có thể cho phép người dùng xác thực bằng Firebase bằng ID Apple của họ bằng cách sử dụng Firebase SDK để thực hiện quy trình đăng nhập OAuth 2.0 toàn diện.

Trước khi bắt đầu

Để đăng nhập cho người dùng bằng Apple, trước tiên hãy định cấu hình tính năng Đăng nhập bằng Apple trên trang web dành cho nhà phát triển của Apple, sau đó bật Apple làm nhà cung cấp dịch vụ đăng nhập cho Dự án Firebase.

Tham gia Chương trình nhà phát triển của Apple

Chỉ thành viên của Nhà phát triển Apple mới có thể định cấu hình cho tính năng Đăng nhập bằng Apple Chương trình.

Định cấu hình tính năng Đăng nhập bằng Apple

Bạn phải bật và định cấu hình tính năng Đăng nhập bằng Apple đúng cách trong dự án Firebase của mình. Cấu hình sẽ khác nhau tuỳ theo nền tảng Android và Apple. Vui lòng làm theo "Định cấu hình đăng nhập bằng Apple" của Các nền tảng của Apple và/hoặc Hướng dẫn dành cho Android trước đây tiếp tục.

Cho phép Apple làm nhà cung cấp dịch vụ đăng nhập

  1. Trong bảng điều khiển Firebase, hãy mở phần Xác thực. Trên thẻ Phương thức đăng nhập, bật nhà cung cấp Apple.
  2. Định cấu hình chế độ cài đặt của nhà cung cấp dịch vụ Đăng nhập qua Apple:
    1. Nếu đang triển khai ứng dụng của mình chỉ trên các nền tảng của Apple, bạn có thể thoát khỏi Các trường ID dịch vụ, ID nhóm của Apple, khóa riêng tư và ID khóa đều trống.
    2. Để được hỗ trợ trên thiết bị Android:
      1. Thêm Firebase vào dự án Android của bạn. Hãy hãy nhớ đăng ký chữ ký SHA-1 của ứng dụng khi bạn thiết lập ứng dụng trong Bảng điều khiển Firebase.
      2. Trong Firebase , hãy mở phần Xác thực. Trên thẻ Phương thức đăng nhập, bật nhà cung cấp Apple. Chỉ định Mã dịch vụ mà bạn đã tạo phần trước. Ngoài ra, trong phần cấu hình luồng mã OAuth, chỉ định ID nhóm Apple của bạn, cũng như khoá riêng tư và mã khoá mà bạn đã tạo trong phần trước.

Tuân thủ các yêu cầu của Apple về dữ liệu ẩn danh

Tính năng Đăng nhập bằng Apple cho phép người dùng ẩn danh dữ liệu của họ, bao gồm cả địa chỉ email của họ khi đăng nhập. Người dùng chọn phương án này có địa chỉ email với miền privaterelay.appleid.com. Thời gian bạn sử dụng tính năng Đăng nhập bằng Apple trong ứng dụng của mình, bạn phải tuân thủ mọi chính sách dành cho nhà phát triển hoặc điều khoản của Apple liên quan đến Mã nhận dạng.

Điều này bao gồm việc có được sự đồng ý cần thiết của người dùng trước khi bạn liên kết mọi thông tin cá nhân nhận dạng trực tiếp với Apple ẩn danh Mã nhận dạng. Khi sử dụng tính năng Xác thực Firebase, điều này có thể bao gồm những điều sau hành động:

  • Liên kết một địa chỉ email với một ID Apple ẩn danh hoặc ngược lại.
  • Liên kết số điện thoại với ID Apple ẩn danh hoặc ngược lại
  • Liên kết thông tin đăng nhập xã hội không ẩn danh (Facebook, Google, v.v.) với Apple ID ẩn danh hoặc ngược lại.

Danh sách bên trên chưa đầy đủ. Tham khảo Chương trình dành cho nhà phát triển của Apple Thoả thuận cấp phép trong phần Thành viên của tài khoản nhà phát triển để thực hiện đảm bảo ứng dụng của bạn đáp ứng các yêu cầu của Apple.

Truy cập vào lớp firebase::auth::Auth

Lớp Auth là cổng vào cho tất cả lệnh gọi API.
  1. Thêm tệp tiêu đề Ứng dụng và Xác thực:
    #include "firebase/app.h"
    #include "firebase/auth.h"
    
  2. Trong mã khởi chạy của bạn, hãy tạo một Lớp firebase::App.
    #if defined(__ANDROID__)
      firebase::App* app =
          firebase::App::Create(firebase::AppOptions(), my_jni_env, my_activity);
    #else
      firebase::App* app = firebase::App::Create(firebase::AppOptions());
    #endif  // defined(__ANDROID__)
    
  3. Mua lớp firebase::auth::Auth cho firebase::App của bạn. Có một mối liên kết một với một giữa AppAuth.
    firebase::auth::Auth* auth = firebase::auth::Auth::GetAuth(app);
    

Xử lý quy trình đăng nhập bằng Firebase SDK

Quy trình Đăng nhập bằng Apple khác nhau tuỳ theo nền tảng của Apple và Android.

Trên các nền tảng của Apple

Xác thực người dùng bằng Firebase thông qua tính năng Đăng nhập của Apple SDK object-C được gọi từ mã C++ của bạn.

  1. Đối với mỗi yêu cầu đăng nhập, hãy tạo một chuỗi ngẫu nhiên — một "nonce" – bạn sẽ sử dụng phương thức này để đảm bảo mã thông báo nhận dạng mà bạn nhận được là được cấp riêng theo yêu cầu xác thực của ứng dụng. Chiến dịch này là rất quan trọng để ngăn chặn các cuộc tấn công phát lại.

      - (NSString *)randomNonce:(NSInteger)length {
        NSAssert(length > 0, @"Expected nonce to have positive length");
        NSString *characterSet = @"0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._";
        NSMutableString *result = [NSMutableString string];
        NSInteger remainingLength = length;
    
        while (remainingLength > 0) {
          NSMutableArray *randoms = [NSMutableArray arrayWithCapacity:16];
          for (NSInteger i = 0; i < 16; i++) {
            uint8_t random = 0;
            int errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random);
            NSAssert(errorCode == errSecSuccess, @"Unable to generate nonce: OSStatus %i", errorCode);
    
            [randoms addObject:@(random)];
          }
    
          for (NSNumber *random in randoms) {
            if (remainingLength == 0) {
              break;
            }
    
            if (random.unsignedIntValue < characterSet.length) {
              unichar character = [characterSet characterAtIndex:random.unsignedIntValue];
              [result appendFormat:@"%C", character];
              remainingLength--;
            }
          }
        }
      }
    
    

    Bạn sẽ gửi hàm băm SHA256 của số chỉ dùng một lần cùng với yêu cầu đăng nhập. Apple sẽ truyền không thay đổi trong phản hồi. Firebase xác thực phản hồi bằng cách băm số chỉ dùng một lần ban đầu rồi so sánh với giá trị mà Apple chuyển qua.

  2. Bắt đầu quy trình đăng nhập của Apple, bao gồm trong yêu cầu của bạn, hàm băm SHA256 của số chỉ dùng một lần và lớp uỷ quyền sẽ xử lý phản hồi của Apple (xem bước tiếp theo):

      - (void)startSignInWithAppleFlow {
        NSString *nonce = [self randomNonce:32];
        self.currentNonce = nonce;
        ASAuthorizationAppleIDProvider *appleIDProvider = [[ASAuthorizationAppleIDProvider alloc] init];
        ASAuthorizationAppleIDRequest *request = [appleIDProvider createRequest];
        request.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail];
        request.nonce = [self stringBySha256HashingString:nonce];
    
        ASAuthorizationController *authorizationController =
            [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]];
        authorizationController.delegate = self;
        authorizationController.presentationContextProvider = self;
        [authorizationController performRequests];
      }
    
      - (NSString *)stringBySha256HashingString:(NSString *)input {
        const char *string = [input UTF8String];
        unsigned char result[CC_SHA256_DIGEST_LENGTH];
        CC_SHA256(string, (CC_LONG)strlen(string), result);
    
        NSMutableString *hashed = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
        for (NSInteger i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
          [hashed appendFormat:@"%02x", result[i]];
        }
        return hashed;
      }
    
  3. Xử lý phản hồi của Apple trong quá trình triển khai AS LTEControllerDelegate. Nếu đăng nhập thành công, hãy sử dụng mã nhận dạng mã thông báo từ phản hồi của Apple với số chỉ dùng một lần chưa được băm để xác thực với Firebase:

      - (void)authorizationController:(ASAuthorizationController *)controller
         didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0)) {
        if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) {
          ASAuthorizationAppleIDCredential *appleIDCredential = authorization.credential;
          NSString *rawNonce = self.currentNonce;
          NSAssert(rawNonce != nil, @"Invalid state: A login callback was received, but no login request was sent.");
    
          if (appleIDCredential.identityToken == nil) {
            NSLog(@"Unable to fetch identity token.");
            return;
          }
    
          NSString *idToken = [[NSString alloc] initWithData:appleIDCredential.identityToken
                                                    encoding:NSUTF8StringEncoding];
          if (idToken == nil) {
            NSLog(@"Unable to serialize id token from data: %@", appleIDCredential.identityToken);
          }
        }
    
  4. Sử dụng chuỗi mã thông báo thu được và số chỉ dùng một lần ban đầu để tạo Firebase Thông tin đăng nhập và đăng nhập vào Firebase.

    firebase::auth::OAuthProvider::GetCredential(
            /*provider_id=*/"apple.com", token, nonce,
            /*access_token=*/nullptr);
    
    firebase::Future<firebase::auth::AuthResult> result =
        auth->SignInAndRetrieveDataWithCredential(credential);
    
  5. Bạn có thể dùng cùng một mẫu với Reauthenticate, mẫu này có thể là dùng để truy xuất thông tin đăng nhập mới cho những hoạt động nhạy cảm cần đến lần đăng nhập gần đây.

    firebase::Future<firebase::auth::AuthResult> result =
        user->Reauthenticate(credential);
    
  6. Bạn có thể dùng cùng một mẫu để liên kết một tài khoản có tính năng Đăng nhập bằng Apple. Tuy nhiên, bạn có thể gặp lỗi khi một tài khoản Firebase hiện tại đã đã được liên kết với tài khoản Apple mà bạn đang muốn liên kết. Khi điều này xảy ra, tương lai sẽ trả về trạng thái là kAuthErrorCredentialAlreadyInUseAuthResult có thể chứa thông số hợp lệ credential. Bạn có thể sử dụng thông tin đăng nhập này để đăng nhập vào tài khoản Apple tài khoản qua SignInAndRetrieveDataWithCredential mà không cần tạo một mã thông báo Đăng nhập Apple và số chỉ dùng một lần khác.

    firebase::Future<firebase::auth::AuthResult> link_result =
        auth->current_user().LinkWithCredential(credential);
    
    // To keep example simple, wait on the current thread until call completes.
    while (link_result.status() == firebase::kFutureStatusPending) {
      Wait(100);
    }
    
    // Determine the result of the link attempt
    if (link_result.error() == firebase::auth::kAuthErrorNone) {
      // user linked correctly.
    } else if (link_result.error() ==
                   firebase::auth::kAuthErrorCredentialAlreadyInUse &&
               link_result.result()
                   ->additional_user_info.updated_credential.is_valid()) {
      // Sign In with the new credential
      firebase::Future<firebase::auth::AuthResult> result =
          auth->SignInAndRetrieveDataWithCredential(
              link_result.result()->additional_user_info.updated_credential);
    } else {
      // Another link error occurred.
    }
    

Trên Android

Trên Android, hãy xác thực người dùng bằng Firebase bằng cách tích hợp nền tảng web Thông tin chung về Đăng nhập OAuth vào ứng dụng của bạn bằng cách sử dụng Firebase SDK để thực hiện việc kết thúc kết thúc quy trình đăng nhập.

Để xử lý quy trình đăng nhập bằng Firebase SDK, hãy làm theo các bước sau:

  1. Tạo một thực thể của FederatedOAuthProviderData được định cấu hình bằng mã nhà cung cấp phù hợp với Apple.

    firebase::auth::FederatedOAuthProviderData provider_data("apple.com");
    
  2. Không bắt buộc: Chỉ định các phạm vi OAuth 2.0 khác ngoài phạm vi mặc định mà bạn muốn yêu cầu từ nhà cung cấp dịch vụ xác thực.

    provider_data.scopes.push_back("email");
    provider_data.scopes.push_back("name");
    
  3. Không bắt buộc: Nếu bạn muốn hiển thị màn hình đăng nhập của Apple bằng một ngôn ngữ không phải tiếng Anh, hãy đặt thông số locale. Xem Đăng nhập bằng Tài liệu của Apple cho các ngôn ngữ được hỗ trợ.

    // Localize to French.
    provider_data.custom_parameters["language"] = "fr";
    ```
    
  4. Sau khi dữ liệu của nhà cung cấp của bạn đã được định cấu hình, hãy sử dụng dữ liệu đó để tạo Liên kết OAuthProvider.

    // Construct a FederatedOAuthProvider for use in Auth methods.
    firebase::auth::FederatedOAuthProvider provider(provider_data);
    
  5. Xác thực bằng Firebase bằng đối tượng nhà cung cấp dịch vụ Xác thực. Xin lưu ý rằng không giống như các hoạt động FirebaseAuth khác, thao tác này sẽ kiểm soát giao diện người dùng bằng cách bật lên thiết lập một chế độ xem web để người dùng có thể nhập thông tin xác thực của mình.

    Để bắt đầu quy trình đăng nhập, hãy gọi signInWithProvider:

    firebase::Future<firebase::auth::AuthResult> result =
      auth->SignInWithProvider(provider_data);
    

    Sau đó, đơn đăng ký của bạn có thể đợi hoặc hãy đăng ký lệnh gọi lại trên Future.

  6. Bạn có thể dùng cùng một mẫu với ReauthenticateWithProvider, mẫu này có thể là dùng để truy xuất thông tin đăng nhập mới cho những hoạt động nhạy cảm cần đến lần đăng nhập gần đây.

    firebase::Future<firebase::auth::AuthResult> result =
      user.ReauthenticateWithProvider(provider_data);
    

    Sau đó, ứng dụng của bạn có thể chờ hoặc đăng ký lệnh gọi lại trên Tương lai.

  7. Ngoài ra, bạn có thể sử dụng LinkWithCredential() để liên kết nhiều nhà cung cấp danh tính đối với các tài khoản hiện có.

    Xin lưu ý rằng Apple yêu cầu bạn phải được người dùng đồng ý rõ ràng trước khi bạn liên kết tài khoản Apple của họ với các dữ liệu khác.

    Ví dụ: để liên kết tài khoản Facebook với tài khoản Firebase hiện tại, hãy sử dụng mã truy cập mà bạn nhận được từ việc đăng nhập người dùng vào Facebook:

    // Initialize a Facebook credential with a Facebook access token.
    AuthCredential credential =
        firebase::auth::FacebookAuthProvider.getCredential(token);
    
    // Assuming the current user is an Apple user linking a Facebook provider.
    firebase::Future<firebase::auth::AuthResult> result =
        auth.current_user().LinkWithCredential(credential);
    

Đăng nhập bằng Apple Notes

Không giống như các nhà cung cấp khác được tính năng Xác thực Firebase hỗ trợ, Apple không cung cấp URL ảnh.

Ngoài ra, khi người dùng chọn không chia sẻ email của họ với ứng dụng, Apple cấp một địa chỉ email duy nhất cho người dùng đó (theo biểu mẫu xyz@privaterelay.appleid.com) mà hệ thống này chia sẻ với ứng dụng của bạn. Nếu bạn đã định cấu hình dịch vụ chuyển tiếp email riêng tư, Apple chuyển tiếp các email được gửi đến địa chỉ ẩn danh sang địa chỉ email thực của người dùng.

Apple chỉ chia sẻ thông tin người dùng (chẳng hạn như tên hiển thị) với các ứng dụng khi người dùng đăng nhập lần đầu tiên. Thông thường, Firebase lưu trữ tên hiển thị lần đầu tiên người dùng đăng nhập bằng Apple. Bạn có thể tải tính năng này bằng current_user().display_name(). Tuy nhiên, nếu trước đây bạn đã sử dụng Apple để ký người dùng truy cập vào ứng dụng mà không sử dụng Firebase, Apple sẽ không cung cấp cho Firebase tên hiển thị của người dùng.

Các bước tiếp theo

Sau khi người dùng đăng nhập lần đầu tiên, một tài khoản người dùng mới sẽ được tạo và được liên kết với thông tin đăng nhập, tức là tên người dùng và mật khẩu, số điện thoại hoặc thông tin của nhà cung cấp dịch vụ xác thực—người dùng đã đăng nhập. Tài khoản mới này được lưu trữ dưới dạng của dự án Firebase và có thể được dùng để xác định người dùng trên mỗi trong dự án của bạn, bất kể người dùng đăng nhập bằng cách nào.

Trong ứng dụng của mình, bạn có thể lấy thông tin hồ sơ cơ bản của người dùng từ Đối tượng firebase::auth::User. Xem Quản lý người dùng.

Trong Cơ sở dữ liệu theo thời gian thực của Firebase và Quy tắc bảo mật của Cloud Storage, bạn có thể nhận được mã nhận dạng người dùng duy nhất của người dùng đã đăng nhập từ biến xác thực và sử dụng mã đó để kiểm soát loại dữ liệu mà người dùng có thể truy cập.