Inicio de sesión de usuarios con Facebook en iOS

Puedes permitir que tus usuarios se autentiquen con Identity Platform usando sus cuentas de Facebook integrando Inicio de sesión con Facebook o Inicio de sesión limitado con Facebook en tu aplicación. En esta página se explica cómo usar Identity Platform para añadir la opción de iniciar sesión con Facebook a tu aplicación iOS.

Antes de empezar

  1. Añade Firebase a tu proyecto de iOS.

  2. Incluye los siguientes pods en tu Podfile:

    pod 'FirebaseAuth'
    
  3. Ve a la página Proveedores de identidades de la consola de Google Cloud .

    Ir a la página Proveedores de identidades

  4. Haz clic en Add A Provider (Añadir proveedor).

  5. Selecciona Facebook en la lista.

  6. Introduce tu ID de aplicación y tu secreto de aplicación de Facebook. Si aún no tienes un ID y un secreto, puedes obtenerlos en la página Facebook for Developers.

  7. Configura el URI que aparece en Configurar Facebook como un URI de redirección de OAuth válido para tu aplicación de Facebook. Si has configurado un dominio personalizado en Identity Platform, actualiza el URI de redirección en la configuración de tu aplicación de Facebook para que use el dominio personalizado en lugar del dominio predeterminado. Por ejemplo, cambia https://myproject.firebaseapp.com/__/auth/handler por https://auth.myownpersonaldomain.com/__/auth/handler.

  8. Para registrar los dominios de tu aplicación, haz clic en Añadir dominio en Dominios autorizados. Para las actividades de desarrollo, localhost ya está habilitado de forma predeterminada.

  9. En Configura tu aplicación, haz clic en Detalles de la configuración. Copia el fragmento en el código de tu aplicación para inicializar el SDK de cliente de Identity Platform.

  10. Haz clic en Guardar.

Implementar el inicio de sesión con Facebook

Para usar el inicio de sesión de Facebook clásico, sigue estos pasos. También puedes usar el inicio de sesión limitado de Facebook, como se muestra en la siguiente sección.

  1. Integra Inicio de sesión con Facebook en tu aplicación siguiendo la documentación para desarrolladores. Cuando inicialices el objeto FBSDKLoginButton, asigna un delegado para recibir eventos de inicio y cierre de sesión. Por ejemplo:

    Swift

    let loginButton = FBSDKLoginButton()
    loginButton.delegate = self

    Objective‑C

    FBSDKLoginButton *loginButton = [[FBSDKLoginButton alloc] init];
    loginButton.delegate = self;
    En tu delegado, implementa didCompleteWithResult:error:.

    Swift

    func loginButton(_ loginButton: FBSDKLoginButton!, didCompleteWith result: FBSDKLoginManagerLoginResult!, error: Error!) {
      if let error = error {
        print(error.localizedDescription)
        return
      }
      // ...
    }

    Objective‑C

    - (void)loginButton:(FBSDKLoginButton *)loginButton
        didCompleteWithResult:(FBSDKLoginManagerLoginResult *)result
                        error:(NSError *)error {
      if (error == nil) {
        // ...
      } else {
        NSLog(error.localizedDescription);
      }
    }
  2. Importa el módulo de Firebase a tu UIApplicationDelegate:

    Swift

    import FirebaseCore
    import FirebaseAuth
          

    Objective‑C

    @import FirebaseCore;
    @import FirebaseAuth;
          
  3. Configura una instancia compartida de FirebaseApp; normalmente, en el método application:didFinishLaunchingWithOptions: de tu aplicación:

    Swift

    // Use Firebase library to configure APIs
    FirebaseApp.configure()

    Objective‑C

    // Use Firebase library to configure APIs
    [FIRApp configure];
  4. Una vez que un usuario haya iniciado sesión correctamente, en tu implementación de didCompleteWithResult:error:, obtén un token de acceso para el usuario que haya iniciado sesión e intercámbialo por una credencial de Identity Platform:

    Swift

    let credential = FacebookAuthProvider
      .credential(withAccessToken: AccessToken.current!.tokenString)

    Objective‑C

    FIRAuthCredential *credential = [FIRFacebookAuthProvider
        credentialWithAccessToken:[FBSDKAccessToken currentAccessToken].tokenString];

