احراز هویت با استفاده از اپل

می‌توانید با استفاده از Firebase SDK به کاربران خود اجازه دهید با Firebase با استفاده از Apple ID خود احراز هویت کنند تا جریان ورود به سیستم OAuth 2.0 را انجام دهند.

قبل از اینکه شروع کنی

برای ورود کاربرانی که از اپل استفاده می کنند، ابتدا Sign In with Apple را در سایت برنامه نویس اپل پیکربندی کنید، سپس Apple را به عنوان ارائه دهنده ورود به سیستم برای پروژه Firebase خود فعال کنید.

به برنامه توسعه دهندگان اپل بپیوندید

ورود به سیستم با Apple فقط توسط اعضای برنامه توسعه دهنده Apple قابل پیکربندی است.

پیکربندی ورود با اپل

  1. ورود به سیستم با اپل را برای برنامه خود در صفحه گواهی‌ها، شناسه‌ها و نمایه‌های سایت توسعه‌دهنده اپل فعال کنید.
  2. اگر از یکی از ویژگی‌های Firebase Authentication که برای کاربران ایمیل ارسال می‌کند، از جمله ورود به سیستم پیوند ایمیل، تأیید آدرس ایمیل، لغو تغییر حساب و موارد دیگر استفاده می‌کنید، سرویس رله ایمیل خصوصی Apple را پیکربندی کنید و noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com را ثبت کنید (یا دامنه الگوی ایمیل سفارشی شما) تا اپل بتواند ایمیل های ارسال شده توسط Firebase Authentication را به آدرس های ایمیل ناشناس اپل ارسال کند.

اپل را به عنوان ارائه دهنده ورود فعال کنید

  1. Firebase را به پروژه اپل خود اضافه کنید . هنگام تنظیم برنامه خود در کنسول Firebase، حتماً شناسه بسته نرم افزاری خود را ثبت کنید.
  2. در کنسول Firebase ، بخش Auth را باز کنید. در زبانه روش ورود ، ارائه دهنده اپل را فعال کنید. اگر فقط از ورود با اپل در یک برنامه استفاده می‌کنید، می‌توانید فیلدهای شناسه سرویس، شناسه تیم اپل، کلید خصوصی و شناسه کلید را خالی بگذارید.

الزامات داده های ناشناس اپل را رعایت کنید

ورود به سیستم با اپل به کاربران این امکان را می دهد که هنگام ورود به سیستم، داده های خود، از جمله آدرس ایمیل خود را ناشناس کنند. کاربرانی که این گزینه را انتخاب می کنند، آدرس های ایمیل با دامنه privaterelay.appleid.com . هنگامی که از ورود به سیستم با Apple در برنامه خود استفاده می کنید، باید از هر گونه خط مشی یا شرایط توسعه دهنده قابل اجرا از طرف Apple در مورد این شناسه های اپل ناشناس پیروی کنید.

این شامل دریافت هرگونه رضایت کاربر مورد نیاز قبل از مرتبط کردن هر گونه اطلاعات شخصی شناسایی مستقیم با یک شناسه ناشناس اپل است. هنگام استفاده از Firebase Authentication، این ممکن است شامل اقدامات زیر باشد:

  • یک آدرس ایمیل را به یک Apple ID ناشناس پیوند دهید یا برعکس.
  • یک شماره تلفن را به یک Apple ID ناشناس پیوند دهید یا برعکس
  • یک اعتبار اجتماعی غیر ناشناس (فیس بوک، گوگل و غیره) را به یک شناسه اپل ناشناس پیوند دهید یا برعکس.

فهرست فوق کامل نیست. به توافقنامه مجوز برنامه توسعه‌دهنده اپل در بخش عضویت حساب توسعه‌دهنده خود مراجعه کنید تا مطمئن شوید برنامه شما با الزامات اپل مطابقت دارد.

با اپل وارد شوید و با Firebase احراز هویت کنید

