Autenticar usando a Apple

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

É possível usar o SDK do Firebase para permitir que os usuários façam a autenticação com o Firebase usando um ID Apple para realizar todo o fluxo de login do OAuth 2.0.

Antes de começar

Para fazer login com usuários usando a Apple, primeiro configure o recurso Iniciar sessão com a Apple no site do desenvolvedor da Apple e a ative 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.

Como configurar o recurso "Iniciar sessão com a Apple"

  1. 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.
  2. Se você usar qualquer um dos recursos do Firebase Authentication que enviam e-mails para os usuários, incluindo login com o link de e-mail, verificação de endereço de e-mail, revogação de alteração de conta e outros, configure o serviço de retransmissão 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 retransmitir e-mails enviados pelo Firebase Authentication para endereços anônimos de e-mail da Apple.

Ativar a Apple como um provedor de login

  1. Adicione o Firebase ao projeto da Apple. Registre o ID do pacote do seu aplicativo ao configurá-lo no Console do Firebase.
  2. No Console do Firebase, abra a seção Auth. Na guia Método de login, ative o provedor Apple. Se você estiver usando o recurso Iniciar sessão com a Apple apenas em um aplicativo, deixe vazios os campos "ID de serviço", "ID da equipe da Apple", "Chave privada" e "ID da chave".

Obedeça aos 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 ou vice-versa.
  • Vincular um número de telefone a um ID Apple anônimo ou vice-versa.
  • Vincular uma credencial social não anônima (Facebook, Google etc.) a um ID Apple anônimo 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:

  1. 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

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

    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;
    }
        
  2. 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];
    }
    
  3. 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.
          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);
    }
    

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 e-mails com o aplicativo, a Apple fornece um endereço de e-mail exclusivo para esse usuário, no formato xyz@privaterelay.appleid.com, que compartilha com seu aplicativo. Se você tiver configurado o serviço de retransmissão de e-mail privado, a Apple encaminhará os e-mails enviados para o endereço anônimo para o endereço de e-mail real do usuário.

A Apple só compartilha informações do usuário, como o nome de exibição, com apps na primeira vez que um usuário faz login. Normalmente, o Firebase armazena o nome de exibição na primeira vez que um usuário faz login com a Apple, que você pode obter com Auth.auth().currentUser.displayName. No entanto, se você já usou a Apple para fazer login com um usuário no app sem usar o Firebase, a Apple não fornecerá o nome de exibição 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.
  // ...
}];

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 User nos seus apps. Consulte Gerenciar usuários.

  • Nas Regras de segurança do Firebase Realtime Database e do Cloud Storage, é possível acessar na variável auth o ID exclusivo do usuário que fez login e usar esse ID para controlar quais dados uma pessoa 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 Solucionar erros.