# ¿Son seguras las rutas alternativas de Touch ID para las aprobaciones de agentes?

Los flujos de aprobación con Touch ID fallan de formas habituales: la tapa del portátil está cerrada, el teclado externo no es del tipo adecuado, el sensor no reconoce un dedo o macOS bloquea la biometría después de varios intentos fallidos. Si un agente puede convertir esas situaciones en un estado de aprobación ambiguo, el sistema ya ha cometido el error peligroso. La ruta alternativa debe indicar si una acción sigue pendiente, se ha denegado de forma definitiva o espera otro evento de autenticación.

Parece un asunto de interfaz hasta que un agente tiene acceso a una capacidad SSH o prepara una solicitud de API autenticada. Entonces cada estado impreciso se convierte en comportamiento operativo. Un agente no entiende «inténtalo más tarde» si la puerta de enlace no le devuelve un resultado preciso. Las personas también toman malas decisiones cuando la única señal es una solicitud biométrica insistente y una petición que quizá siga activa o quizá no.

La regla de diseño que uso es sencilla: un fallo biométrico puede retrasar una solicitud, pero nunca amplía la autoridad. La ausencia de un sensor puede cambiar la ruta hacia una decisión humana, pero nunca permite que el agente elija una ruta más débil. Un almacén de credenciales bloqueado detiene el trabajo basado en secretos hasta que vuelva a abrirse la barrera indicada.

## La disponibilidad de Touch ID no equivale a una aprobación

Touch ID indica si macOS puede verificar a una persona mediante un sensor biométrico en ese momento. La aprobación indica si esa persona verificada autoriza a este proceso del agente o a esta acción propuesta. Tratar ambas cosas como un único evento produce malas rutas alternativas porque las causas del fallo pertenecen a capas diferentes.

Una aprobación por sesión puede ser una decisión humana sencilla: la persona lee la identidad del proceso que hace la solicitud y acepta o rechaza la ejecución. Una aprobación por llamada vuelve a preguntar porque el propietario de la credencial marcó esa clave concreta como suficientemente sensible para exigirlo. Ninguna de las dos decisiones indica que un almacén de credenciales cifrado esté disponible. El almacén puede seguir bloqueado, el Mac puede estar en la ventana de inicio de sesión o el sensor físico puede ser inaccesible.

Sallyport mantiene esta separación de forma útil, no decorativa. Su barrera del almacén es absoluta: mientras el almacén está bloqueado, toda acción se deniega. La autorización por sesión y las claves por llamada deciden la autoridad del agente solo después de que esa barrera permita la acción. Así, una tarjeta de aprobación agradable no puede convertirse en una puerta trasera para saltarse un almacén bloqueado.

Esta distinción también evita una recomendación habitual y poco rigurosa: «Si Touch ID falla, muestra simplemente un botón de aprobación normal». Eso puede ser correcto para una solicitud de autorización humana diseñada para permitir un clic. Es incorrecto si la solicitud de Touch ID que falla es la que protege el acceso a un almacén de credenciales. La misma pantalla puede contener ambos conceptos, pero deben producir cambios de estado diferentes.

Usa también estados separados en el protocolo del agente:

- `awaiting_human_approval` significa que la acción no se ha ejecutado y una persona puede aprobar o rechazar la solicitud identificada.
- `awaiting_vault_unlock` significa que la acción no puede continuar porque la frontera de secretos está cerrada.
- `denied` significa que la puerta de enlace no conservará la autoridad para esta solicitud.
- `expired` significa que la solicitud esperó demasiado y debe proponerse de nuevo.

No llames a los cuatro estados «se requiere aprobación». Esa frase oculta el dato que más necesita el agente: si debe esperar, detenerse o preparar una solicitud nueva.

## El modo clamshell elimina el sensor integrado

