ตรวจสอบสิทธิ์โดยใช้ Apple และ C++

คุณอนุญาตให้ผู้ใช้ตรวจสอบสิทธิ์กับ Firebase โดยใช้ Apple ID ได้โดย ใช้ Firebase SDK เพื่อดำเนินการขั้นตอนการลงชื่อเข้าใช้ OAuth 2.0 แบบครบวงจร

ก่อนเริ่มต้น

หากต้องการให้ผู้ใช้ลงชื่อเข้าใช้โดยใช้ Apple ก่อนอื่นให้กำหนดค่าการลงชื่อเข้าใช้ด้วย Apple ในเว็บไซต์นักพัฒนาแอปของ Apple จากนั้นเปิดใช้ Apple เป็นผู้ให้บริการลงชื่อเข้าใช้สำหรับโปรเจ็กต์ Firebase

เข้าร่วมโปรแกรมนักพัฒนาซอฟต์แวร์ของ Apple

มีเพียงสมาชิกโปรแกรมนักพัฒนาซอฟต์แวร์ของ Apple เท่านั้นที่กำหนดค่าการลงชื่อเข้าใช้ด้วย Apple ได้

กำหนดค่าการลงชื่อเข้าใช้ด้วย Apple

คุณต้องเปิดใช้และกำหนดค่าการลงชื่อเข้าใช้ด้วย Apple อย่างถูกต้องในโปรเจ็กต์ Firebase การกำหนดค่าจะแตกต่างกันไปในแพลตฟอร์ม Android และ Apple โปรดทำตามส่วน "กำหนดค่าการลงชื่อเข้าใช้ด้วย Apple" ของคำแนะนำสำหรับแพลตฟอร์ม Apple และ/หรือ Android ก่อนดำเนินการต่อ

เปิดใช้ Apple เป็นผู้ให้บริการลงชื่อเข้าใช้

  1. เปิดส่วนการตรวจสอบสิทธิ์ในคอนโซล Firebase ในแท็บวิธีการลงชื่อเข้าใช้ ให้เปิดใช้ผู้ให้บริการ Apple
  2. กำหนดการตั้งค่าผู้ให้บริการลงชื่อเข้าใช้ด้วย Apple ดังนี้
    1. หากคุณกําลังติดตั้งใช้งานแอปบนแพลตฟอร์ม Apple เท่านั้น คุณสามารถเว้นช่องรหัสบริการ, รหัสทีม Apple, คีย์ส่วนตัว และรหัสคีย์ว่างไว้ได้
    2. หากต้องการรับการสนับสนุนในอุปกรณ์ Android ให้ทำดังนี้
      1. เพิ่ม Firebase ในโปรเจ็กต์ Android อย่าลืมลงทะเบียนลายเซ็น SHA-1 ของแอปเมื่อตั้งค่าแอปในFirebase Console
      2. ในคอนโซล Firebase ให้เปิดส่วน Auth ในแท็บวิธีการลงชื่อเข้าใช้ ให้เปิดใช้ผู้ให้บริการ Apple ระบุรหัสบริการที่คุณสร้างไว้ใน ส่วนก่อนหน้า นอกจากนี้ ในส่วนการกำหนดค่าโฟลว์ของรหัส OAuth ให้ระบุรหัสทีม Apple รวมถึงคีย์ส่วนตัวและรหัสคีย์ที่คุณสร้างขึ้น ในส่วนก่อนหน้า

ปฏิบัติตามข้อกำหนดของ Apple เกี่ยวกับข้อมูลที่ไม่ระบุตัวบุคคล

การลงชื่อเข้าใช้ด้วย Apple ช่วยให้ผู้ใช้มีตัวเลือกในการปกปิดข้อมูล รวมถึงอีเมลเมื่อลงชื่อเข้าใช้ ผู้ใช้ที่เลือกตัวเลือกนี้ จะมีอีเมลที่มีโดเมน privaterelay.appleid.com เมื่อ คุณใช้ระบบลงชื่อเข้าใช้ด้วย Apple ในแอป คุณต้องปฏิบัติตาม นโยบายหรือข้อกำหนดสำหรับนักพัฒนาแอปที่เกี่ยวข้องจาก Apple เกี่ยวกับ Apple ID ที่ไม่ระบุตัวตนเหล่านี้

