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 thông qua Apple ID 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 từ đầu đến cuối.

Trước khi bắt đầu

Để cho phép người dùng đăng nhập 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 của bạn.

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

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

Đị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 đúng cách tính năng Đăng nhập bằng Apple trong dự án Firebase của mình. Cấu hình này sẽ khác nhau trên các nền tảng Android và Apple. Vui lòng làm theo phần "Định cấu hình tính năng Đăng nhập bằng Apple" trong hướng dẫn về nền tảng Apple và/hoặc Android trước khi tiếp tục.

Bật Apple làm nhà cung cấp dịch vụ đăng nhập

  1. Trong bảng điều khiển Firebase, hãy chuyển đến Security > Authentication.
  2. Trong thẻ Sign-in method (Phương thức đăng nhập), hãy bật nhà cung cấp dịch vụ đăng nhập Apple.
  3. Định cấu hình các chế độ cài đặt của nhà cung cấp dịch vụ Đăng nhập bằng Apple:
    • Apple: Nếu chỉ triển khai ứng dụng trên các nền tảng Apple, bạn có thể để trống các trường Mã dịch vụ, Mã nhóm Apple, khoá riêng tư và mã khoá.
    • Android: Hoàn tất các bước sau để hỗ trợ thiết bị Android:
      1. Thêm Firebase vào dự án Android.
      2. Chỉ định dấu vân tay SHA-1 của ứng dụng nếu bạn chưa làm.
        1. Trong bảng điều khiển Firebase, hãy chuyển đến phần cài đặt Settings > General tab.
        2. Di chuyển xuống thẻ Your apps (Ứng dụng của bạn), chọn ứng dụng Android rồi thêm dấu vân tay SHA-1 vào trường SHA certificate fingerprints (Dấu vân tay chứng chỉ SHA).

        Xem Xác thực ứng dụng của bạn để biết thông tin chi tiết về cách lấy dấu vân tay SHA của ứng dụng.

      3. Định cấu hình các chế độ cài đặt của nhà cung cấp dịch vụ Đăng nhập bằng Apple:
        1. Trong bảng điều khiển Firebase, hãy chuyển đến phần Security > Authentication.
        2. Trong thẻ Sign-in method (Phương thức đăng nhập), hãy nhấp vào nhà cung cấp dịch vụ đăng nhập Apple.
        3. Chỉ định Mã dịch vụ mà bạn đã tạo trong phần trước. Ngoài ra, trong phần cấu hình quy trình mã OAuth, hãy chỉ định Mã nhóm Apple, 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, khi đăng nhập. Người dùng chọn phương án này sẽ có địa chỉ email với miền privaterelay.appleid.com. Khi sử dụng tính năng Đăng nhập bằng Apple trong ứng dụng, bạn phải tuân thủ mọi chính sách hoặc điều khoản hiện hành dành cho nhà phát triển của Apple liên quan đến các Apple ID ẩn danh này.

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

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

Danh sách bên trên không phải là danh sách đầy đủ. Hãy tham khảo Thoả thuận cấp phép Chương trình Nhà phát triển của Apple trong phần Thành viên của tài khoản nhà phát triể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 cho tất cả lệnh gọi API.
  1. Thêm tệp tiêu đề Auth và App:
    #include "firebase/app.h"
    #include "firebase/auth.h"
  2. Trong mã khởi chạy, hãy tạo 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. Lấy lớp firebase::auth::Auth cho firebase::App. Có mối quan hệ 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 sẽ khác nhau trên các nền tảng Apple và Android.

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