Cerrar la tapa de un MacBook hace que el sensor Touch ID integrado sea físicamente inaccesible. Apple señala el modo clamshell como el ejemplo actual de un sensor biométrico integrado inaccesible en macOS: un MacBook cerrado y conectado a un monitor y un teclado externos no puede usar su sensor Touch ID interno, salvo que el teclado externo tenga Touch ID.

Este comportamiento no es excepcional para quienes usan agentes de programación. El MacBook suele estar debajo del escritorio o junto al monitor precisamente porque permanece encendido durante mucho tiempo. Si el diseño de aprobación supone que la persona puede alcanzar el botón de encendido, funcionará durante una demostración y fallará en el uso normal.

Comprueba la disposición del escritorio antes de decidir la ruta alternativa:

| Disposición física | Ruta de Touch ID | Comportamiento correcto de la puerta de enlace |
| --- | --- | --- |
| Portátil abierto | El sensor integrado puede estar accesible | Ofrece Touch ID cuando el control lo permita. |
| Portátil cerrado, teclado externo sin Touch ID | El sensor integrado no está disponible | No invites a escanear. Muestra una ruta de aprobación que el control permita realmente o deniega el trabajo basado en secretos mientras el almacén siga bloqueado. |
| Portátil cerrado, teclado externo con Touch ID | El sensor externo puede proporcionar Touch ID | Ofrece el escaneo solo después de que macOS indique que la biometría se puede usar. |
| Portátil cerrado, teclado Touch ID desconectado o sin batería | No hay una ruta biométrica utilizable | Marca la aprobación biométrica como no disponible y aplica la misma regla que a cualquier sensor no disponible. |

La palabra importante de esa tabla es «puede». La presencia del hardware no demuestra que se pueda usar en ese momento. Un teclado inalámbrico puede estar apagado, desconectado, emparejado con otro ordenador o simplemente no estar disponible para la sesión actual. La aplicación debe preguntar al sistema operativo si la política solicitada puede ejecutarse antes de mostrar un texto que indique a alguien que toque un sensor.

No conviertas el modo clamshell en un motivo automático para rebajar una acción de aprobación por llamada a aprobación por sesión. Ese cambio dura más que el problema físico y concede un alcance mayor del que la persona vio. Si la solicitud necesita una decisión por llamada, mantenla como aprobación por llamada. Muestra una aprobación con clic si ese control la permite, o haz que la acción espere o falle según su clase.

Hay una diferencia práctica entre «una persona no puede escanear» y «una persona no puede aprobar». Alguien con un teclado externo normal todavía puede leer una tarjeta y hacer clic en un botón de aprobación. Eso basta para una clave por llamada diseñada para aprobación con un clic o Touch ID. No desbloquea un almacén protegido por Touch ID. El texto de la pantalla debe ser igual de claro: «Aprobación disponible, almacén bloqueado» es mucho mejor que un aviso rojo de fallo genérico.

## Un escaneo fallido debe retener una solicitud, no crear autoridad nueva

Un escaneo de huella fallido es normal. La piel seca, un ángulo incorrecto del dedo, restos en el sensor y un toque apresurado ocurren. La respuesta adecuada es un estado de reintento acotado, no una denegación inmediata ni una solicitud activa sin límite.

La documentación de LocalAuthentication de Apple distingue un fallo de autenticación simple del bloqueo biométrico. Un fallo de la comprobación de credenciales devuelve `authenticationFailed`; el bloqueo es un estado separado que aparece después de demasiados intentos fallidos. La puerta de enlace debe conservar esa distinción porque las rutas de recuperación son diferentes.

Ante un escaneo fallido normal, conserva la acción original sin cambios y pendiente durante un intervalo breve. La persona debe ver qué está aprobando, qué proceso del agente lo solicitó, el host o la API de destino, la etiqueta de la credencial y la operación concreta. El agente debe recibir un resultado pendiente legible por máquina, no un tiempo de espera disfrazado de fallo.

Una respuesta útil podría tener este formato:

