Agrega la autenticación de varios factores de TOTP a tu app para iOS

Si actualizaste a Firebase Authentication con Identity Platform, puedes agregar la autenticación de varios factores (MFA) de contraseña de un solo uso basada en el tiempo (TOTP) a tu app.

Firebase Authentication con Identity Platform te permite usar una TOTP como factor adicional para la MFA. Cuando habilitas esta función, los usuarios que intentan acceder a tu app ven una solicitud de una TOTP. Para generarla, deben usar una app de autenticador capaz de generar códigos TOTP válidos, como Google Authenticator.

Antes de comenzar

  1. Habilita al menos un proveedor que admita la MFA. Ten en cuenta que todos los proveedores, excepto los siguientes, admiten la MFA:

    • Autenticación telefónica
    • Autenticación anónima
    • Tokens de autenticación personalizados
    • Apple Game Center
  2. Asegúrate de que tu app verifique las direcciones de correo electrónico de los usuarios. La MFA requiere verificación por correo electrónico. Esto evita que los actores maliciosos se registren en un servicio con una dirección de correo electrónico que no les pertenece y, luego, bloqueen al propietario real de la dirección de correo electrónico agregando un segundo factor.

  3. Si aún no lo has hecho, instala el SDK de Firebase para Apple.

    La MFA de TOTP solo es compatible con la versión 10.12.0 del SDK de Apple y versiones posteriores, y solo en iOS.

Habilitar MFA de TOTP

Para habilitar TOTP como el segundo factor, usa el SDK de Admin o llama al extremo de REST de configuración del proyecto.

Para usar el SDK de Admin, haz lo siguiente:

  1. Si aún no lo has hecho, instala el SDK de Firebase Admin para Node.js.

    La MFA de TOTP solo es compatible con las versiones 11.6.0 y posteriores del SDK de Firebase Admin para Node.js.

  2. Ejecuta este comando:

    import { getAuth } from 'firebase-admin/auth';
    
    getAuth().projectConfigManager().updateProjectConfig(
    {
          multiFactorConfig: {
              providerConfigs: [{
                  state: "ENABLED",
                  totpProviderConfig: {
                      adjacentIntervals: {
                          NUM_ADJ_INTERVALS
                      },
                  }
              }]
          }
    })
    

    Reemplaza lo siguiente:

    • NUM_ADJ_INTERVALS: La cantidad de intervalos de tiempo adyacentes a partir de los cuales se aceptarán TOTP, de cero a diez. El valor predeterminado es cinco.

      Las TOTP garantizan que cuando dos partes (el verificador y el validador) generen OTP en el mismo período (por lo general, de 30 segundos), generarán la misma contraseña. Sin embargo, para adaptarse a la variación del reloj entre las partes y el tiempo de respuesta humana, puedes configurar el servicio de TOTP para que también acepte las TOTP de los períodos adyacentes.

Para habilitar la MFA de TOTP mediante la API de REST, ejecuta lo siguiente:

curl -X PATCH "https://identitytoolkit.googleapis.com/admin/v2/projects/PROJECT_ID/config?updateMask=mfa" \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    -H "Content-Type: application/json" \
    -H "X-Goog-User-Project: PROJECT_ID" \
    -d \
    '{
        "mfa": {
          "providerConfigs": [{
            "state": "ENABLED",
            "totpProviderConfig": {
              "adjacentIntervals": "NUM_ADJ_INTERVALS"
            }
          }]
       }
    }'

Reemplaza lo siguiente:

  • PROJECT_ID: El ID del proyecto
  • NUM_ADJ_INTERVALS: Es la cantidad de intervalos de tiempo, de cero a diez. El valor predeterminado es cinco.

    Las TOTP garantizan que cuando dos partes (el verificador y el validador) generen OTP en el mismo período (por lo general, de 30 segundos), generarán la misma contraseña. Sin embargo, para adaptarse a la variación del reloj entre las partes y el tiempo de respuesta humana, puedes configurar el servicio de TOTP para que también acepte las TOTP de los períodos adyacentes.

Elige un patrón de inscripción

Puedes elegir si tu app requerirá una autenticación de varios factores, además de cómo y cuándo inscribir a tus usuarios. Estos son algunos patrones comunes:

  • Inscribir el segundo factor del usuario como parte del registro. Usa este método si tu app requiere la autenticación de varios factores para todos los usuarios.

  • Ofrecer una opción que se puede omitir para inscribir un segundo factor durante el registro. Si quieres fomentar el proceso de autenticación de varios factores en tu app, pero no exigirlo, puedes usar este enfoque.

  • Proporcionar la capacidad de agregar un segundo factor desde la página de administración de la cuenta o el perfil del usuario, en lugar de la pantalla de registro. Esto minimiza la fricción durante el proceso de registro y, a la vez, permite que la autenticación de varios factores esté disponible para los usuarios sensibles a la seguridad.

  • Requiere agregar un segundo factor de manera incremental cuando el usuario quiera acceder a las funciones con requisitos de seguridad mayores.

Inscribir a los usuarios en la MFA de TOTP

