Signing in users with Apple on iOS
This document shows you how to use Identity Platform to add Sign in with Apple to your iOS app.
Before you begin
Create an iOS app that uses Identity Platform.
Join the Apple Developer Program.
Configuring your app with Apple
On the Apple Developer site:
Enable Sign In with Apple for your app.
If you use Identity Platform to send emails to your users, Configure your project with Apple's private email relay service using the following email:
noreply@project-id.firebaseapp.com
You can also use a custom email template, if your app has one.
Complying with Apple's anonymized data requirements
Apple gives users the option of anonymizing their data, including
their email address. Apple assigns users who select this option an obfuscated
email address with the domain privaterelay.appleid.com
.
Your app must comply with any applicable developer policies or terms from Apple regarding anonymized Apple IDs. This includes obtaining user consent before associating any personally identifying information (PII) with an anonymized Apple ID. Actions that involve PII include, but are not limited to:
- Linking an email address to an anonymized Apple ID, or vice versa.
- Linking a phone number to an anonymized Apple ID, or vice versa
- Linking a non-anonymous social credential, such as Facebook or Google, to to anonymized Apple ID, or vice versa.
For more information, refer to the Apple Developer Program License Agreement for your Apple developer account.
Configuring Apple as a provider
To configure Apple as an identity provider:
Go to the Identity Providers page in the Google Cloud console.
Click Add A Provider.
Select Apple from the list.
Under Platform, select iOS.
Enter your app's Buldle ID.
Register your app's domains by clicking Add domain under Authorized Domains. For development purposes,
localhost
is already enabled by default.Under Configure your application, click iOS. Copy the snippet into your app's code to initialize the Identity Platform Client SDK.
Click Save.
Signing in users with the Client SDK
Sign in the user and obtain an ID token using Apple's Authentication Services framework.
Generate a random string, known as a nonce, by calling
SecRandomCopyBytes(_:_:_:)
.The nonce is used to prevent replay attacks. You include the SHA-256 hash of your nonce in your authentication request, and Apple returns it, unmodified, in the response. Identity Platform then validates the response by comparing the original hash to the value returned by Apple.
Start Apple's sign-in flow, including the SHA-256 hash of the nonce you created in the previous step, and a delegate class to handle Apple's response:
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() } @available(iOS 13, *) private func sha256(_ input: String) -> String { let inputData = Data(input.utf8) let hashedData = SHA256.hash(data: inputData) let hashString = hashedData.compactMap { return String(format: "%02x", $0) }.joined() return hashString }
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]; } - (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; }
Handle Apple's response in your implementation of
ASAuthorizationControllerDelegate
. If sign-in succeeds, use the ID token from Apple's response with the unhashed nonce to authenticate with Identity Platform: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. 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)") } }
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. 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); }
Unlike many other identity providers, Apple does not provide a photo URL.
If a user chooses not to share their real email with your app, Apple provisions
a unique email address for that user to share instead. This email takes the form
xyz@privaterelay.appleid.com
. If you configured the private email relay
service, Apple forwards emails sent to the anonymized address to the user's real
email address.
Apple only shares user information, such as display names, with apps the
first time a user signs in. In most cases, Identity Platform stores this
data, which lets you fetch it using firebase.auth().currentUser.displayName
during future sessions. However, if you allowed users to sign into your app
using Apple before integrating with Identity Platform, user information
is not available.
What's next
- Learn more about Identity Platform users.
- Sign in users with other identity providers.