```json
{
  "status": "awaiting_human_approval",
  "request_id": "apr_7f3c",
  "reason": "biometric_retry",
  "expires_at": "2026-07-22T18:42:00Z",
  "retry_after_ms": 1500,
  "action_started": false
}
```

El `request_id` debe estar vinculado a la acción propuesta completa, no solo a la credencial. Si un agente solicita ejecutar `git push` y después cambia el remoto, la rama o el comando mientras la persona vuelve a probar Touch ID, ha creado una solicitud diferente. Recházala o exige una nueva tarjeta de aprobación. Reutilizar una aprobación porque el mismo proceso sigue activo es la forma en que unos reintentos aparentemente inocuos se convierten en un fallo de agente confundido.

Establece dos límites. Primero, limita la frecuencia de los intentos en la interfaz para que un agente defectuoso no pueda reabrir continuamente avisos que reclamen atención. Segundo, haz caducar la solicitud pendiente después de un periodo breve y visible. Una persona que vuelva diez minutos más tarde debe decidir sobre una solicitud recién mostrada, porque el estado del repositorio, el contenido de la API o el sistema remoto pueden haber cambiado.

El estado de reintento no debe conservar material de credenciales en el proceso del agente. El agente puede mantener el comando o el cuerpo de solicitud que pretendía usar, pero la puerta de enlace no debe entregarle un token «mientras espera» la autenticación. La inyección del secreto ocurre únicamente cuando la puerta de enlace ejecuta la operación del canal aprobada.

Aquí es donde muchos equipos colocan el bucle de reintento equivocado en el lugar equivocado. Permiten que el agente reintente toda la invocación de la herramienta cada pocos segundos. Eso genera tarjetas duplicadas, aumenta la posibilidad de repetir operaciones de API y enseña al modelo que insistir es una forma de superar las dudas. La puerta de enlace es dueña de la solicitud pendiente. El agente consulta o espera usando ese ID de solicitud. No crea solicitudes nuevas hasta que la primera caduque o se deniegue.

## El bloqueo del sensor significa que la ruta biométrica ha terminado

El bloqueo del sensor no es una solicitud que necesite otra huella. Es macOS indicando que la biometría está desactivada hasta que la persona complete la recuperación que exige el sistema operativo. Apple documenta `biometryLockout` como el estado que aparece después de demasiados intentos fallidos y señala que se necesita una contraseña para desbloquear la biometría.

Esta distinción tiene una consecuencia directa para las acciones del agente: deja de pedir Touch ID. Un diálogo que siga solicitando una huella después de que el sistema operativo haya bloqueado el sensor resulta engañoso y puede hacer que las personas insistan repetidamente. Indica que macOS exige autenticación de la cuenta para restaurar Touch ID y después termina o suspende la solicitud de la puerta de enlace según la clase de acción.

No trates silenciosamente la contraseña de la cuenta como equivalente a Touch ID. La política `deviceOwnerAuthentication` de Apple puede usar Touch ID, un Apple Watch emparejado cercano o la contraseña de macOS. La política de Apple que solo permite biometría falla cuando la biometría no está disponible, no está configurada o está bloqueada. Ambas son políticas válidas del sistema. Ofrecen garantías de seguridad diferentes.

Si la barrera de tu almacén indica que Touch ID es la puerta de entrada, la alternativa con contraseña cambia esa puerta. Puedes decidir que una contraseña de macOS sea un autenticador alternativo aceptable en otro diseño de producto, pero debes declarar y aplicar esa elección en la frontera del almacén. No la heredes accidentalmente porque un framework ofrezca un valor predeterminado cómodo.

Para un almacén vinculado a Touch ID, el bloqueo debe producir un resultado operativo terminal para cada llamada basada en secretos que esté esperando:

```text
status: denied
reason: vault_authentication_unavailable
recovery: authenticate with macOS to restore Touch ID, then submit a new request
action_started: false
```

