ตรวจสอบสิทธิ์โดยใช้การเข้าสู่ระบบ Facebook บนแพลตฟอร์ม Apple

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

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

ใช้ Swift Package Manager เพื่อติดตั้งและจัดการทรัพยากร Dependency ของ Firebase

  1. เปิดโปรเจ็กต์แอปใน Xcode แล้วไปที่ไฟล์ > เพิ่มแพ็กเกจ
  2. เมื่อได้รับข้อความแจ้ง ให้เพิ่มที่เก็บ SDK สำหรับแพลตฟอร์ม Firebase ของ Apple ดังนี้
  3.   https://github.com/firebase/firebase-ios-sdk.git
  4. เลือกไลบรารีการตรวจสอบสิทธิ์ Firebase
  5. เพิ่มแฟล็ก -ObjC ลงในส่วนแฟล็ก Linker อื่นๆ ของการตั้งค่าบิลด์ของเป้าหมาย
  6. เมื่อเสร็จสิ้นแล้ว Xcode จะเริ่มแก้ปัญหาและดาวน์โหลดทรัพยากร Dependency ในเบื้องหลังโดยอัตโนมัติ

จากนั้น ให้ทำตามขั้นตอนการกำหนดค่าต่อไปนี้

  1. ในเว็บไซต์ Facebook for Developers ให้รับรหัสแอปและข้อมูลลับของแอปสำหรับแอปของคุณ
  2. เปิดใช้การเข้าสู่ระบบ Facebook โดยทำดังนี้
    1. ในคอนโซล Firebase ให้เปิดส่วนการตรวจสอบสิทธิ์
    2. ในแท็บวิธีการลงชื่อเข้าใช้ ให้เปิดใช้วิธีการลงชื่อเข้าใช้ Facebook และระบุ App ID และ App Secret ที่ได้รับจาก Facebook
    3. จากนั้นตรวจสอบว่า URI การเปลี่ยนเส้นทาง OAuth (เช่น my-app-12345.firebaseapp.com/__/auth/handler) อยู่ในรายการ URI การเปลี่ยนเส้นทาง OAuth ในหน้าการตั้งค่าของแอป Facebook ในเว็บไซต์ Facebook for Developers ในการกำหนดค่า การตั้งค่าผลิตภัณฑ์ > การเข้าสู่ระบบ Facebook

ใช้การเข้าสู่ระบบ Facebook

ในการใช้การเข้าสู่ระบบ Facebook แบบ "คลาสสิก" ให้ทำตามขั้นตอนต่อไปนี้ หรือใช้การเข้าสู่ระบบแบบจำกัดของ Facebook ดังที่แสดงในส่วนถัดไปก็ได้

  1. ผสานรวมการเข้าสู่ระบบด้วย Facebook ในแอปโดยทำตาม เอกสารประกอบของนักพัฒนาแอป เมื่อคุณเริ่มต้นออบเจ็กต์ FBSDKLoginButton ให้กำหนดผู้รับมอบสิทธิ์เพื่อรับเหตุการณ์การเข้าสู่ระบบและออกจากระบบ เช่น

    Swift

    let loginButton = FBSDKLoginButton()
    loginButton.delegate = self
    

    Objective-C

    FBSDKLoginButton *loginButton = [[FBSDKLoginButton alloc] init];
    loginButton.delegate = self;
    
    ในผู้รับมอบสิทธิ์ ให้ใช้ didCompleteWithResult:error:

    Swift

    func loginButton(_ loginButton: FBSDKLoginButton!, didCompleteWith result: FBSDKLoginManagerLoginResult!, error: Error!) {
      if let error = error {
        print(error.localizedDescription)
        return
      }
      // ...
    }
    

    Objective-C

    - (void)loginButton:(FBSDKLoginButton *)loginButton
        didCompleteWithResult:(FBSDKLoginManagerLoginResult *)result
                        error:(NSError *)error {
      if (error == nil) {
        // ...
      } else {
        NSLog(error.localizedDescription);
      }
    }
    
  2. นำเข้าโมดูล FirebaseCore ใน UIApplicationDelegate รวมถึงโมดูล Firebase อื่นๆ ที่ผู้รับมอบสิทธิ์แอปใช้ เช่น วิธีใช้ Cloud Firestore และ Authentication มีดังนี้

    SwiftUI

    import SwiftUI
    import FirebaseCore
    import FirebaseFirestore
    import FirebaseAuth
    // ...
          

    Swift

    import FirebaseCore
    import FirebaseFirestore
    import FirebaseAuth
    // ...
          

    Objective-C

    @import FirebaseCore;
    @import FirebaseFirestore;
    @import FirebaseAuth;
    // ...
          
  3. กำหนดค่าอินสแตนซ์ที่แชร์ของ FirebaseApp ในเมธอด application(_:didFinishLaunchingWithOptions:) ของตัวแทนแอป ดังนี้

    SwiftUI

    // Use Firebase library to configure APIs
    FirebaseApp.configure()

    Swift

    // Use Firebase library to configure APIs
    FirebaseApp.configure()

    Objective-C

    // Use Firebase library to configure APIs
    [FIRApp configure];
  4. หากใช้ SwiftUI คุณต้องสร้างการมอบสิทธิ์แอปพลิเคชันและแนบการมอบสิทธิ์กับโครงสร้าง App ผ่าน UIApplicationDelegateAdaptor หรือ NSApplicationDelegateAdaptor คุณต้องปิดใช้ SWizzing ที่มอบสิทธิ์ของแอปด้วย ดูข้อมูลเพิ่มเติมได้ที่วิธีการของ SwiftUI

    SwiftUI

    @main
    struct YourApp: App {
      // register app delegate for Firebase setup
      @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
    
      var body: some Scene {
        WindowGroup {
          NavigationView {
            ContentView()
          }
        }
      }
    }
          
  5. หลังจากที่ผู้ใช้ลงชื่อเข้าใช้สำเร็จแล้ว ในการใช้งาน didCompleteWithResult:error: ให้รับโทเค็นเพื่อการเข้าถึงสำหรับผู้ใช้ที่ลงชื่อเข้าใช้และแลกเปลี่ยนเป็นข้อมูลเข้าสู่ระบบ Firebase ดังนี้

    Swift

    let credential = FacebookAuthProvider
      .credential(withAccessToken: AccessToken.current!.tokenString)
    

    Objective-C

    FIRAuthCredential *credential = [FIRFacebookAuthProvider
        credentialWithAccessToken:[FBSDKAccessToken currentAccessToken].tokenString];
    

