Você pode usar o SDK do Firebase para permitir que os usuários façam a autenticação com o Firebase usando o ID Apple para realizar todo o fluxo de login do OAuth 2.0.
Antes de começar
Para fazer login de usuários usando a Apple, primeiro configure o recurso Iniciar sessão com a Apple no site do desenvolvedor da Apple e ative-a como um provedor de login para seu projeto do Firebase.
Participar do Programa para desenvolvedores da Apple
O recurso Iniciar sessão com a Apple só pode ser configurado por membros do Programa para desenvolvedores da Apple.
Configurar o recurso "Iniciar sessão com a Apple"
- Ative o recurso Iniciar sessão com a Apple para seu app na página Certificados, Identificadores e Perfis do site do desenvolvedor da Apple.
- Associe seu site ao aplicativo conforme descrito na primeira seção de Configurar Login com a Apple para a Web. Quando solicitado, registre o URL a seguir como um URL de retorno:
Você pode acessar o ID do projeto do Firebase na página de configurações do console do Firebase. Quando terminar, anote o novo ID de serviço, que será necessário na próxima seção.https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler
- Crie uma chave privada de Início de sessão com a Apple. Você vai precisar da sua nova chave privada e do ID da chave na próxima seção.
- Se você usar qualquer um dos recursos do Firebase Authentication que enviam e-mails para os usuários,
incluindo o login por link de e-mail, a verificação de endereço de e-mail, a revogação de alterações
na conta, entre outros,
configure o serviço de redirecionamento de e-mail privado da Apple
e registre
noreply@YOUR_FIREBASE_PROJECT_ID.firebaseapp.com(ou seu domínio de modelo de e-mail personalizado) para que a Apple possa redirecionar os e-mails enviados pelo Firebase Authentication para endereços de e-mail anônimos da Apple.
Ativar a Apple como um provedor de login
- Adicione o Firebase ao projeto da Apple. Registre o ID do pacote do app ao configurá-lo no console do Firebase.
- No console do Firebase, abra a seção Autenticação. Na guia Método de login, ative o provedor da Apple. Especifique o ID do serviço que você criou na seção anterior. Além disso, na seção de configuração do fluxo de código do OAuth, especifique o ID de equipe da Apple, a chave privada e o ID da chave que você criou na seção anterior.
Atenda os requisitos de dados anônimos da Apple
O recurso Iniciar sessão com a Apple permite que os usuários decidam se querem que seus dados fiquem anônimos,
incluindo o endereço de e-mail, ao fazer login. Os usuários que escolhem essa opção
têm endereços de e-mail com o domínio privaterelay.appleid.com. Ao
usar o recurso Iniciar sessão com a Apple no seu aplicativo, você precisa estar em conformidade com todos os
termos ou políticas do desenvolvedor aplicáveis da Apple relacionados a esses IDs anônimos
da Apple.
Isso inclui obter o consentimento do usuário exigido antes de associar qualquer informação pessoal de identificação direta a um ID anônimo da Apple. O uso do Firebase Authentication pode incluir as seguintes ações:
- Vincular um endereço de e-mail a um ID Apple anonimizado ou vice-versa.
- Vincular um número de telefone a um ID Apple anonimizado ou vice-versa.
- Vincular uma credencial social não anônima (Facebook, Google etc.) a um ID Apple anonimizado ou vice-versa.
Essa não é uma lista completa. Consulte o Contrato de licença do programa para desenvolvedores da Apple na seção de associação da sua conta de desenvolvedor para verificar se o aplicativo atende aos requisitos da Apple.
Usar o recurso "Iniciar sessão com a Apple" e fazer a autenticação com o Firebase
Para autenticar com uma conta da Apple, primeiro faça login com ela usando o
framework AuthenticationServices
da Apple e, em seguida, use o token de ID da resposta da Apple para criar um objeto
AuthCredential do Firebase:
Para cada solicitação de login, gere uma string aleatória, um valor de uso único que será usado para garantir que o token de ID recebido seja concedido especificamente em resposta à solicitação de autenticação do aplicativo. Essa etapa é importante para evitar ataques repetidos.
É possível gerar um valor de uso único criptograficamente seguro com
SecRandomCopyBytes(_:_:_), como no exemplo a seguir: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]; }
Você enviará a hash SHA256 do valor de uso único com sua solicitação de login, que será transmitida pela Apple sem alterações na resposta. O Firebase valida a resposta fazendo o hash do valor de uso único original e comparando-o ao valor transmitido pela 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; }
Inicie o fluxo de login da Apple, incluindo na solicitação a hash SHA256 do valor de uso único e a classe delegada que processará a resposta da Apple (consulte a próxima etapa):
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]; }Processe a resposta da Apple em sua implementação de
ASAuthorizationControllerDelegate. Se o login foi bem-sucedido, use o token de ID da resposta da Apple com o valor de uso único sem hash para fazer a autenticação com o 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); }
Diferentemente de outros provedores compatíveis com o Firebase Auth, a Apple não fornece um URL de foto.
Além disso, quando o usuário opta por não compartilhar o e-mail com o app, a Apple
provisiona um endereço de e-mail exclusivo para esse usuário, no formato
xyz@privaterelay.appleid.com, que é compartilhado com seu app. Se você
tiver configurado o serviço de redirecionamento de e-mail privado, a Apple encaminhará os e-mails
enviados ao endereço anonimizado para o endereço de e-mail real do usuário.
Reautenticação e vinculação de contas
O mesmo padrão pode ser usado com reauthenticateWithCredential(), que você pode usar para recuperar uma nova credencial para operações confidenciais que exigem login recente:
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.
// ...
}];
E você pode usar linkWithCredential() para vincular diferentes provedores de identidade a
contas atuais.
Observe que a Apple exige que você receba consentimento explícito dos usuários antes de vincular as contas da Apple a outros dados.
O recurso Iniciar sessão com a Apple não permitirá que você reutilize uma credencial de autenticação para vincular a uma conta existente. Se você quiser vincular uma credencial do recurso Iniciar sessão com a Apple a outra conta, primeiro tente vincular as contas usando a antiga credencial desse recurso e, em seguida, examine o erro retornado para encontrar uma nova credencial.
A nova credencial estará localizada no dicionário userInfo do erro e poderá ser acessada por meio da chave AuthErrorUserInfoUpdatedCredentialKey.
Por exemplo, para vincular uma conta do Facebook à conta atual do Firebase, use o token de acesso de início de sessão do usuário no 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.
// ...
}];
Revogação de token
A Apple exige que os apps compatíveis com a criação de contas permitam que os usuários iniciem a exclusão da conta no app, conforme descrito nas Diretrizes de avaliação da App Store.
Para atender a esse requisito, implemente as seguintes etapas:
Verifique se você preencheu as seções ID do serviço e Configuração do fluxo do código OAuth da configuração de provedor de login com a Apple, conforme descrito em Configurar o recurso "Iniciar sessão com a Apple".
Como o Firebase não armazena tokens de usuário quando os usuários são criados com o recurso "Iniciar sessão com a Apple", você precisa solicitar que o usuário faça login novamente antes de revogar o token e excluir a conta.
Swift
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) } }
Consiga o código de autorização do
ASAuthorizationAppleIDCredentiale use-o para chamarAuth.auth().revokeToken(withAuthorizationCode:)para revogar os tokens do usuário.Swift
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) } } }
Por fim, exclua a conta de usuário e todos os dados associados a ela.
Próximas etapas
Depois que um usuário faz login pela primeira vez, uma nova conta de usuário é criada e vinculada às credenciais, que podem ser o número do telefone, o nome de usuário e a senha ou as informações do provedor de autenticação. Essa nova conta é armazenada como parte do projeto do Firebase e pode ser usada para identificar um usuário em todos os apps do projeto, seja qual for o método de login utilizado.
-
É possível receber as informações básicas de perfil do usuário do objeto
Usernos seus apps. Consulte Gerenciar usuários. Nas Regras de segurança Firebase Realtime Database e Cloud Storage, você pode acessar o ID exclusivo do usuário conectado pela variável
authe usar essas informações para controlar quais dados um usuário pode acessar.
Os usuários podem fazer login no app usando vários provedores de autenticação. Basta vincular as credenciais desses provedores a uma conta de usuário.
Para desconectar um usuário, chame
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; }
Adicione também o código de tratamento de erros para todo o intervalo de erros de autenticação. Consulte Tratamento de erros.