اصالت‌سنجی بااستفاده از ورود به سیستم Facebook در پلاتفرم‌های Apple

با ادغام «ورود به سیستم Facebook» یا «ورود به سیستم محدود Facebook» در برنامه‌تان، می‌توانید به کاربران اجازه دهید بااستفاده از حساب‌های Facebook خود با Firebase اصالت‌سنجی کنند.

قبل از شروع

از «مدیر بسته Swift» برای نصب و مدیریت وابستگی‌های Firebase استفاده کنید.

  1. در Xcode، با باز بودن پروژه برنامه، به File > Add Packages (فایل > افزودن بسته‌ها) پیمایش کنید.
  2. وقتی درخواست شد، مخزن کیت توسعه نرم‌افزار پلاتفرم‌های Firebase Apple را اضافه کنید:
  3.   https://github.com/firebase/firebase-ios-sdk.git
  4. کتابخانه Firebase Authentication را انتخاب کنید.
  5. پرچم -ObjC را به بخش پرچم‌های پیونددهنده دیگر در تنظیمات ساخت هدف اضافه کنید.
  6. پس‌از اتمام، Xcode به‌طور خودکار شروع به حل و بارگیری وابستگی‌های شما در پس‌زمینه می‌کند.

سپس، چند مرحله پیکربندی را انجام دهید:

  1. در سایت Facebook for Developers، شناسه برنامه و رمز برنامه را برای برنامه‌تان دریافت کنید.
  2. «ورود به سیستم با Facebook» را به‌عنوان ارائه‌دهنده ورود به سیستم برای پروژه Firebase خود فعال کنید:
    1. در کنسول Firebase، به امنیت > اصالت‌سنجی بروید.
    2. در برگه روش ورود به سیستم، ارائه‌دهنده ورود به سیستم Facebook را فعال کنید و شناسه برنامه و رمز برنامه را که از Facebook دریافت کرده‌اید مشخص کنید.
    3. مطمئن شوید که شناسه «URI هدایت OAuth» شما (برای مثال، my-app-12345.firebaseapp.com/__/auth/handler) به‌عنوان یکی از شناسه‌های «URI هدایت OAuth» در صفحه تنظیمات برنامه Facebook شما در سایت Facebook for Developers در پیکربندی تنظیمات محصول > ورود به سیستم 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 استفاده می‌کنید، باید نماینده برنامه ایجاد کنید و آن را ازطریق UIApplicationDelegateAdaptor یا NSApplicationDelegateAdaptor به ساختار App پیوست کنید. همچنین باید تغییرات در نماینده برنامه را غیرفعال کنید. برای اطلاعات بیشتر، دستورالعمل‌های 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

برای استفاده از «ورود به سیستم محدود Facebook» به‌جای «ورود به سیستم کلاسیک Facebook»، مراحل زیر را تکمیل کنید.

  1. با دنبال کردن اسناد توسعه‌دهنده، «ورود به سیستم محدود Facebook» را در برنامه‌تان ادغام کنید.
  2. برای هر درخواست ورود به سیستم، یک رشته تصادفی منحصربه‌فرد—یک «نانس»—تولید کنید که از آن برای اطمینان از اینکه شناسه دریافتی شما به‌طور خاص در پاسخ به درخواست اصالت‌سنجی برنامه شما اعطا شده است استفاده خواهید کرد. این مرحله برای جلوگیری از حملات بازپخش مهم است. می‌توانید یک نانس رمزنگاری‌شده ایمن با 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 مقدار یک‌بارمصرف ارسال می‌کنید، که 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 تنظیم کنید، و یک نانس پیوست کنید. برای مثال:

    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 استفاده می‌کنید، باید نماینده برنامه ایجاد کنید و آن را ازطریق UIApplicationDelegateAdaptor یا NSApplicationDelegateAdaptor به ساختار App پیوست کنید. همچنین باید تغییرات در نماینده برنامه را غیرفعال کنید. برای اطلاعات بیشتر، دستورالعمل‌های 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;
}

همچنین ممکن است بخواهید کد مدیریت خطا را برای طیف کامل خطاهای اصالت‌سنجی اضافه کنید. مدیریت خطاها را ببینید.