Xác thực người dùng bằng Firebase thông qua Apple Sign In Objective-C SDK được gọi từ mã C++.

  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 – "nonce" – mà bạn sẽ dùng để đảm bảo mã thông báo nhận dạng mà bạn nhận được được cấp riêng để phản hồi yêu cầu xác thực của ứng dụng. Bước này 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 nonce cùng với yêu cầu đăng nhập. Apple sẽ chuyển hàm băm này mà không thay đổi trong phản hồi. Firebase xác thực phản hồi bằng cách băm nonce ban đầu rồi so sánh với giá trị do Apple chuyển.

  2. Bắt đầu quy trình đăng nhập của Apple, bao gồm cả hàm băm SHA256 của nonce và lớp uỷ quyền sẽ xử lý phản hồi của Apple trong yêu cầu của bạn (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 ASAuthorizationControllerDelegate`. Nếu đăng nhập thành công, hãy sử dụng mã thông báo nhận dạng trong phản hồi của Apple cùng với nonce chưa băm để xác thực bằng 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 và số chỉ dùng một lần ban đầu để tạo Thông tin xác thực Firebase rồi đă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ể sử dụng cùng một mẫu với Reauthenticate để truy xuất thông tin xác thực mới cho các thao tác nhạy cảm cần đăng nhập gần đây.

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

    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 tính năng Đăng nhập OAuth chung dựa trên web vào ứng dụng của bạn bằng Firebase SDK để thực hiện quy trình đăng nhập từ đầu đến cuối.

Để 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ể 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ác ngoài tiếng Anh, hãy đặt tham số locale. Hãy xem tài liệu Đăng nhập bằng Apple để biết các ngôn ngữ được hỗ trợ.

    // Localize to French.
    provider_data.custom_parameters["language"] = "fr";
    ```
    
  4. Sau khi định cấu hình dữ liệu nhà cung cấp, hãy sử dụng dữ liệu đó để tạo FederatedOAuthProvider.

    // 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 thao tác FirebaseAuth khác, thao tác này sẽ kiểm soát giao diện người dùng của bạn bằng cách bật một chế độ xem web mà người dùng có thể nhập thông tin xác thực của họ.

    Để 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 đó, ứng dụng của bạn có thể chờ hoặc đăng ký lệnh gọi lại trên Future.

  6. Bạn có thể sử dụng cùng một mẫu với ReauthenticateWithProvider để truy xuất thông tin xác thực mới cho các thao tác nhạy cảm cầ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 Future.

  7. Ngoài ra, bạn có thể sử dụng LinkWithCredential() để liên kết các nhà cung cấp danh tính khác nhau 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ó được sự đồng ý rõ ràng của người dùng trước khi 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 khi cho phép người dùng đăng nhập 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 ứng dụng Ghi chú của Apple

Không giống như các nhà cung cấp khác được Firebase Auth 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 sẽ cung cấp một địa chỉ email duy nhất cho người dùng đó (theo mẫu xyz@privaterelay.appleid.com) và chia sẻ địa chỉ email đó 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 sẽ chuyển tiếp email gửi đến địa chỉ ẩn danh đến đị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 trong lần đầu tiên người dùng đăng nhập. Thông thường, Firebase sẽ lưu trữ tên hiển thị trong lần đầu tiên người dùng đăng nhập bằng Apple. Bạn có thể lấy tên hiển thị này bằng current_user().display_name(). Tuy nhiên, nếu trước đây bạn đã sử dụng Apple để cho phép người dùng đăng nhập vào ứng dụng mà không dùng Firebase, thì Apple sẽ không cung cấp tên hiển thị của người dùng cho Firebase.

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à liên kết với thông tin xác thực – tức là tên người dùng và mật khẩu, số điện thoại hoặc thông tin nhà cung cấp dịch vụ xác thực – mà người dùng đã đăng nhập. Tài khoản mới này được lưu trữ trong dự án Firebase của bạn và có thể dùng để xác định người dùng trên mọi ứng dụng trong dự án, bất kể cách người dùng đăng nhập.

Trong ứng dụng, bạn có thể lấy thông tin cơ bản về hồ sơ người dùng từ đối tượng firebase::auth::User. Xem bài viết Quản lý người dùng.

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