Implementar el inicio de sesión limitado de Facebook

Para usar el inicio de sesión limitado de Facebook en lugar del inicio de sesión de Facebook "clásico", sigue estos pasos.

  1. Integra el inicio de sesión limitado de Facebook en tu aplicación siguiendo la documentación para desarrolladores.
  2. Por cada solicitud de inicio de sesión, genera una cadena aleatoria única (un "nonce") que usarás para asegurarte de que el token de ID que obtengas se haya concedido específicamente en respuesta a la solicitud de autenticación de tu aplicación. Este paso es importante para evitar ataques de repetición. Puedes generar un nonce criptográficamente seguro en iOS con SecRandomCopyBytes(_:_:_), como en el siguiente ejemplo:

    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];
    }
            
    Enviarás el hash SHA-256 del nonce con tu solicitud de inicio de sesión, que Facebook enviará sin modificar en la respuesta. Identity Platform valida la respuesta cifrando el nonce original y comparándolo con el valor que ha enviado Facebook.

    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;
    }
            
  3. Cuando configures el FBSDKLoginButton, asigna un delegado para recibir eventos de inicio y cierre de sesión, define el modo de seguimiento como FBSDKLoginTrackingLimited y adjunta un nonce. Por ejemplo:

    Swift

    func setupLoginButton() {
        let nonce = randomNonceString()
        currentNonce = nonce
        loginButton.delegate = self
        loginButton.loginTracking = .limited
        loginButton.nonce = sha256(nonce)
    }
            

    Objective‑C

    - (void)setupLoginButton {
      NSString *nonce = [self randomNonce:32];
      self.currentNonce = nonce;
      self.loginButton.delegate = self;
      self.loginButton.loginTracking = FBSDKLoginTrackingLimited
      self.loginButton.nonce = [self stringBySha256HashingString:nonce];
    }
            
    En tu delegado, implementa didCompleteWithResult:error:.

    Swift

    func loginButton(_ loginButton: FBSDKLoginButton!, didCompleteWith result: FBSDKLoginManagerLoginResult!, error: Error!) {
      if let error = error {
        print(error.localizedDescription)
        return
      }
      // ...
    }
            

    Objective‑C

    - (void)loginButton:(FBSDKLoginButton *)loginButton
        didCompleteWithResult:(FBSDKLoginManagerLoginResult *)result
                        error:(NSError *)error {
      if (error == nil) {
        // ...
      } else {
        NSLog(error.localizedDescription);
      }
    }
            
  4. Importa el módulo de Firebase a tu UIApplicationDelegate:

    Swift

    import Firebase

    Objective‑C

    @import Firebase;
  5. Configura una instancia compartida de FirebaseApp; normalmente, en el método application:didFinishLaunchingWithOptions: de tu aplicación:

    Swift

    // Use Firebase library to configure APIs
    FirebaseApp.configure()

    Objective‑C

    // Use Firebase library to configure APIs
    [FIRApp configure];
  6. Cuando un usuario inicie sesión correctamente en tu implementación de didCompleteWithResult:error:, usa el token de ID de la respuesta de Facebook con el nonce sin cifrar para obtener una credencial de Identity Platform:

    Swift

    // Initialize an Identity Platform credential.
    let idTokenString = AuthenticationToken.current?.tokenString
    let nonce = currentNonce
    let credential = OAuthProvider.credential(withProviderID: "facebook.com",
                                              IDToken: idTokenString,
                                              rawNonce: nonce)
            

    Objective‑C

    // Initialize an Identity Platform credential.
    NSString *idTokenString = FBSDKAuthenticationToken.currentAuthenticationToken.tokenString;
    NSString *rawNonce = self.currentNonce;
    FIROAuthCredential *credential = [FIROAuthProvider credentialWithProviderID:@"facebook.com"
                                                                        IDToken:idTokenString
                                                                       rawNonce:rawNonce];
            

Autenticar con Identity Platform

Por último, autentica con Identity Platform mediante la credencial de Identity Platform:

Swift