ซึ่งรวมถึงการขอความยินยอมจากผู้ใช้ที่จำเป็นก่อนที่คุณจะ เชื่อมโยงข้อมูลส่วนบุคคลที่ระบุตัวบุคคลนั้นได้โดยตรงกับ Apple ID ที่ ไม่ระบุตัวตน เมื่อใช้การตรวจสอบสิทธิ์ Firebase อาจรวมถึงการดำเนินการต่อไปนี้

  • ลิงก์อีเมลกับ Apple ID ที่มีการปกปิดข้อมูลระบุตัวบุคคล หรือในทางกลับกัน
  • ลิงก์หมายเลขโทรศัพท์กับ Apple ID ที่ไม่ระบุตัวบุคคลหรือในทางกลับกัน
  • ลิงก์ข้อมูลเข้าสู่ระบบโซเชียลที่ไม่ระบุตัวตน (Facebook, Google ฯลฯ) กับ Apple ID ที่ไม่ระบุตัวตน หรือในทางกลับกัน

โปรดทราบว่ารายการด้านบนเป็นเพียงตัวอย่างบางส่วนเท่านั้น โปรดอ่านข้อตกลงการอนุญาตให้ใช้สิทธิของโปรแกรมนักพัฒนาแอปของ Apple ในส่วนการเป็นสมาชิกของบัญชีนักพัฒนาแอปเพื่อให้แน่ใจว่าแอปเป็นไปตามข้อกำหนดของ Apple

เข้าถึงชั้นเรียน firebase::auth::Auth

Auth คลาสเป็นเกตเวย์สำหรับการเรียก API ทั้งหมด
  1. เพิ่มไฟล์ส่วนหัวการตรวจสอบสิทธิ์และแอป
    #include "firebase/app.h"
    #include "firebase/auth.h"
  2. ในโค้ดการเริ่มต้น ให้สร้างคลาส 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. ซื้อfirebase::auth::Authชั้นเรียนสำหรับfirebase::App มีการแมประหว่าง App กับ Auth แบบหนึ่งต่อหนึ่ง
    firebase::auth::Auth* auth = firebase::auth::Auth::GetAuth(app);

จัดการขั้นตอนการลงชื่อเข้าใช้ด้วย Firebase SDK

กระบวนการลงชื่อเข้าใช้ด้วย Apple จะแตกต่างกันไปในแพลตฟอร์ม Apple และ Android

ในแพลตฟอร์มของ Apple