Después de habilitar la MFA de TOTP como segundo factor para tu app, implementa la lógica del cliente para inscribir a los usuarios en la MFA de TOTP:

  1. Vuelve a autenticar al usuario.

  2. Genera un Secret de TOTP para el usuario autenticado:

    // Generate a TOTP secret.
    guard let mfaSession = try? await currentUser.multiFactor.session() else { return }
    guard let totpSecret = try? await TOTPMultiFactorGenerator.generateSecret(with: mfaSession) else { return }
    
    // Display the secret to the user and prompt them to enter it into their
    // authenticator app. (See the next step.)
    
  3. Muestra el Secret al usuario y pídele que lo ingrese en su app de autenticación:

    // Display this key:
    let secret = totpSecret.sharedSecretKey()
    

    Además de mostrar la clave secreta, puedes intentar agregarla automáticamente a la app de autenticación predeterminada del dispositivo. Para ello, genera un URI de clave compatible con el Autenticador de Google y pásalo a openInOTPApp(withQRCodeURL:):

    let otpAuthUri = totpSecret.generateQRCodeURL(
        withAccountName: currentUser.email ?? "default account",
        issuer: "Your App Name")
    totpSecret.openInOTPApp(withQRCodeURL: otpAuthUri)
    

    Después de que el usuario agrega su secreto a la app de autenticador, comenzará a generar TOTP.

  4. Pídele al usuario que escriba la TOTP que aparece en su app de autenticación y úsala para finalizar la inscripción de la MFA:

    // Ask the user for a verification code from the authenticator app.
    let verificationCode = // Code from user input.
    
    // Finalize the enrollment.
    let multiFactorAssertion = TOTPMultiFactorGenerator.assertionForEnrollment(
        with: totpSecret,
        oneTimePassword: verificationCode)
    do {
        try await currentUser.multiFactor.enroll(
            with: multiFactorAssertion,
            displayName: "TOTP")
    } catch {
        // Wrong or expired OTP. Re-prompt the user.
    }
    

Permite que los usuarios accedan con un segundo factor

Para que los usuarios accedan con la MFA de TOTP, usa el siguiente código:

  1. Llama a uno de los métodos signIn(with...:) como lo harías si no usaras la MFA (por ejemplo, signIn(withEmail:password:)). Si el método arroja un error con el código secondFactorRequired, inicia el flujo de MFA de tu app.

    do {
        let authResult = try await Auth.auth().signIn(withEmail: email, password: password)
    
        // If the user is not enrolled with a second factor and provided valid
        // credentials, sign-in succeeds.
    
        // (If your app requires MFA, this could be considered an error
        // condition, which you would resolve by forcing the user to enroll a
        // second factor.)
    
        // ...
    } catch let error as AuthErrorCode where error.code == .secondFactorRequired {
        // Initiate your second factor sign-in flow. (See next step.)
        // ...
    } catch {
        // Other auth error.
        throw error
    }
    
  2. El flujo de MFA de tu app primero debe solicitarle al usuario que elija el segundo factor que quiere usar. Si quieres obtener una lista de los segundos factores admitidos, examina la propiedad hints de una instancia MultiFactorResolver:

    let mfaKey = AuthErrorUserInfoMultiFactorResolverKey
    guard let resolver = error.userInfo[mfaKey] as? MultiFactorResolver else { return }
    let enrolledFactors = resolver.hints.map(\.displayName)
    
  3. Si el usuario elige usar TOTP, pídele que escriba la TOTP que aparece en su app de autenticador y que lo use para acceder:

    let multiFactorInfo = resolver.hints[selectedIndex]
    switch multiFactorInfo.factorID {
    case TOTPMultiFactorID:
        let otpFromAuthenticator = // OTP typed by the user.
        let assertion = TOTPMultiFactorGenerator.assertionForSignIn(
            withEnrollmentID: multiFactorInfo.uid,
            oneTimePassword: otpFromAuthenticator)
        do {
            let authResult = try await resolver.resolveSignIn(with: assertion)
        } catch {
            // Wrong or expired OTP. Re-prompt the user.
        }
    default:
        return
    }
    

Dar de baja la MFA de TOTP

En esta sección, se describe cómo controlar la cancelación de la inscripción de un usuario a la MFA de TOTP.

Si un usuario se registró para usar varias opciones de MFA, y si da de baja la opción que se habilitó más recientemente, recibirá un auth/user-token-expired y saldrá de la cuenta. El usuario debe volver a acceder y verificar sus credenciales existentes, por ejemplo, una dirección de correo electrónico y una contraseña.

Para dar de baja al usuario, manejar el error y activar la reautenticación, usa el siguiente código:

guard let currentUser = Auth.auth().currentUser else { return }

// Prompt the user to select a factor to unenroll, from this array:
currentUser.multiFactor.enrolledFactors

// ...

// Unenroll the second factor.
let multiFactorInfo = currentUser.multiFactor.enrolledFactors[selectedIndex]
do {
    try await currentUser.multiFactor.unenroll(with: multiFactorInfo)
} catch let error as AuthErrorCode where error.code == .invalidUserToken {
    // Second factor unenrolled, but the user was signed out. Re-authenticate
    // them.
}

Próximos pasos