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

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

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

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

เข้าร่วมโปรแกรม Developer Program ของ Apple

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

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

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

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

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

        ดูรายละเอียดเกี่ยวกับวิธีรับลายนิ้วมือ SHA ของแอปได้ที่ การตรวจสอบสิทธิ์ไคลเอ็นต์

      3. กำหนดค่าการตั้งค่าผู้ให้บริการลงชื่อเข้าใช้ Apple ดังนี้
        1. ในคอนโซล Firebase ให้ไปที่ความปลอดภัย > การตรวจสอบสิทธิ์
        2. ในแท็บวิธีการลงชื่อเข้าใช้ ให้คลิกผู้ให้บริการลงชื่อเข้าใช้ Apple
        3. ระบุรหัสบริการที่คุณสร้างไว้ในส่วนก่อนหน้า นอกจากนี้ ใน ส่วนการกำหนดค่าขั้นตอนการให้สิทธิ์ 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. เพิ่มไฟล์ส่วนหัว Auth และ App ดังนี้
    #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" สำหรับคำขอลงชื่อเข้าใช้ทุกรายการ ซึ่งคุณจะใช้เพื่อให้แน่ใจว่าโทเค็นรหัสที่คุณได้รับนั้นได้รับอนุญาตเป็นการเฉพาะเพื่อตอบสนองต่อคำขอการตรวจสอบสิทธิ์ของแอป ขั้นตอนนี้มีความสำคัญในการป้องกันการโจมตีแบบ Replay

      - (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 แล้วลงชื่อเข้าใช้ 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 โดยไม่ต้องสร้างโทเค็นและ Nonce สำหรับ "ลงชื่อเข้าใช้ด้วย 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.
    }

บน 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);
    

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

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

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

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

  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 ดูหัวข้อ จัดการผู้ใช้

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