برای احراز هویت با حساب اپل، ابتدا کاربر را با استفاده از فریم ورک AuthenticationServices Apple وارد حساب کاربری اپل خود کنید و سپس از کد ID از پاسخ اپل برای ایجاد یک شی Firebase AuthCredential استفاده کنید:

  1. برای هر درخواست ورود به سیستم، یک رشته تصادفی ایجاد کنید - یک "nonce" - که از آن استفاده می کنید تا مطمئن شوید که رمز شناسه دریافتی به طور خاص در پاسخ به درخواست احراز هویت برنامه شما اعطا شده است. این مرحله برای جلوگیری از حملات تکراری مهم است.

    می توانید با SecRandomCopyBytes(_:_:_) یک nonce امن رمزنگاری ایجاد کنید، مانند مثال زیر:

    سریع

    // Adapted from https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce
    private func randomNonceString(length: Int = 32) -> String {
      precondition(length > 0)
      let charset: [Character] =
        Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._")
      var result = ""
      var remainingLength = length
    
      while remainingLength > 0 {
        let randoms: [UInt8] = (0 ..< 16).map { _ in
          var random: UInt8 = 0
          let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random)
          if errorCode != errSecSuccess {
            fatalError(
              "Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)"
            )
          }
          return random
        }
    
        randoms.forEach { random in
          if remainingLength == 0 {
            return
          }
    
          if random < charset.count {
            result.append(charset[Int(random)])
            remainingLength -= 1
          }
        }
      }
    
      return result
    }
    
        

    هدف-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];
    }
        

    شما هش SHA256 nonce را همراه با درخواست ورود خود ارسال می‌کنید که اپل در پاسخ آن را بدون تغییر ارسال می‌کند. Firebase پاسخ را با هش کردن nonce اصلی و مقایسه آن با مقدار ارسال شده توسط Apple تأیید می کند.

    سریع

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

    هدف-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;
    }
        
  2. جریان ورود به سیستم اپل را شروع کنید، از جمله در درخواست خود هش SHA256 از nonce و کلاس نماینده که پاسخ اپل را مدیریت می کند (مرحله بعدی را ببینید):

    سریع

    import CryptoKit
    
    // Unhashed nonce.
    fileprivate var currentNonce: String?
    
    @available(iOS 13, *)
    func startSignInWithAppleFlow() {
      let nonce = randomNonceString()
      currentNonce = nonce
      let appleIDProvider = ASAuthorizationAppleIDProvider()
      let request = appleIDProvider.createRequest()
      request.requestedScopes = [.fullName, .email]
      request.nonce = sha256(nonce)
    
      let authorizationController = ASAuthorizationController(authorizationRequests: [request])
      authorizationController.delegate = self
      authorizationController.presentationContextProvider = self
      authorizationController.performRequests()
    }
    

    هدف-C

    @import CommonCrypto;
    
    - (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];
    }
    
  3. در اجرای ASAuthorizationControllerDelegate ، پاسخ اپل را مدیریت کنید. اگر ورود به سیستم موفقیت آمیز بود، برای احراز هویت با Firebase، از کد شناسه پاسخ اپل با نون هش نشده استفاده کنید:

    سریع

    @available(iOS 13.0, *)
    extension MainViewController: ASAuthorizationControllerDelegate {
    
      func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
        if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
          guard let nonce = currentNonce else {
            fatalError("Invalid state: A login callback was received, but no login request was sent.")
          }
          guard let appleIDToken = appleIDCredential.identityToken else {
            print("Unable to fetch identity token")
            return
          }
          guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else {
            print("Unable to serialize token string from data: \(appleIDToken.debugDescription)")
            return
          }
          // Initialize a Firebase credential.
          let credential = OAuthProvider.credential(withProviderID: "apple.com",
                                                    IDToken: idTokenString,
                                                    rawNonce: nonce)
          // Sign in with Firebase.
          Auth.auth().signIn(with: credential) { (authResult, error) in
            if error {
              // Error. If error.code == .MissingOrInvalidNonce, make sure
              // you're sending the SHA256-hashed nonce as a hex string with
              // your request to Apple.
              print(error.localizedDescription)
              return
            }
            // User is signed in to Firebase with Apple.
            // ...
          }
        }
      }
    
      func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
        // Handle error.
        print("Sign in with Apple errored: \(error)")
      }
    
    }
    

    هدف-C

    - (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);
        }
    
        // Initialize a Firebase credential.
        FIROAuthCredential *credential = [FIROAuthProvider credentialWithProviderID:@"apple.com"
                                                                            IDToken:idToken
                                                                           rawNonce:rawNonce];
    
        // Sign in with Firebase.
        [[FIRAuth auth] signInWithCredential:credential
                                  completion:^(FIRAuthDataResult * _Nullable authResult,
                                               NSError * _Nullable error) {
          if (error != nil) {
            // Error. If error.code == FIRAuthErrorCodeMissingOrInvalidNonce,
            // make sure you're sending the SHA256-hashed nonce as a hex string
            // with your request to Apple.
            return;
          }
          // Sign-in succeeded!
        }];
      }
    }
    
    - (void)authorizationController:(ASAuthorizationController *)controller
               didCompleteWithError:(NSError *)error API_AVAILABLE(ios(13.0)) {
      NSLog(@"Sign in with Apple errored: %@", error);
    }
    