Auth.auth().signIn(with: credential) { authResult, error in
    if let error = error {
      let authError = error as NSError
      if isMFAEnabled, authError.code == AuthErrorCode.secondFactorRequired.rawValue {
        // The user is a multi-factor user. Second factor challenge is required.
        let resolver = authError
          .userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver
        var displayNameString = ""
        for tmpFactorInfo in resolver.hints {
          displayNameString += tmpFactorInfo.displayName ?? ""
          displayNameString += " "
        }
        self.showTextInputPrompt(
          withMessage: "Select factor to sign in\n\(displayNameString)",
          completionBlock: { userPressedOK, displayName in
            var selectedHint: PhoneMultiFactorInfo?
            for tmpFactorInfo in resolver.hints {
              if displayName == tmpFactorInfo.displayName {
                selectedHint = tmpFactorInfo as? PhoneMultiFactorInfo
              }
            }
            PhoneAuthProvider.provider()
              .verifyPhoneNumber(with: selectedHint!, uiDelegate: nil,
                                 multiFactorSession: resolver
                                   .session) { verificationID, error in
                if error != nil {
                  print(
                    "Multi factor start sign in failed. Error: \(error.debugDescription)"
                  )
                } else {
                  self.showTextInputPrompt(
                    withMessage: "Verification code for \(selectedHint?.displayName ?? "")",
                    completionBlock: { userPressedOK, verificationCode in
                      let credential: PhoneAuthCredential? = PhoneAuthProvider.provider()
                        .credential(withVerificationID: verificationID!,
                                    verificationCode: verificationCode!)
                      let assertion: MultiFactorAssertion? = PhoneMultiFactorGenerator
                        .assertion(with: credential!)
                      resolver.resolveSignIn(with: assertion!) { authResult, error in
                        if error != nil {
                          print(
                            "Multi factor finanlize sign in failed. Error: \(error.debugDescription)"
                          )
                        } else {
                          self.navigationController?.popViewController(animated: true)
                        }
                      }
                    }
                  )
                }
              }
          }
        )
      } else {
        self.showMessagePrompt(error.localizedDescription)
        return
      }
      // ...
      return
    }
    // User is signed in
    // ...
}
    

Objective‑C

[[FIRAuth auth] signInWithCredential:credential
                          completion:^(FIRAuthDataResult * _Nullable authResult,
                                       NSError * _Nullable error) {
    if (isMFAEnabled && error && error.code == FIRAuthErrorCodeSecondFactorRequired) {
      FIRMultiFactorResolver *resolver = error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey];
      NSMutableString *displayNameString = [NSMutableString string];
      for (FIRMultiFactorInfo *tmpFactorInfo in resolver.hints) {
        [displayNameString appendString:tmpFactorInfo.displayName];
        [displayNameString appendString:@" "];
      }
      [self showTextInputPromptWithMessage:[NSString stringWithFormat:@"Select factor to sign in\n%@", displayNameString]
                           completionBlock:^(BOOL userPressedOK, NSString *_Nullable displayName) {
       FIRPhoneMultiFactorInfo* selectedHint;
       for (FIRMultiFactorInfo *tmpFactorInfo in resolver.hints) {
         if ([displayName isEqualToString:tmpFactorInfo.displayName]) {
           selectedHint = (FIRPhoneMultiFactorInfo *)tmpFactorInfo;
         }
       }
       [FIRPhoneAuthProvider.provider
        verifyPhoneNumberWithMultiFactorInfo:selectedHint
        UIDelegate:nil
        multiFactorSession:resolver.session
        completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
          if (error) {
            [self showMessagePrompt:error.localizedDescription];
          } else {
            [self showTextInputPromptWithMessage:[NSString stringWithFormat:@"Verification code for %@", selectedHint.displayName]
                                 completionBlock:^(BOOL userPressedOK, NSString *_Nullable verificationCode) {
             FIRPhoneAuthCredential *credential =
                 [[FIRPhoneAuthProvider provider] credentialWithVerificationID:verificationID
                                                              verificationCode:verificationCode];
             FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
             [resolver resolveSignInWithAssertion:assertion completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
               if (error) {
                 [self showMessagePrompt:error.localizedDescription];
               } else {
                 NSLog(@"Multi factor finanlize sign in succeeded.");
               }
             }];
           }];
          }
        }];
     }];
    }
  else if (error) {
    // ...
    return;
  }
  // User successfully signed in. Get user data from the FIRUser object
  if (authResult == nil) { return; }
  FIRUser *user = authResult.user;
  // ...
}];
    

Siguientes pasos