میتوانید به کاربران اجازه دهید بااستفاده از Apple ID خودشان با Firebase اصالتسنجی کنند، برای این کار باید از «کیت توسعه نرمافزار Firebase» برای انجام جریان ورود به سیستم سرتاسر OAuth 2.0 استفاده کنید.
قبل از شروع
برای ورود به سیستم کاربران بااستفاده از Apple، ابتدا «ورود به سیستم با Apple» را در سایت توسعهدهنده Apple پیکربندی کنید، سپس Apple را بهعنوان ارائهدهنده ورود به سیستم برای پروژه Firebase خود فعال کنید.
به «برنامه توسعهدهندگان Apple» بپیوندید
«ورود به سیستم با Apple» فقط توسط اعضای برنامه توسعهدهندگان Apple قابل پیکربندی است.
پیکربندی «ورود به سیستم با Apple»
- «ورود به سیستم با Apple» را برای برنامهتان در صفحه گواهینامهها، شناسهها، و نمایهها سایت توسعهدهندگان Apple فعال کنید.
- وبسایتتان را همانطور که در بخش اول پیکربندی «ورود به سیستم با Apple» برای وب توضیح داده شده است با برنامهتان مرتبط کنید. وقتی پیامواره نشان داده شد، نشانی وب زیر را بهعنوان «نشانی وب برگشتی» ثبت کنید:
میتوانید شناسه پروژه Firebase خود را در Firebaseصفحه تنظیمات کنسول دریافت کنید. وقتی کارتان تمام شد، «شناسه سرویس» جدیدتان را یادداشت کنید، زیرا در بخش بعدی به آن نیاز خواهید داشت.https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler
- ایجاد کلید خصوصی «ورود به سیستم با Apple». در بخش بعدی به کلید خصوصی جدید و شناسه کلید نیاز خواهید داشت.
- اگر از هریک از ویژگیهای Firebase Authentication که ایمیل به کاربران ارسال میکند استفاده میکنید،
ازجمله ورود به سیستم با پیوند ایمیل، درستیسنجی نشانی ایمیل، لغو تغییر حساب، و
موارد دیگر، خدمات بازارسال ایمیل خصوصی Apple را پیکربندی کنید
و
noreply@YOUR_FIREBASE_PROJECT_ID.firebaseapp.com(یا دامنه الگوی ایمیل سفارشیسازیشده خودتان) را ثبت کنید تا Apple بتواند ایمیلهای ارسالی از Firebase Authentication به نشانیهای ایمیل ناشناس Apple را بازارسال کند.
فعال کردن Apple بهعنوان ارائهدهنده ورود به سیستم
- Firebase را به پروژه Apple خود اضافه کنید. حتماً هنگام راهاندازی برنامه در کنسول Firebase، شناسه دستهای برنامهتان را ثبت کنید.
- در کنسول Firebase، به امنیت > اصالتسنجی بروید.
- در زبانه روش ورود به سیستم، ارائهدهنده ورود به سیستم Apple را فعال کنید. «شناسه سرویس» را که در بخش قبلی ایجاد کردهاید مشخص کنید. همچنین، در بخش پیکربندی جریان کد OAuth، «شناسه تیم Apple» و کلید خصوصی و شناسه کلیدی را که در بخش قبلی ایجاد کردید مشخص کنید.
از الزامات دادههای ناشناسسازیشده Apple پیروی کنید
«ورود به سیستم با Apple» به کاربران این امکان را میدهد که هنگام ورود به سیستم، دادههایشان، ازجمله نشانی ایمیلشان، را ناشناس کنند. کاربرانی که این گزینه را انتخاب میکنند
نشانی ایمیل با دامنه privaterelay.appleid.com دارند. وقتی
از «ورود به سیستم با Apple» در برنامهتان استفاده میکنید، باید از همه
خطمشیها یا شرایط توسعهدهنده ذیربط Apple درخصوص این
شناسههای Apple ناشناس پیروی کنید.
این شامل دریافت رضایت کاربر موردنیاز قبلاز قراردادن هرگونه اطلاعات شخصی که مستقیماً شناساییکننده است درارتباط با «شناسه Apple» ناشناسسازیشده میشود. هنگام استفاده از «اصالتسنجی Firebase»، این ممکن است شامل اقدامات زیر باشد:
- نشانی ایمیل را به Apple ID ناشناس پیوند دهید یا برعکس.
- پیوند دادن شماره تلفن به Apple ID ناشناس یا برعکس
- اعتبار اجتماعی غیرگمنام (Facebook، Google، و غیره) را به Apple ID گمنامشده پیوند دهید یا برعکس.
فهرست بالا کامل نیست. برای اطمینان از اینکه برنامهتان الزامات Apple را برآورده میکند، به «قرارداد پروانه برنامه توسعهدهندگان Apple» در بخش «عضویت» حساب توسعهدهندهتان مراجعه کنید.
ورود به سیستم با Apple و اصالتسنجی با Firebase
برای اصالتسنجی با حساب Apple، ابتدا کاربر را بااستفاده از چارچوب AuthenticationServices Apple به سیستم حساب Apple خود وارد کنید،
و سپس از شناسه توکن در پاسخ Apple برای ایجاد شیء Firebase
AuthCredential استفاده کنید:
برای هر درخواست ورود به سیستم، رشتهای تصادفی—یک «نانس»—تولید کنید که از آن برای اطمینان از اینکه شناسه دریافتی شما بهطور خاص در پاسخ به درخواست اصالتسنجی برنامه شما اعطا شده است استفاده خواهید کرد. این مرحله برای جلوگیری از حملههای بازپخش مهم است.
میتوانید یک نانس رمزنگاریشده ایمن با
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]; }
درخواست ورود به سیستم را با مقدار درهمسازی SHA256 مقدار یکبارمصرف ارسال میکنید که Apple آن را بدون تغییر در پاسخ ارسال خواهد کرد. Firebase پاسخ را با درهمسازی مقدار یکبارمصرف اصلی و مقایسه آن با مقدار ارسالشده توسط Apple اعتبارسنجی میکند.
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; }
جریان ورود به سیستم Apple را شروع کنید، ازجمله در درخواستتان مقدار درهمسازی SHA256 مقدار یکبارمصرف و کلاس نمایندهای که پاسخ Apple را مدیریت خواهد کرد (به مرحله بعدی مراجعه کنید):
Swift
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() }Objective-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]; }پاسخ Apple را در پیادهسازی
ASAuthorizationControllerDelegateمدیریت کنید. اگر ورود به سیستم موفقیتآمیز بود، از شناسه رمز از پاسخ Apple با مقدار یکبارمصرف بدون درهمسازی برای اصالتسنجی با Firebase استفاده کنید:Swift
@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, including the user's full name. let credential = OAuthProvider.appleCredential(withIDToken: idTokenString, rawNonce: nonce, fullName: appleIDCredential.fullName) // 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)") } }Objective-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, including the user's full name. FIROAuthCredential *credential = [FIROAuthProvider appleCredentialWithIDToken:IDToken rawNonce:self.appleRawNonce fullName:appleIDCredential.fullName]; // 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، Apple نشانی وب عکس ارائه نمیدهد.
همچنین، وقتی کاربر انتخاب میکند ایمیلش را با برنامه همرسانی نکند، Apple نشانی ایمیل یکتایی برای آن کاربر (بهشکل xyz@privaterelay.appleid.com) تدارک میبیند که آن را با برنامه شما همرسانی میکند. اگر سرویس رله ایمیل خصوصی را پیکربندی کرده باشید، Apple ایمیلهای ارسالشده به نشانی ناشناس را به نشانی ایمیل واقعی کاربر بازارسال میکند.
احراز هویت مجدد و پیوند حساب
از همین الگو میتوان با reauthenticateWithCredential() استفاده کرد، که میتوانید از آن برای بازیابی اعتبارنامه جدید برای عملیات حساس که نیاز به ورود به سیستم اخیر دارند استفاده کنید:
Swift
// 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.
// ...
}
Objective-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() برای پیوند دادن ارائهدهندگان هویت مختلف به حسابهای موجود استفاده کنید.
توجه داشته باشید که Apple شما را ملزم میکند قبلاز پیوند دادن حسابهای Apple کاربران به دادههای دیگر، رضایت صریح آنها را دریافت کنید.
«ورود به سیستم با Apple» به شما اجازه نمیدهد از اطلاعات اعتباری اصالتسنجی برای پیوند دادن به یک حساب موجود استفاده مجدد کنید. اگر میخواهید اطلاعات اعتباری «ورود به سیستم با Apple» را به حساب دیگری پیوند دهید، ابتدا باید بااستفاده از اطلاعات اعتباری قدیمی «ورود به سیستم با Apple» تلاش کنید حسابها را پیوند دهید و سپس خطای برگشتی را بررسی کنید تا اطلاعات اعتباری جدید را پیدا کنید.
اطلاعات اعتباری جدید در فرهنگ لغت userInfo خطا قرار خواهد گرفت و ازطریق کلید AuthErrorUserInfoUpdatedCredentialKey قابل دسترسی خواهد بود.
برای مثال، برای پیوند دادن حساب Facebook به حساب Firebase کنونی، از رمز دسترسیای که از ورود کاربر به سیستم Facebook دریافت کردهاید استفاده کنید:
Swift
// 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.
// ...
}
Objective-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.
// ...
}];
ابطال کد
طبق الزامات Apple، برنامههایی که از ایجاد حساب پشتیبانی میکنند باید به کاربران اجازه دهند حذف حسابشان را در برنامه شروع کنند، همانطور که در دستورالعملهای بازبینی App Store توضیح داده شده است
برای برآورده کردن این الزام، مراحل زیر را پیادهسازی کنید:
مطمئن شوید که بخش شناسه خدمات و پیکربندی جریان کد OAuth پیکربندی ارائهدهنده «ورود به سیستم با Apple» را همانطور که در بخش پیکربندی «ورود به سیستم با Apple» توضیح داده شده است تکمیل کردهاید.
ازآنجاییکه Firebase وقتی کاربران با «ورود به سیستم با Apple» ایجاد میشوند نشانهای کاربر را ذخیره نمیکند، باید از کاربر بخواهید قبلاز باطل کردن نشان و حذف حسابش دوباره به سیستم وارد شود.
Swift
private var currentNonce: String? private func deleteCurrentUser() { do { let nonce = try CryptoUtils.randomNonceString() currentNonce = nonce let appleIDProvider = ASAuthorizationAppleIDProvider() let request = appleIDProvider.createRequest() request.requestedScopes = [.fullName, .email] request.nonce = CryptoUtils.sha256(nonce) let authorizationController = ASAuthorizationController(authorizationRequests: [request]) authorizationController.delegate = self authorizationController.presentationContextProvider = self authorizationController.performRequests() } catch { // In the unlikely case that nonce generation fails, show error view. displayError(error) } }
کد مجوز را از
ASAuthorizationAppleIDCredentialدریافت کنید و از آن برای فراخوانیAuth.auth().revokeToken(withAuthorizationCode:)بهمنظور لغو ژتونهای کاربر استفاده کنید.Swift
private var user: User? func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { guard let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential else { print("Unable to retrieve AppleIDCredential") return } guard let _ = currentNonce else { fatalError("Invalid state: A login callback was received, but no login request was sent.") } guard let appleAuthCode = appleIDCredential.authorizationCode else { print("Unable to fetch authorization code") return } guard let authCodeString = String(data: appleAuthCode, encoding: .utf8) else { print("Unable to serialize auth code string from data: \(appleAuthCode.debugDescription)") return } Task { do { try await Auth.auth().revokeToken(withAuthorizationCode: authCodeString) try await user?.delete() self.updateUI() } catch { self.displayError(error) } } }
درنهایت، حساب کاربر (و همه دادههای منسوب) را حذف کنید
مراحل بعدی
پساز اینکه کاربر برای اولینبار به سیستم وارد شد، حساب کاربری جدیدی ایجاد میشود و به اطلاعات اعتباری—یعنی نام کاربری و گذرواژه، شماره تلفن، یا اطلاعات ارائهدهنده اصالتسنجی—که کاربر با آن به سیستم وارد شده است پیوند داده میشود. این حساب جدید بهعنوان بخشی از پروژه 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; }
همچنین ممکن است بخواهید کد مدیریت خطا را برای طیف کامل خطاهای اصالتسنجی اضافه کنید. مدیریت خطاها را ببینید.