برخلاف سایر ارائه دهندگان پشتیبانی شده توسط Firebase Auth، اپل URL عکس ارائه نمی کند.

همچنین، وقتی کاربر تصمیم می‌گیرد ایمیل خود را با برنامه به اشتراک نگذارد، اپل یک آدرس ایمیل منحصربه‌فرد برای آن کاربر ارائه می‌کند (به شکل xyz@privaterelay.appleid.com )، که آن را با برنامه شما به اشتراک می‌گذارد. اگر سرویس ارسال ایمیل خصوصی را پیکربندی کرده باشید، اپل ایمیل های ارسال شده به آدرس ناشناس را به آدرس ایمیل واقعی کاربر فوروارد می کند.

اپل تنها اولین باری که کاربر وارد سیستم می شود، اطلاعات کاربر مانند نام نمایشی را با برنامه ها به اشتراک می گذارد. معمولاً، Firebase نام نمایشی را در اولین باری که کاربر با اپل وارد می شود، ذخیره می کند که می توانید با Auth.auth().currentUser.displayName با این حال، اگر قبلاً از اپل برای ورود کاربر به برنامه بدون استفاده از Firebase استفاده کرده‌اید، اپل نام نمایشی کاربر را به Firebase ارائه نمی‌کند.

احراز هویت مجدد و پیوند دادن حساب

از همین الگو می‌توان با reauthenticateWithCredential() استفاده کرد، که می‌توانید برای بازیابی یک اعتبار جدید برای عملیات حساسی که نیاز به ورود اخیر دارند استفاده کنید:

سریع

// Initialize a fresh Apple credential with Firebase.
let credential = OAuthProvider.credential(
  withProviderID: "apple.com",
  IDToken: appleIdToken,
  rawNonce: rawNonce
)
// Reauthenticate current Apple user with fresh Apple credential.
Auth.auth().currentUser.reauthenticate(with: credential) { (authResult, error) in
  guard error != nil else { return }
  // Apple user successfully re-authenticated.
  // ...
}

هدف-C

FIRAuthCredential *credential = [FIROAuthProvider credentialWithProviderID:@"apple.com",
                                                                   IDToken:appleIdToken,
                                                                  rawNonce:rawNonce];
[[FIRAuth auth].currentUser
    reauthenticateWithCredential:credential
                      completion:^(FIRAuthDataResult * _Nullable authResult,
                                   NSError * _Nullable error) {
  if (error) {
    // Handle error.
  }
  // Apple user successfully re-authenticated.
  // ...
}];

