Uso de Touch ID y Face ID con Xamarin.iOS

iOS admite dos sistemas de autenticación biométrica:

  1. Touch ID usa un sensor de huella digital bajo el botón Inicio.
  2. Face ID usa sensores de cámara frontal para autenticar a los usuarios con un examen facial.

Touch ID se introdujo en iOS 7 y Face ID en iOS 11.

Estos sistemas de autenticación se basan en un procesador de seguridad basado en hardware denominado Enclave seguro. El Enclave seguro es responsable de cifrar representaciones matemáticas de datos faciales y de huellas digitales, y autenticar a los usuarios con esta información. Según Apple, los datos de caras y huellas digitales no dejan el dispositivo y no se realizan copias de seguridad en iCloud. Las aplicaciones interactúan con el Enclave seguro a través de la API de autenticación local y no pueden recuperar datos de caras o huellas digitales ni acceder directamente al enclave seguro.

Las aplicaciones pueden usar Touch ID y Face ID para autenticar a un usuario antes de proporcionar acceso al contenido protegido.

Contexto de autenticación local

La autenticación biométrica en iOS se basa en un objeto de contexto de autenticación local, que es una instancia de la clase LAContext. La clase LAContext permite:

  • Comprobar la disponibilidad del hardware biométrico.
  • Evaluar directivas de autenticación.
  • Evaluar los controles de acceso.
  • Personalizar y mostrar las solicitudes de autenticación.
  • Volver a usar o invalidar un estado de autenticación.
  • Administrar credenciales.

Detección de métodos de autenticación disponibles

El proyecto de ejemplo incluye una AuthenticationView respaldada por un AuthenticationViewController. Esta clase invalida el método ViewWillAppear para detectar métodos de autenticación disponibles:

partial class AuthenticationViewController: UIViewController
{
    // ...
    string BiometryType = "";

    public override void ViewWillAppear(bool animated)
    {
        base.ViewWillAppear(animated);
        unAuthenticatedLabel.Text = "";

        var context = new LAContext();
        var buttonText = "";

        // Is login with biometrics possible?
        if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out var authError1))
        {
            // has Touch ID or Face ID
            if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
            {
                context.LocalizedReason = "Authorize for access to secrets"; // iOS 11
                BiometryType = context.BiometryType == LABiometryType.TouchId ? "Touch ID" : "Face ID";
                buttonText = $"Login with {BiometryType}";
            }
            // No FaceID before iOS 11
            else
            {
                buttonText = $"Login with Touch ID";
            }
        }

        // Is pin login possible?
        else if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthentication, out var authError2))
        {
            buttonText = $"Login"; // with device PIN
            BiometryType = "Device PIN";
        }

        // Local authentication not possible
        else
        {
            // Application might choose to implement a custom username/password
            buttonText = "Use unsecured";
            BiometryType = "none";
        }
        AuthenticateButton.SetTitle(buttonText, UIControlState.Normal);
    }
}

Se llama al método ViewWillAppear cuando la interfaz de usuario está a punto de mostrar al usuario. Este método define una nueva instancia de LAContext y usa el método CanEvaluatePolicy para determinar si la autenticación biométrica está habilitada. Si es así, comprueba la versión del sistema y la enumeración BiometryType para determinar qué opciones biométricas están disponibles.

Si la autenticación biométrica no está habilitada, la aplicación intenta revertir a la autenticación de PIN. Si no hay autenticación biométrica ni PIN disponible, el propietario del dispositivo no ha habilitado las características de seguridad y el contenido no se puede proteger a través de la autenticación local.

Autenticar a un usuario

El AuthenticationViewController del proyecto de ejemplo incluye un método AuthenticateMe, que es responsable de autenticar al usuario:

partial class AuthenticationViewController: UIViewController
{
    // ...
    string BiometryType = "";

    partial void AuthenticateMe(UIButton sender)
    {
        var context = new LAContext();
        NSError AuthError;
        var localizedReason = new NSString("To access secrets");

        // Because LocalAuthentication APIs have been extended over time,
        // you must check iOS version before setting some properties
        context.LocalizedFallbackTitle = "Fallback";

        if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
        {
            context.LocalizedCancelTitle = "Cancel";
        }
        if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
        {
            context.LocalizedReason = "Authorize for access to secrets";
            BiometryType = context.BiometryType == LABiometryType.TouchId ? "TouchID" : "FaceID";
        }

        // Check if biometric authentication is possible
        if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out AuthError))
        {
            replyHandler = new LAContextReplyHandler((success, error) =>
            {
                // This affects UI and must be run on the main thread
                this.InvokeOnMainThread(() =>
                {
                    if (success)
                    {
                        PerformSegue("AuthenticationSegue", this);
                    }
                    else
                    {
                        unAuthenticatedLabel.Text = $"{BiometryType} Authentication Failed";
                    }
                });

            });
            context.EvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason, replyHandler);
        }

        // Fall back to PIN authentication
        else if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthentication, out AuthError))
        {
            replyHandler = new LAContextReplyHandler((success, error) =>
            {
                // This affects UI and must be run on the main thread
                this.InvokeOnMainThread(() =>
                {
                    if (success)
                    {
                        PerformSegue("AuthenticationSegue", this);
                    }
                    else
                    {
                        unAuthenticatedLabel.Text = "Device PIN Authentication Failed";
                        AuthenticateButton.Hidden = true;
                    }
                });

            });
            context.EvaluatePolicy(LAPolicy.DeviceOwnerAuthentication, localizedReason, replyHandler);
        }

        // User hasn't configured any authentication: show dialog with options
        else
        {
            unAuthenticatedLabel.Text = "No device auth configured";
            var okCancelAlertController = UIAlertController.Create("No authentication", "This device does't have authentication configured.", UIAlertControllerStyle.Alert);
            okCancelAlertController.AddAction(UIAlertAction.Create("Use unsecured", UIAlertActionStyle.Default, alert => PerformSegue("AuthenticationSegue", this)));
            okCancelAlertController.AddAction(UIAlertAction.Create("Cancel", UIAlertActionStyle.Cancel, alert => Console.WriteLine("Cancel was clicked")));
            PresentViewController(okCancelAlertController, true, null);
        }
    }
}

Se llama al método AuthenticateMe en respuesta al usuario que pulsa un botón Iniciar sesión. Se crea una instancia de un nuevo objeto LAContext y se comprueba la versión del dispositivo para determinar qué propiedades establecer en el contexto de autenticación local.

Se llama al método CanEvaluatePolicy para comprobar si la autenticación biométrica está habilitada, revertir a la autenticación de PIN si es posible y, por último, ofrecer un modo no seguro si no hay ninguna autenticación disponible. Si hay un método de autenticación disponible, el método EvaluatePolicy se usa para mostrar la interfaz de usuario y completar el proceso de autenticación.

El proyecto de ejemplo contiene datos ficticios y una vista para mostrar los datos si la autenticación se realiza correctamente.