ใช้งานการเข้าสู่ระบบ Facebook Limited

หากต้องการใช้การเข้าสู่ระบบ Facebook แบบจำกัดแทนการเข้าสู่ระบบ Facebook แบบ "คลาสสิก" ให้ทำตามขั้นตอนต่อไปนี้

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

    Swift

    private func randomNonceString(length: Int = 32) -> String {
      precondition(length > 0)
      var randomBytes = [UInt8](repeating: 0, count: length)
      let errorCode = SecRandomCopyBytes(kSecRandomDefault, randomBytes.count, &randomBytes)
      if errorCode != errSecSuccess {
        fatalError(
          "Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)"
        )
      }
    
      let charset: [Character] =
        Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._")
    
      let nonce = randomBytes.map { byte in
        // Pick a random character from the set, wrapping around if needed.
        charset[Int(byte) % charset.count]
      }
    
      return String(nonce)
    }
    
            

    Objective-C

    // Adapted from https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-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--;
          }
        }
      }
    
      return [result copy];
    }
            
    คุณจะต้องส่งแฮช SHA-256 ของ Nonce ไปกับคำขอลงชื่อเข้าใช้ ซึ่ง Facebook จะไม่ส่งโดยไม่มีการเปลี่ยนแปลงในการตอบกลับ Firebase จะตรวจสอบการตอบกลับโดยการแฮชค่าที่ได้จากการแฮชเดิมและเปรียบเทียบกับค่าที่ Facebook ส่งให้

    Swift

    @available(iOS 13, *)
    private func sha256(_ input: String) -> String {
      let inputData = Data(input.utf8)
      let hashedData = SHA256.hash(data: inputData)
      let hashString = hashedData.compactMap {
        String(format: "%02x", $0)
      }.joined()
    
      return hashString
    }
    
            

    Objective-C

    - (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. เมื่อตั้งค่า FBSDKLoginButton ให้กำหนดผู้รับมอบสิทธิ์ให้รับเหตุการณ์การเข้าสู่ระบบและการออกจากระบบ ตั้งค่าโหมดการติดตามเป็น FBSDKLoginTrackingLimited และแนบ Nonce เช่น

    Swift

    func setupLoginButton() {
        let nonce = randomNonceString()
        currentNonce = nonce
        loginButton.delegate = self
        loginButton.loginTracking = .limited
        loginButton.nonce = sha256(nonce)
    }
            

    Objective-C

    - (void)setupLoginButton {
      NSString *nonce = [self randomNonce:32];
      self.currentNonce = nonce;
      self.loginButton.delegate = self;
      self.loginButton.loginTracking = FBSDKLoginTrackingLimited
      self.loginButton.nonce = [self stringBySha256HashingString:nonce];
    }
            
    ในผู้รับมอบสิทธิ์ ให้ใช้ didCompleteWithResult:error:

    Swift

    func loginButton(_ loginButton: FBSDKLoginButton!, didCompleteWith result: FBSDKLoginManagerLoginResult!, error: Error!) {
      if let error = error {
        print(error.localizedDescription)
        return
      }
      // ...
    }
            

    Objective-C

    - (void)loginButton:(FBSDKLoginButton *)loginButton
        didCompleteWithResult:(FBSDKLoginManagerLoginResult *)result
                        error:(NSError *)error {
      if (error == nil) {
        // ...
      } else {
        NSLog(error.localizedDescription);
      }
    }
            
  4. นำเข้าโมดูล FirebaseCore ใน UIApplicationDelegate รวมถึงโมดูล Firebase อื่นๆ ที่ผู้รับมอบสิทธิ์แอปใช้ เช่น วิธีใช้ Cloud Firestore และ Authentication มีดังนี้

    SwiftUI

    import SwiftUI
    import FirebaseCore
    import FirebaseFirestore
    import FirebaseAuth
    // ...
          

    Swift

    import FirebaseCore
    import FirebaseFirestore
    import FirebaseAuth
    // ...
          

    Objective-C

    @import FirebaseCore;
    @import FirebaseFirestore;
    @import FirebaseAuth;
    // ...
          
  5. กำหนดค่าอินสแตนซ์ที่แชร์ของ FirebaseApp ในเมธอด application(_:didFinishLaunchingWithOptions:) ของตัวแทนแอป ดังนี้

    SwiftUI

    // Use Firebase library to configure APIs
    FirebaseApp.configure()

    Swift

    // Use Firebase library to configure APIs
    FirebaseApp.configure()

    Objective-C

    // Use Firebase library to configure APIs
    [FIRApp configure];
  6. หากใช้ SwiftUI คุณต้องสร้างการมอบสิทธิ์แอปพลิเคชันและแนบการมอบสิทธิ์กับโครงสร้าง App ผ่าน UIApplicationDelegateAdaptor หรือ NSApplicationDelegateAdaptor คุณต้องปิดใช้ SWizzing ที่มอบสิทธิ์ของแอปด้วย ดูข้อมูลเพิ่มเติมได้ที่วิธีการของ SwiftUI

    SwiftUI

    @main
    struct YourApp: App {
      // register app delegate for Firebase setup
      @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
    
      var body: some Scene {
        WindowGroup {
          NavigationView {
            ContentView()
          }
        }
      }
    }
          
  7. หลังจากที่ผู้ใช้ลงชื่อเข้าใช้สำเร็จแล้ว ในการใช้งาน didCompleteWithResult:error: ให้ใช้โทเค็นรหัสจากการตอบสนองของ Facebook ที่มีค่าที่ได้จากการแฮชที่ไม่ได้แฮชเพื่อรับข้อมูลเข้าสู่ระบบ Firebase ดังนี้

    Swift

    // Initialize a Firebase credential.
    let idTokenString = AuthenticationToken.current?.tokenString
    let nonce = currentNonce
    let credential = OAuthProvider.credential(withProviderID: "facebook.com",
                                              idToken: idTokenString!,
                                              rawNonce: nonce)
            

    Objective-C

    // Initialize a Firebase credential.
    NSString *idTokenString = FBSDKAuthenticationToken.currentAuthenticationToken.tokenString;
    NSString *rawNonce = self.currentNonce;
    FIROAuthCredential *credential = [FIROAuthProvider credentialWithProviderID:@"facebook.com"
                                                                        IDToken:idTokenString
                                                                       rawNonce:rawNonce];
            

ตรวจสอบสิทธิ์ด้วย Firebase

สุดท้าย ให้ตรวจสอบสิทธิ์กับ Firebase โดยใช้ข้อมูลเข้าสู่ระบบ Firebase ดังนี้

Swift

Auth.auth().signIn(with: credential) { authResult, error in
    if let error = error {
      let authError = error as NSError
      if isMFAEnabled, authError.code == AuthErrorCode.secondFactorRequired.rawValue {
        // The user is a multi-factor user. Second factor challenge is required.
        let resolver = authError
          .userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver
        var displayNameString = ""
        for tmpFactorInfo in resolver.hints {
          displayNameString += tmpFactorInfo.displayName ?? ""
          displayNameString += " "
        }
        self.showTextInputPrompt(
          withMessage: "Select factor to sign in\n\(displayNameString)",
          completionBlock: { userPressedOK, displayName in
            var selectedHint: PhoneMultiFactorInfo?
            for tmpFactorInfo in resolver.hints {
              if displayName == tmpFactorInfo.displayName {
                selectedHint = tmpFactorInfo as? PhoneMultiFactorInfo
              }
            }
            PhoneAuthProvider.provider()
              .verifyPhoneNumber(with: selectedHint!, uiDelegate: nil,
                                 multiFactorSession: resolver
                                   .session) { verificationID, error in
                if error != nil {
                  print(
                    "Multi factor start sign in failed. Error: \(error.debugDescription)"
                  )
                } else {
                  self.showTextInputPrompt(
                    withMessage: "Verification code for \(selectedHint?.displayName ?? "")",
                    completionBlock: { userPressedOK, verificationCode in
                      let credential: PhoneAuthCredential? = PhoneAuthProvider.provider()
                        .credential(withVerificationID: verificationID!,
                                    verificationCode: verificationCode!)
                      let assertion: MultiFactorAssertion? = PhoneMultiFactorGenerator
                        .assertion(with: credential!)
                      resolver.resolveSignIn(with: assertion!) { authResult, error in
                        if error != nil {
                          print(
                            "Multi factor finanlize sign in failed. Error: \(error.debugDescription)"
                          )
                        } else {
                          self.navigationController?.popViewController(animated: true)
                        }
                      }
                    }
                  )
                }
              }
          }
        )
      } else {
        self.showMessagePrompt(error.localizedDescription)
        return
      }
      // ...
      return
    }
    // User is signed in
    // ...
}
    

Objective-C

[[FIRAuth auth] signInWithCredential:credential
                          completion:^(FIRAuthDataResult * _Nullable authResult,
                                       NSError * _Nullable error) {
    if (isMFAEnabled && error && error.code == FIRAuthErrorCodeSecondFactorRequired) {
      FIRMultiFactorResolver *resolver = error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey];
      NSMutableString *displayNameString = [NSMutableString string];
      for (FIRMultiFactorInfo *tmpFactorInfo in resolver.hints) {
        [displayNameString appendString:tmpFactorInfo.displayName];
        [displayNameString appendString:@" "];
      }
      [self showTextInputPromptWithMessage:[NSString stringWithFormat:@"Select factor to sign in\n%@", displayNameString]
                           completionBlock:^(BOOL userPressedOK, NSString *_Nullable displayName) {
       FIRPhoneMultiFactorInfo* selectedHint;
       for (FIRMultiFactorInfo *tmpFactorInfo in resolver.hints) {
         if ([displayName isEqualToString:tmpFactorInfo.displayName]) {
           selectedHint = (FIRPhoneMultiFactorInfo *)tmpFactorInfo;
         }
       }
       [FIRPhoneAuthProvider.provider
        verifyPhoneNumberWithMultiFactorInfo:selectedHint
        UIDelegate:nil
        multiFactorSession:resolver.session
        completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
          if (error) {
            [self showMessagePrompt:error.localizedDescription];
          } else {
            [self showTextInputPromptWithMessage:[NSString stringWithFormat:@"Verification code for %@", selectedHint.displayName]
                                 completionBlock:^(BOOL userPressedOK, NSString *_Nullable verificationCode) {
             FIRPhoneAuthCredential *credential =
                 [[FIRPhoneAuthProvider provider] credentialWithVerificationID:verificationID
                                                              verificationCode:verificationCode];
             FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
             [resolver resolveSignInWithAssertion:assertion completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
               if (error) {
                 [self showMessagePrompt:error.localizedDescription];
               } else {
                 NSLog(@"Multi factor finanlize sign in succeeded.");
               }
             }];
           }];
          }
        }];
     }];
    }
  else if (error) {
    // ...
    return;
  }
  // User successfully signed in. Get user data from the FIRUser object
  if (authResult == nil) { return; }
  FIRUser *user = authResult.user;
  // ...
}];
    

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

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

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

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

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

หากต้องการนำผู้ใช้ออกจากระบบ โปรดโทรหา signOut:

Swift

let firebaseAuth = Auth.auth()
do {
  try firebaseAuth.signOut()
} catch let signOutError as NSError {
  print("Error signing out: %@", signOutError)
}

Objective-C

NSError *signOutError;
BOOL status = [[FIRAuth auth] signOut:&signOutError];
if (!status) {
  NSLog(@"Error signing out: %@", signOutError);
  return;
}

คุณอาจต้องเพิ่มโค้ดการจัดการข้อผิดพลาดสำหรับข้อผิดพลาดในการตรวจสอบสิทธิ์ทั้งหมดด้วย โปรดดูหัวข้อจัดการข้อผิดพลาด