و، می توانید از linkWithCredential() برای پیوند دادن ارائه دهندگان هویت مختلف به حساب های موجود استفاده کنید.

توجه داشته باشید که اپل از شما می‌خواهد قبل از اینکه حساب‌های اپل آن‌ها را به داده‌های دیگر پیوند دهید، رضایت صریح از کاربران دریافت کنید.

ورود به سیستم با اپل به شما اجازه استفاده مجدد از اعتبارنامه اعتباری برای پیوند دادن به یک حساب موجود را نمی دهد. اگر می‌خواهید یک Sign in با اعتبار اپل را به حساب دیگری پیوند دهید، ابتدا باید سعی کنید حساب‌ها را با استفاده از Sign in قدیمی با اعتبار Apple پیوند دهید و سپس خطای بازگشتی را بررسی کنید تا یک اعتبار جدید پیدا کنید. اعتبار جدید در فرهنگ لغت userInfo خطا قرار می گیرد و می توان از طریق کلید AuthErrorUserInfoUpdatedCredentialKey به آن دسترسی داشت.

به عنوان مثال، برای پیوند دادن یک حساب فیس بوک به حساب فعلی Firebase، از نشانه دسترسی که از ورود کاربر به فیس بوک دریافت کرده اید استفاده کنید:

سریع

// Initialize a Facebook credential with Firebase.
let credential = FacebookAuthProvider.credential(
  withAccessToken: AccessToken.current!.tokenString
)
// Assuming the current user is an Apple user linking a Facebook provider.
Auth.auth().currentUser.link(with: credential) { (authResult, error) in
  // Facebook credential is linked to the current Apple user.
  // The user can now sign in with Facebook or Apple to the same Firebase
  // account.
  // ...
}

هدف-C

// Initialize a Facebook credential with Firebase.
FacebookAuthCredential *credential = [FIRFacebookAuthProvider credentialWithAccessToken:accessToken];
// Assuming the current user is an Apple user linking a Facebook provider.
[FIRAuth.auth linkWithCredential:credential completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
  // Facebook credential is linked to the current Apple user.
  // The user can now sign in with Facebook or Apple to the same Firebase
  // account.
  // ...
}];

مراحل بعدی

پس از اینکه کاربر برای اولین بار وارد سیستم شد، یک حساب کاربری جدید ایجاد می‌شود و به اعتبارنامه‌ها - یعنی نام کاربری و رمز عبور، شماره تلفن یا اطلاعات ارائه‌دهنده تاییدیه - پیوند داده می‌شود که کاربر با آن وارد شده است. این حساب جدید به‌عنوان بخشی از پروژه Firebase شما ذخیره می‌شود و می‌توان از آن برای شناسایی کاربر در همه برنامه‌های پروژه شما، صرف نظر از نحوه ورود کاربر به سیستم استفاده کرد.

  • در برنامه های خود، می توانید اطلاعات اولیه نمایه کاربر را از شی User دریافت کنید. به مدیریت کاربران مراجعه کنید.

  • در قوانین امنیتی Firebase Realtime Database و Cloud Storage، می‌توانید شناسه کاربری منحصربه‌فرد کاربر واردشده به سیستم را از متغیر auth دریافت کنید و از آن برای کنترل داده‌هایی که کاربر می‌تواند به آن دسترسی داشته باشد استفاده کنید.

می‌توانید به کاربران اجازه دهید با استفاده از چندین ارائه‌دهنده احراز هویت، با پیوند دادن اعتبار ارائه‌دهنده تأیید اعتبار به یک حساب کاربری موجود، به برنامه شما وارد شوند.

برای خروج از سیستم یک کاربر، با signOut: .

سریع

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

هدف-C

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

همچنین ممکن است بخواهید کد رسیدگی به خطا را برای طیف کامل خطاهای احراز هویت اضافه کنید. به رسیدگی به خطاها مراجعه کنید.