Esto es deliberadamente más estricto que un escaneo fallido transitorio. La persona debe completar una acción de recuperación fuera del flujo de aprobación de la puerta de enlace. Mantener activa la solicitud antigua durante esa recuperación crea una ambigüedad incómoda: ¿la introducción posterior de la contraseña de la cuenta autorizó el comando SSH original o solo restauró el sensor? Haz que la respuesta sea inequívoca. Restauró el sensor. El agente debe solicitar de nuevo la operación.

Para las aprobaciones que no dependen del desbloqueo del almacén, puedes elegir otro resultado. Una tarjeta de autorización de sesión puede seguir disponible como decisión con un clic si su diseño lo permite y la acción no necesita un secreto bloqueado. La interfaz debe indicar exactamente qué ha fallado. «Touch ID bloqueado, la aprobación con clic sigue disponible» es un mensaje coherente. «Error de autenticación» no lo es.

## Haz que esperar o detenerse dependa de la acción

No decidas si hay que esperar basándote solo en el código de error. Decídelo según las consecuencias de la acción, la necesidad de que sus datos sigan siendo actuales y el estado de la frontera de credenciales. El mismo sensor no disponible puede hacer que una consulta de estado inofensiva espere, que un comando de infraestructura irreversible se detenga y que una solicitud basada en el almacén falle hasta que este se desbloquee.

Yo uso tres clases de acciones.

### Clase uno: esperar una respuesta humana breve

Retén una acción solo cuando se cumplan todas estas condiciones:

- La puerta de enlace no ha iniciado la operación ni ha inyectado una credencial.
- La solicitud tiene una identidad estable y una caducidad visible.
- Reproducirla después de la aprobación no sorprenderá a la persona porque el destino y el contenido siguen fijos.
- La acción es reversible, de solo lectura o suficientemente idempotente para que un retraso breve no cambie su significado.

Algunos ejemplos son leer la versión de un paquete privado, consultar la configuración de la rama protegida de un repositorio o realizar una solicitud de API de prueba claramente identificada. Incluso en esos casos, la espera pertenece a la puerta de enlace, no al bucle de reintento del agente.

### Clase dos: detenerse y exigir una solicitud nueva

Detente cuando el retraso cambie el significado práctico del comando. Un despliegue, una migración de una base de datos de producción, un push forzado, una rotación de credenciales, un cobro o un comando SSH que borre datos no deben quedarse esperando con una aprobación latente. La persona que lo vea más tarde merece una tarjeta nueva que refleje el contexto actual.

Detente también si la solicitud contiene valores efímeros. Una solicitud de API firmada, un artefacto de despliegue de un solo uso, una URL de corta duración o un comando cuyo espacio de trabajo local haya cambiado no deben reanudarse a partir de una intención obsoleta. La puerta de enlace no puede saber que el agente sigue queriendo exactamente lo mismo solo porque el proceso no ha terminado.

### Clase tres: denegar de inmediato porque el almacén está cerrado

Un almacén bloqueado tiene prioridad sobre la comodidad. Si la llamada HTTP o el comando SSH propuesto necesita un secreto guardado y la barrera del almacén está bloqueada, deniega la acción en lugar de ponerla en cola para ejecutarla automáticamente después del desbloqueo. La persona puede desbloquear el almacén y después el agente puede enviar una solicitud nueva. Así se conserva un registro causal claro: desbloquear primero, proponer después y ejecutar al final.

Aquí es donde la escalera fija de decisiones de Sallyport resulta útil. Su barrera del almacén deniega toda acción mientras está bloqueada. Después se aplican la autorización por sesión y la aprobación por llamada. No hay un lenguaje de políticas que intente deducir que un `curl` retrasado es lo bastante inofensivo como para reactivarlo después de un evento biométrico.