ตรวจสอบสิทธิ์ผู้ใช้ด้วย Firebase ผ่าน Apple Sign In Objective-C SDK ที่เรียกใช้จากโค้ด C++

  1. สำหรับคำขอลงชื่อเข้าใช้ทุกรายการ ให้สร้างสตริงแบบสุ่ม ซึ่งก็คือ "nonce" ซึ่งคุณจะใช้เพื่อให้แน่ใจว่าโทเค็นรหัสที่คุณได้รับนั้น ได้รับเฉพาะเพื่อตอบสนองต่อคำขอการตรวจสอบสิทธิ์ของแอป ขั้นตอนนี้มีความสำคัญในการป้องกันการโจมตีแบบเล่นซ้ำ

      - (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--;
            }
          }
        }
      }
    
    

    คุณจะส่งแฮช SHA256 ของ Nonce พร้อมกับคำขอลงชื่อเข้าใช้ ซึ่ง Apple จะส่งต่อโดยไม่มีการเปลี่ยนแปลงในการตอบกลับ Firebase จะตรวจสอบการตอบกลับ โดยการแฮช Nonce เดิมและเปรียบเทียบกับค่าที่ Apple ส่งมา

  2. เริ่มขั้นตอนการลงชื่อเข้าใช้ของ Apple โดยระบุแฮช SHA256 ของ ค่า Nonce และคลาสตัวแทนที่จะจัดการการตอบกลับของ Apple ในคำขอ (ดู ขั้นตอนถัดไป)

      - (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. จัดการการตอบกลับของ Apple ในการติดตั้งใช้งาน ASAuthorizationControllerDelegate หากลงชื่อเข้าใช้สำเร็จ ให้ใช้โทเค็น รหัสจากการตอบกลับของ Apple พร้อมกับ Nonce ที่ไม่ได้แฮชเพื่อตรวจสอบสิทธิ์กับ 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. ใช้สตริงโทเค็นและ Nonce เดิมที่ได้เพื่อสร้าง Firebase Credential และลงชื่อเข้าใช้ Firebase

    firebase::auth::OAuthProvider::GetCredential(
            /*provider_id=*/"apple.com", token, nonce,
            /*access_token=*/nullptr);
    
    firebase::Future<firebase::auth::AuthResult> result =
        auth->SignInAndRetrieveDataWithCredential(credential);
    
  5. คุณสามารถใช้รูปแบบเดียวกันกับ Reauthenticate ซึ่งใช้เพื่อดึงข้อมูลเข้าสู่ระบบใหม่สำหรับการดำเนินการที่ละเอียดอ่อนซึ่งต้องมีการเข้าสู่ระบบล่าสุดได้

    firebase::Future<firebase::auth::AuthResult> result =
        user->Reauthenticate(credential);
    
  6. คุณสามารถใช้รูปแบบเดียวกันนี้เพื่อลิงก์บัญชีด้วยการลงชื่อเข้าใช้ด้วย Apple อย่างไรก็ตาม คุณอาจพบข้อผิดพลาดเมื่อบัญชี Firebase ที่มีอยู่ลิงก์กับบัญชี Apple ที่คุณพยายามลิงก์อยู่แล้ว เมื่อเกิดกรณีนี้ขึ้น อนาคตจะแสดงสถานะเป็น kAuthErrorCredentialAlreadyInUse และ AuthResult อาจมี credential ที่ถูกต้อง คุณใช้ข้อมูลเข้าสู่ระบบนี้เพื่อลงชื่อเข้าใช้บัญชีที่ลิงก์กับ Apple ผ่าน SignInAndRetrieveDataWithCredential ได้โดยไม่ต้อง สร้างโทเค็นการลงชื่อเข้าใช้ Apple และ Nonce อื่น

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

บน Android

ใน Android ให้ตรวจสอบสิทธิ์ผู้ใช้ด้วย Firebase โดยการผสานรวมการเข้าสู่ระบบ OAuth ทั่วไปบนเว็บเข้ากับแอปโดยใช้ Firebase SDK เพื่อดำเนินการขั้นตอนการลงชื่อเข้าใช้ตั้งแต่ต้นจนจบ

หากต้องการจัดการขั้นตอนการลงชื่อเข้าใช้ด้วย Firebase SDK ให้ทำตามขั้นตอนต่อไปนี้

  1. สร้างอินสแตนซ์ของ FederatedOAuthProviderData ที่กำหนดค่าด้วย รหัสผู้ให้บริการที่เหมาะสมสำหรับ Apple

    firebase::auth::FederatedOAuthProviderData provider_data("apple.com");
    
  2. ไม่บังคับ: ระบุขอบเขต OAuth 2.0 เพิ่มเติมจากค่าเริ่มต้นที่คุณต้องการขอจากผู้ให้บริการตรวจสอบสิทธิ์

    provider_data.scopes.push_back("email");
    provider_data.scopes.push_back("name");
    
  3. ไม่บังคับ: หากต้องการแสดงหน้าจอลงชื่อเข้าใช้ของ Apple ในภาษาอื่นที่ไม่ใช่ภาษาอังกฤษ ให้ตั้งค่าพารามิเตอร์ locale ดูภาษาที่รองรับได้ใน เอกสารประกอบการลงชื่อเข้าใช้ด้วย Apple

    // Localize to French.
    provider_data.custom_parameters["language"] = "fr";
    ```
    
  4. เมื่อกำหนดค่าข้อมูลผู้ให้บริการแล้ว ให้ใช้ข้อมูลดังกล่าวเพื่อสร้าง FederatedOAuthProvider

    // Construct a FederatedOAuthProvider for use in Auth methods.
    firebase::auth::FederatedOAuthProvider provider(provider_data);
    
  5. ตรวจสอบสิทธิ์ด้วย Firebase โดยใช้ออบเจ็กต์ผู้ให้บริการการตรวจสอบสิทธิ์ โปรดทราบว่าการดำเนินการนี้จะควบคุม UI โดยการแสดง มุมมองเว็บที่ผู้ใช้สามารถป้อนข้อมูลเข้าสู่ระบบได้ ซึ่งแตกต่างจาก การดำเนินการอื่นๆ ของ FirebaseAuth

    หากต้องการเริ่มขั้นตอนการลงชื่อเข้าใช้ ให้เรียกใช้ signInWithProvider ดังนี้

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

    จากนั้นแอปพลิเคชันอาจรอหรือ ลงทะเบียนการเรียกกลับในอนาคต

  6. คุณสามารถใช้รูปแบบเดียวกันกับ ReauthenticateWithProvider ซึ่งใช้เพื่อดึงข้อมูลเข้าสู่ระบบใหม่สำหรับการดำเนินการที่ละเอียดอ่อนซึ่งต้องมีการเข้าสู่ระบบล่าสุดได้

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

    จากนั้นแอปพลิเคชันอาจรอหรือลงทะเบียนการเรียกกลับใน อนาคต

  7. และคุณสามารถใช้ LinkWithCredential() เพื่อลิงก์ผู้ให้บริการข้อมูลประจำตัวที่แตกต่างกัน กับบัญชีที่มีอยู่ได้

    โปรดทราบว่า Apple กำหนดให้คุณต้องได้รับความยินยอมอย่างชัดแจ้งจากผู้ใช้ก่อนที่จะลิงก์บัญชี Apple ของผู้ใช้กับข้อมูลอื่นๆ

    ตัวอย่างเช่น หากต้องการลิงก์บัญชี Facebook กับบัญชี Firebase ปัจจุบัน ให้ใช้ โทเค็นการเข้าถึงที่คุณได้รับจากการลงชื่อเข้าใช้ 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);
    

ลงชื่อเข้าใช้ด้วยโน้ตของ Apple

Apple ไม่ได้ให้ URL รูปภาพ ซึ่งต่างจากผู้ให้บริการรายอื่นๆ ที่ Firebase Auth รองรับ

นอกจากนี้ เมื่อผู้ใช้เลือกที่จะไม่แชร์อีเมลกับแอป Apple จะ จัดสรรอีเมลที่ไม่ซ้ำกันสำหรับผู้ใช้รายนั้น (ในรูปแบบ xyz@privaterelay.appleid.com) ซึ่งจะแชร์กับแอปของคุณ หากคุณ กำหนดค่าบริการส่งต่ออีเมลส่วนตัว Apple จะส่งต่ออีเมลที่ส่งไปยัง อีเมลที่ปกปิดข้อมูลระบุตัวบุคคลไปยังอีเมลจริงของผู้ใช้

Apple จะแชร์ข้อมูลผู้ใช้ เช่น ชื่อที่แสดง กับแอปใน ครั้งแรกที่ผู้ใช้ลงชื่อเข้าใช้เท่านั้น โดยปกติแล้ว Firebase จะจัดเก็บชื่อที่แสดงเมื่อผู้ใช้ลงชื่อเข้าใช้ด้วย Apple เป็นครั้งแรก ซึ่งคุณจะรับได้ด้วย current_user().display_name() อย่างไรก็ตาม หากก่อนหน้านี้คุณใช้ Apple เพื่อให้ผู้ใช้ลงชื่อเข้าใช้แอปโดยไม่ใช้ Firebase ทาง Apple จะไม่ให้ชื่อที่แสดงของผู้ใช้แก่ Firebase

ขั้นตอนถัดไป

หลังจากที่ผู้ใช้ลงชื่อเข้าใช้เป็นครั้งแรก ระบบจะสร้างบัญชีผู้ใช้ใหม่และ ลิงก์กับข้อมูลเข้าสู่ระบบ ซึ่งได้แก่ ชื่อผู้ใช้และรหัสผ่าน หมายเลขโทรศัพท์ หรือ ข้อมูลผู้ให้บริการตรวจสอบสิทธิ์ที่ผู้ใช้ลงชื่อเข้าใช้ ระบบจะจัดเก็บบัญชีใหม่นี้เป็นส่วนหนึ่งของโปรเจ็กต์ Firebase และใช้เพื่อระบุผู้ใช้ในทุกแอปในโปรเจ็กต์ได้ ไม่ว่าผู้ใช้จะลงชื่อเข้าใช้ด้วยวิธีใดก็ตาม

ในแอป คุณสามารถรับข้อมูลโปรไฟล์พื้นฐานของผู้ใช้จากออบเจ็กต์ firebase::auth::User ได้ ดูหัวข้อ จัดการผู้ใช้

ในกฎความปลอดภัยของฐานข้อมูลเรียลไทม์และ Cloud Storage ของ Firebase คุณจะรับรหัสผู้ใช้ที่ไม่ซ้ำกันของผู้ใช้ที่ลงชื่อเข้าใช้จากตัวแปร auth และใช้รหัสดังกล่าวเพื่อควบคุมข้อมูลที่ผู้ใช้เข้าถึงได้