La alternativa tentadora es una cola de ejecución que se active cuando la persona toque el sensor. Es popular porque hace que las demostraciones parezcan fluidas. En el uso real, convierte la autenticación en un disparador para un trabajo que quizá ya no se quiera realizar. La aprobación debe liberar una solicitud que la persona todavía pueda ver, no vaciar una cola reunida mientras estaba ausente.

## Los teclados externos con Touch ID necesitan una comprobación de disponibilidad

Un teclado externo con Touch ID resuelve un problema del modo clamshell, pero añade otra dependencia. El teclado debe estar presente y disponible en la sesión activa del Mac cuando se produzca la aprobación. Trátalo como una condición actual, no como un dato de configuración comprobado una sola vez.

Los errores de LocalAuthentication de Apple incluyen `biometryDisconnected` y `biometryNotPaired` para accesorios biométricos extraíbles. Esos códigos importan porque distinguen el hardware ausente de una persona que no ha podido autenticarse. Un teclado desconectado nunca debe consumir un intento de reintento ni contar para el límite de fallos de la persona.

La interfaz y el protocolo deben responder de forma diferente a estos cuatro estados:

| Estado | Lo que ve la persona | Lo que recibe el agente |
| --- | --- | --- |
| Sensor listo | Una solicitud clara y un control de Touch ID | `awaiting_human_approval` |
| Sensor inaccesible en modo clamshell | Una explicación de que no se puede alcanzar el sensor integrado | `approval_path_unavailable` o una ruta de clic permitida |
| Sensor externo desconectado | Una indicación para volver a conectarlo, cargarlo o usar el método alternativo de aprobación permitido | `approval_path_unavailable` |
| Escaneo rechazado | La misma solicitud inmutable y una indicación para reintentarlo | `awaiting_human_approval` con `biometric_retry` |

No muestres a la persona los nombres de error del framework, pero consérvalos en el registro local de actividad. «Teclado externo con Touch ID no disponible» ayuda. `LAError.biometryDisconnected` pertenece a los diagnósticos y las pruebas.

La tarjeta de aprobación también debe seguir funcionando sin el sensor. Es una cuestión de seguridad y de usabilidad básica. El ratón, el trackpad, el foco del teclado y los controles de accesibilidad deben permitir rechazar una solicitud o elegir una aprobación con clic cuando esté permitida. Un sensor biométrico inaccesible nunca debe dejar a una persona atrapada en una solicitud imposible de responder.

## La pantalla de aprobación debe nombrar la frontera bloqueada

La mayor parte de la confusión procede de un único diálogo genérico que intenta representar todas las formas de autenticación. Separa el mensaje según la frontera que está esperando.

Para una solicitud por sesión, muestra primero la autoridad de firma de código del proceso solicitante y después ofrece a la persona una opción clara para aprobar o rechazar. Este es el momento en que decide si esa ejecución del agente puede operar. Si Touch ID está disponible, puede confirmar la elección. Si el diseño permite hacer clic, el modo clamshell no debe convertir la tarjeta en un callejón sin salida.

Para una clave por llamada, muestra la acción exacta y la etiqueta de la credencial. Una aprobación con un clic sigue siendo una decisión por llamada solo si se aplica a una solicitud inmutable y caduca rápidamente. No permitas que un agente agrupe cinco llamadas detrás de un único botón solo porque Touch ID resulta incómodo en ese escritorio.

Para un almacén bloqueado, indica que el almacén está bloqueado y que la acción no ha comenzado. No presentes el mensaje como una solicitud del agente rechazada, porque la persona puede pensar que debe volver a hacer clic. La recuperación depende del mecanismo de autenticación definido para el almacén. Cuando se abra, exige una solicitud nueva para cualquier acción que necesite el secreto.

Un buen registro de auditoría separa estas transiciones. Por ejemplo:

```text
2026-07-22T18:40:12Z request.created     id=apr_7f3c channel=ssh action="git push origin main"
2026-07-22T18:40:13Z approval.pending   id=apr_7f3c method=touch_id
2026-07-22T18:40:16Z biometric.failed   id=apr_7f3c source=built_in_sensor
2026-07-22T18:41:02Z request.expired    id=apr_7f3c action_started=false
```

El registro de acciones no debe fingir que un escaneo fallido fue una denegación de autorización. Fue un intento fallido de autenticación. La solicitud caducó sin ejecutarse. Estas palabras importan durante una revisión, especialmente cuando un agente afirma que «no pudo desplegar» y la persona operadora necesita saber si el sistema lo bloqueó, la persona lo rechazó o nadie completó la solicitud.

En los sistemas de auditoría resistentes a manipulaciones, registra la transición de estado antes de devolvérsela al agente. Sallyport proyecta sus registros Sessions y Activity desde un único registro cifrado y encadenado mediante hashes, y su comando `sp audit verify` verifica esa cadena sin conexión sobre el texto cifrado. Así, una persona que revise posteriormente tiene pruebas de que una solicitud caducó o fue denegada sin tener que confiar en la propia transcripción del agente.

## Prueba el escritorio físico, no solo la API ideal

Una prueba unitaria de LocalAuthentication que devuelva códigos de éxito y fallo es necesaria, pero demuestra muy poco sobre un flujo de aprobación. Los fallos que frustran a las personas aparecen cuando se combinan la disposición del hardware, el estado del escritorio y el momento de actuación del agente.

Ejecuta esta secuencia de pruebas en un Mac real antes de dar por terminado el flujo:

1. Inicia una sesión del agente con el portátil abierto, envía una solicitud por llamada, recházala y después envía una solicitud nueva y apruébala. Confirma que la solicitud rechazada nunca se ejecuta.
2. Cierra la tapa y conecta un monitor externo y un teclado sin Touch ID. Confirma que la interfaz no pide tocar un sensor inaccesible. Prueba tanto una aprobación mediante clic como una solicitud basada en el almacén.
3. Repite la prueba con un teclado Touch ID externo que funcione. Desconéctalo o apágalo mientras haya una aprobación pendiente. Confirma que la solicitud informa de hardware no disponible, no de un intento biométrico fallido.
4. Provoca varios escaneos fallidos hasta que macOS entre en estado de bloqueo. Confirma que las solicitudes biométricas se detienen, que las solicitudes basadas en secretos no se ponen en cola para ejecutarse después y que la recuperación exige una acción enviada de nuevo.
5. Deja que una solicitud pendiente caduque. Cambia la rama del repositorio, los argumentos del comando o el contenido de la API antes de enviar una solicitud nueva. Confirma que la nueva propuesta recibe un ID de solicitud diferente y una nueva decisión humana.

Revisa los registros después de cada ejecución. Debes ver un evento de creación de solicitud, una secuencia de cambios de estado y un único evento de ejecución o ninguno. Varias ejecuciones después de una sola señal de aprobación indican un fallo de reproducción o de reintento. La ausencia de un evento terminal significa que el personal de soporte tendrá que adivinar si el agente sigue esperando.

Prueba también la cancelación. La persona debe poder rechazar mientras Touch ID no esté disponible, el agente debe poder abandonar una solicitud pendiente y el cierre de la aplicación debe invalidar las aprobaciones activas. Apple distingue la cancelación del usuario, la cancelación de la aplicación y la cancelación del sistema en LocalAuthentication. El modelo de auditoría debe conservar esa distinción aunque la interfaz las agrupe bajo el mensaje sencillo «cancelada».

Una ruta alternativa fiable resulta casi aburrida cuando funciona. La pantalla dice la verdad sobre el sensor, el agente recibe un estado que puede obedecer, los secretos permanecen en el almacén y ninguna acción escapa porque alguien cerró la tapa del portátil. Ese es el estándar que debes mantener: cada fallo físico debe conducir a un resultado específico que no amplíe la autoridad.
