# Les solutions de secours Touch ID sont-elles sûres pour les approbations d'agents ?

Les flux d'approbation Touch ID échouent de manière ordinaire : le capot de l'ordinateur est fermé, le clavier externe n'est pas du bon type, le capteur ne reconnaît pas le doigt ou macOS verrouille la biométrie après plusieurs échecs. Si un agent peut transformer ces situations en état d'approbation ambigu, le système a déjà commis l'erreur dangereuse. La solution de secours doit indiquer si l'action est toujours en attente, définitivement refusée ou suspendue dans l'attente d'un événement d'authentification distinct.

Cela ressemble à un problème d'interface jusqu'au moment où un agent détient une capacité SSH ou prépare une requête API authentifiée. Chaque état imprécis devient alors un comportement opérationnel. Un agent ne comprend pas « réessayez plus tard » si la passerelle ne lui renvoie pas un résultat précis. Les humains prennent eux aussi de mauvaises décisions quand le seul signal est une invite biométrique obstinée et une demande dont on ignore si elle est toujours active.

La règle de conception que j'applique est simple : un échec biométrique peut retarder une demande, mais il n'élargit jamais les autorisations. Un capteur absent peut rediriger vers une décision humaine, mais il ne permet jamais à l'agent de choisir une voie moins sûre. Un coffre d'identifiants verrouillé bloque le travail qui dépend de secrets jusqu'à la réouverture de la barrière indiquée.

## La disponibilité de Touch ID n'est pas une approbation

Touch ID indique si macOS peut vérifier l'identité d'une personne au moyen d'un capteur biométrique à cet instant. L'approbation indique si cette personne vérifiée autorise ce processus d'agent ou cette action proposée. Confondre les deux événements produit de mauvais mécanismes de secours, car leurs causes d'échec appartiennent à des couches différentes.

Une autorisation par session peut être une décision humaine simple : la personne lit l'identité du processus demandeur, puis accepte ou refuse l'exécution. Une autorisation par appel redemande une décision parce que le propriétaire de l'identifiant a jugé cette clé suffisamment sensible pour l'exiger. Aucune de ces décisions ne signifie qu'un coffre d'identifiants chiffré est disponible. Le coffre peut rester verrouillé, le Mac peut afficher la fenêtre de connexion ou le capteur physique peut être inaccessible.

Sallyport maintient cette séparation de façon concrète. Sa barrière de coffre est absolue : tant que le coffre est verrouillé, toute action est refusée. L'autorisation par session et les clés par appel déterminent uniquement les droits de l'agent après le franchissement de cette barrière. Une carte d'approbation agréable ne peut donc pas devenir une porte dérobée vers un coffre verrouillé.

Cette distinction évite aussi une recommandation fréquente mais négligente : « Si Touch ID échoue, affichez simplement un bouton d'approbation normal. » Cela peut convenir à une invite d'autorisation humaine conçue pour accepter un clic. C'est incorrect si la demande Touch ID en échec protège l'accès à un coffre d'identifiants. Un même écran peut contenir les deux notions, mais elles doivent produire des changements d'état différents.

Utilisez aussi des états distincts dans le protocole de l'agent :

- `awaiting_human_approval` signifie que l'action n'a pas été exécutée et qu'une personne peut approuver ou refuser la demande identifiée.
- `awaiting_vault_unlock` signifie que l'action ne peut pas continuer parce que la limite des secrets est fermée.
- `denied` signifie que la passerelle ne conservera pas d'autorisation pour cette demande.
- `expired` signifie que la demande a attendu trop longtemps et doit être soumise à nouveau.

N'appelez pas ces quatre états « approbation requise ». Cette expression dissimule l'information dont l'agent a le plus besoin : doit-il attendre, s'arrêter ou préparer une nouvelle demande ?

## Le mode clamshell supprime l'accès au capteur intégré

Le capot fermé d'un MacBook rend physiquement inaccessible le capteur Touch ID intégré. Apple cite le mode clamshell comme exemple actuel de capteur biométrique intégré inaccessible sur macOS : un MacBook fermé et relié à un écran et à un clavier externes ne peut pas utiliser son capteur Touch ID interne, sauf si le clavier externe possède Touch ID.

Ce comportement n'est pas exceptionnel pour les personnes qui utilisent des agents de programmation. Le MacBook reste sous un bureau ou à côté d'un écran précisément parce qu'il fonctionne pendant de longues périodes. Si la conception de l'approbation suppose que l'utilisateur peut atteindre le bouton d'alimentation, elle fonctionnera pendant une démonstration et échouera dans l'usage quotidien.

Établissez la configuration du bureau avant de choisir le comportement de secours :

| Configuration physique | Voie Touch ID | Comportement correct de la passerelle |
| --- | --- | --- |
| Ordinateur portable ouvert | Le capteur intégré peut être accessible | Proposer Touch ID lorsque la règle de contrôle l'autorise. |
| Ordinateur portable fermé, clavier externe sans Touch ID | Le capteur intégré est indisponible | Ne pas demander de lecture. Afficher une voie d'approbation réellement autorisée ou refuser le travail utilisant des secrets tant que le coffre reste verrouillé. |
| Ordinateur portable fermé, clavier externe avec Touch ID | Le capteur externe peut fournir Touch ID | Proposer la lecture uniquement après que macOS a signalé que la biométrie est utilisable. |
| Ordinateur portable fermé, clavier Touch ID déconnecté ou déchargé | Aucune voie biométrique utilisable | Signaler que l'approbation biométrique est indisponible et appliquer la même règle que pour tout capteur indisponible. |

Le mot important dans ce tableau est « peut ». La présence du matériel ne prouve pas qu'il est utilisable à cet instant. Un clavier sans fil peut être éteint, déconnecté, associé à un autre ordinateur ou simplement indisponible pour la session actuelle. L'application doit demander au système d'exploitation si la règle demandée peut s'exécuter avant d'afficher un texte invitant quelqu'un à toucher un capteur.

Ne faites pas du mode clamshell une raison automatique de remplacer une approbation par appel par une approbation par session. Ce changement dépasse le problème physique et accorde une portée plus large que celle présentée à l'utilisateur. Si la demande exige une décision par appel, conservez ce niveau. Présentez une approbation par clic si ce contrôle l'autorise, ou faites attendre ou échouer l'action selon sa classe.

Il existe une différence pratique entre « une personne ne peut pas effectuer la lecture » et « une personne ne peut pas approuver ». Une personne équipée d'un clavier externe standard peut toujours lire une carte et cliquer sur un bouton d'approbation. Cela suffit pour une clé par appel conçue pour une approbation par clic ou Touch ID. Cela ne déverrouille pas un coffre protégé par Touch ID. Le texte à l'écran doit rester tout aussi direct : « Approbation disponible, coffre verrouillé » vaut mieux qu'une bannière rouge générique signalant un échec.

## Une lecture échouée doit retenir une demande, pas créer de nouveaux droits

Une lecture d'empreinte échouée est normale. Peau sèche, mauvais angle du doigt, résidus sur le capteur ou geste trop rapide, tout cela arrive. La bonne réponse est un état de nouvelle tentative limité, pas un refus immédiat et pas une demande active sans fin.

La documentation LocalAuthentication d'Apple distingue l'échec d'authentification simple du verrouillage biométrique. Un contrôle d'identifiant échoué renvoie `authenticationFailed`, tandis que le verrouillage est un état distinct atteint après trop de tentatives infructueuses. Votre passerelle doit conserver cette distinction, car les voies de récupération ne sont pas les mêmes.

Pour une lecture ordinaire échouée, gardez l'action initiale immuable et en attente pendant un court intervalle. L'utilisateur doit voir ce qu'il approuve, quel processus d'agent a effectué la demande, quelle machine ou API est ciblée, quel identifiant est utilisé et quelle opération sera réalisée. L'agent doit recevoir un résultat en attente lisible par la machine, et non un délai d'expiration déguisé en échec.

Une réponse utile peut ressembler à ceci :

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

Le `request_id` doit être lié à l'ensemble de l'action proposée, pas seulement à l'identifiant. Si un agent demande d'exécuter `git push`, puis modifie le dépôt distant, la branche ou la commande pendant que l'utilisateur réessaie Touch ID, il s'agit d'une autre demande. Refusez-la ou exigez une nouvelle carte d'approbation. Réutiliser une approbation parce que le même processus existe encore est une manière de transformer des tentatives inoffensives en faille de type confused deputy.

Fixez deux limites. Premièrement, limitez suffisamment les tentatives d'interface pour qu'un agent défaillant ne puisse pas rouvrir sans cesse des invites attirant l'attention. Deuxièmement, faites expirer la demande en attente après une courte période clairement visible. Une personne qui revient dix minutes plus tard doit décider sur une demande nouvellement affichée, car l'état du dépôt, la charge utile de l'API ou le système distant de l'agent a pu changer.

L'état de nouvelle tentative ne doit conserver aucun élément d'identifiant dans le processus de l'agent. Celui-ci peut garder sa commande prévue ou le corps de sa requête, mais la passerelle ne doit pas lui remettre de jeton « pendant l'attente » de l'authentification. L'injection du secret n'a lieu que lorsque la passerelle exécute l'opération approuvée par le canal prévu.

C'est souvent ici que les équipes placent la mauvaise boucle de nouvelle tentative au mauvais endroit. Elles laissent l'agent relancer tout l'appel d'outil toutes les quelques secondes. Cela produit des cartes en double, augmente le risque d'opérations API répétées et apprend au modèle que la persistance permet de contourner l'hésitation. La passerelle possède la demande en attente. L'agent interroge cette demande ou attend son identifiant. Il ne fabrique pas de nouvelles demandes avant l'expiration ou le refus de la première.

## Le verrouillage du capteur met fin à la voie biométrique

Le verrouillage du capteur n'est pas une demande nécessitant une nouvelle empreinte. C'est macOS qui indique que la biométrie est désactivée jusqu'à ce que l'utilisateur effectue la récupération demandée par le système. Apple documente `biometryLockout` comme l'état atteint après trop d'échecs et précise qu'un code est nécessaire pour déverrouiller la biométrie.

La conséquence pour les actions d'un agent est directe : cessez de demander Touch ID. Une boîte de dialogue qui continue de réclamer une empreinte alors que le système a verrouillé le capteur est trompeuse et peut pousser l'utilisateur à multiplier les essais. Indiquez que macOS exige une authentification du compte pour rétablir Touch ID, puis terminez ou suspendez la demande de la passerelle selon la classe de l'action.

Ne traitez pas silencieusement le mot de passe du compte comme un équivalent de Touch ID. La règle `deviceOwnerAuthentication` d'Apple peut utiliser Touch ID, une Apple Watch associée à proximité ou le mot de passe macOS de l'utilisateur. La règle biométrique uniquement échoue en revanche lorsque la biométrie est indisponible, non configurée ou verrouillée. Les deux politiques sont valides, mais elles ne promettent pas la même sécurité.

Si la barrière de votre coffre indique que Touch ID est la condition d'accès, le recours au mot de passe modifie cette condition. Vous pouvez décider qu'un mot de passe macOS est un autre authentificateur acceptable dans une conception différente, mais ce choix doit être formulé et appliqué à la limite du coffre. Ne l'adoptez pas par accident parce qu'un framework propose une valeur par défaut pratique.

Pour un coffre lié à Touch ID, le verrouillage doit produire un résultat opérationnel terminal pour chaque appel utilisant un secret qui attend actuellement :

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

Cette règle est volontairement plus stricte qu'un échec de lecture temporaire. La personne doit effectuer une récupération en dehors du flux d'approbation de la passerelle. Maintenir l'ancienne demande pendant cette récupération crée une ambiguïté : l'entrée ultérieure du mot de passe du compte a-t-elle autorisé la commande SSH initiale ou a-t-elle seulement rétabli le capteur ? La réponse doit être claire. Elle a rétabli le capteur. L'agent doit demander à nouveau l'opération.

Pour les approbations qui ne dépendent pas du déverrouillage du coffre, vous pouvez choisir un autre résultat. Une carte d'autorisation de session peut rester disponible comme décision par clic si sa conception le permet et si l'action n'a pas besoin d'un secret verrouillé. L'interface doit indiquer exactement ce qui a échoué. « Touch ID verrouillé, approbation par clic toujours disponible » est un message cohérent. « Échec de l'authentification » ne l'est pas.

## Faites de l'attente ou de l'arrêt une propriété de l'action

Ne décidez pas d'attendre uniquement à partir du code d'erreur. Tenez compte des conséquences de l'action, de son besoin de fraîcheur et de l'état de la limite des identifiants. Le même capteur indisponible peut imposer l'attente pour une requête d'état sans danger, l'arrêt pour une commande d'infrastructure irréversible et l'échec d'une demande utilisant un coffre verrouillé.

J'utilise trois classes d'actions.

### Classe 1 : attendre brièvement une réponse humaine

Ne retenez une action que si toutes les affirmations suivantes sont vraies :

- La passerelle n'a pas commencé l'opération et n'a injecté aucun identifiant.
- La demande possède une identité stable et une expiration visible.
- Sa reprise après approbation ne surprendra pas l'utilisateur, car la cible et la charge utile restent fixes.
- L'action est réversible, en lecture seule ou suffisamment idempotente pour qu'un court délai ne change pas sa signification.

Cela peut concerner la lecture de la version d'un paquet privé, la récupération des paramètres de branche protégée d'un dépôt ou l'envoi d'une requête API clairement identifiée en mode simulation. Même dans ces cas, l'attente appartient à la passerelle, pas à la boucle de nouvelle tentative de l'agent.

### Classe 2 : arrêter et exiger une nouvelle demande

Arrêtez l'action si le délai change la signification pratique de la commande. Un déploiement, une migration de base de données de production, un push forcé, une rotation d'identifiant, la capture d'un paiement ou une commande SSH qui supprime des données ne doit pas rester en attente avec une approbation latente. La personne qui la voit plus tard mérite une nouvelle carte reflétant le contexte actuel.

Arrêtez également la demande si elle contient des valeurs éphémères. Une requête API signée, un artefact de déploiement à usage unique, une URL de courte durée ou une commande dont l'espace de travail local a changé ne doit pas être reprise à partir d'une intention périmée. La passerelle ne peut pas savoir que l'agent veut toujours faire exactement la même chose simplement parce que le processus n'est pas terminé.

### Classe 3 : refuser immédiatement parce que le coffre est fermé

Un coffre verrouillé passe avant la commodité. Si l'appel HTTP ou la commande SSH proposée nécessite un secret stocké et que la barrière du coffre est verrouillée, refusez l'action au lieu de la placer en file d'attente pour une exécution automatique après le déverrouillage. L'utilisateur peut ouvrir le coffre, puis l'agent soumettre une nouvelle demande. Le lien causal reste propre : déverrouiller d'abord, proposer ensuite, exécuter en dernier.

C'est là que l'échelle de décision fixe de Sallyport est utile. Sa barrière de coffre refuse toute action lorsqu'elle est verrouillée, puis l'autorisation par session et l'approbation par appel s'appliquent à la demande. Aucune règle ne tente de supposer qu'un `curl` retardé est suffisamment inoffensif pour être ressuscité après un événement biométrique.

L'alternative tentante est une file d'exécution qui se réveille lorsque l'utilisateur touche le capteur. Elle est populaire parce qu'elle rend les démonstrations fluides. En pratique, elle transforme l'authentification en déclencheur d'un travail qui n'est peut-être plus souhaité. L'approbation doit libérer une demande que la personne peut encore voir, pas vider une file constituée pendant son absence.

## Les claviers Touch ID externes nécessitent une vérification de disponibilité

Un clavier Touch ID externe résout un problème du mode clamshell, mais ajoute une autre dépendance. Le clavier doit être présent et utilisable dans la session Mac active au moment de l'approbation. Considérez cela comme une condition dynamique, et non comme un fait établi une fois pour toutes.

Les erreurs LocalAuthentication d'Apple comprennent `biometryDisconnected` et `biometryNotPaired` pour les accessoires biométriques amovibles. Ces codes sont importants, car ils distinguent le matériel absent d'une personne qui n'a pas réussi à s'authentifier. Un clavier déconnecté ne doit jamais consommer une tentative ni entrer dans le calcul d'un taux d'échec utilisateur.

L'interface et le protocole doivent réagir différemment à quatre états :

| État | Ce que voit la personne | Ce que reçoit l'agent |
| --- | --- | --- |
| Capteur prêt | Une demande claire et une commande Touch ID | `awaiting_human_approval` |
| Capteur inaccessible en mode clamshell | Une explication indiquant que le capteur intégré ne peut pas être atteint | `approval_path_unavailable` ou une voie par clic autorisée |
| Capteur externe déconnecté | Une invitation à reconnecter ou recharger le clavier, ou à utiliser la méthode d'approbation autorisée | `approval_path_unavailable` |
| Lecture refusée | La même demande immuable et une indication de nouvelle tentative | `awaiting_human_approval` avec `biometric_retry` |

N'affichez pas les noms bruts des erreurs du framework à l'utilisateur, mais conservez-les dans l'activité locale. « Clavier Touch ID externe indisponible » est utile. `LAError.biometryDisconnected` appartient aux diagnostics et aux tests.

La carte d'approbation doit aussi rester utilisable sans le capteur. C'est une question de sécurité, mais aussi d'ergonomie élémentaire. Une souris, un trackpad, le focus clavier et les contrôles d'accessibilité doivent permettre de refuser une demande ou de choisir une approbation par clic autorisée. Un capteur biométrique inaccessible ne doit jamais enfermer une personne dans une invite impossible à traiter.

## L'écran d'approbation doit nommer la limite bloquée

La plupart des confusions viennent d'une seule fenêtre générique qui tente de représenter toutes les formes d'authentification. Séparez le message selon la limite qui est en attente.

Pour une demande par session, affichez d'abord l'autorité de signature de code du processus demandeur, puis donnez à la personne un choix clair entre approuver et refuser. C'est à ce moment qu'elle décide si cette exécution de l'agent peut agir. Si Touch ID est disponible, il peut confirmer le choix. Si la conception autorise un clic, le mode clamshell ne doit pas transformer la carte en impasse.

Pour une clé par appel, affichez l'action exacte et le libellé de l'identifiant. Une approbation par clic reste une décision par appel uniquement si elle s'applique à une demande immuable et expire rapidement. Ne laissez pas un agent regrouper cinq appels derrière un seul bouton simplement parce que Touch ID est difficile à utiliser à ce bureau.

Pour un coffre verrouillé, indiquez que le coffre est verrouillé et que l'action n'a pas commencé. Ne présentez pas le message comme une demande d'agent refusée, car l'utilisateur pourrait croire qu'il doit cliquer à nouveau. La récupération dépend du mécanisme d'authentification défini pour le coffre. Une fois celui-ci ouvert, exigez une nouvelle demande pour toute action qui a besoin du secret.

Un bon journal d'audit sépare ces transitions. Par exemple :

```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
```

Le journal des actions ne doit pas faire passer une lecture échouée pour un refus d'autorisation. Il s'agissait d'une tentative d'authentification échouée. La demande a expiré sans exécution. Ces mots comptent lors d'un examen, notamment lorsqu'un agent affirme qu'il « n'a pas pu déployer » et qu'un opérateur doit savoir si le système l'a bloqué, si l'utilisateur l'a refusé ou si personne n'a terminé l'invite.

Pour les systèmes d'audit infalsifiables, enregistrez la transition d'état avant de la retourner à l'agent. Sallyport construit ses journaux Sessions et Activity à partir d'un seul journal chiffré et chaîné par hachage. Sa commande `sp audit verify` vérifie cette chaîne hors ligne sur le texte chiffré. Un examinateur dispose ainsi de preuves qu'une demande a expiré ou a été refusée sans devoir faire confiance au propre compte rendu de l'agent.

## Testez le vrai bureau, pas seulement l'API idéale

Un test unitaire LocalAuthentication qui renvoie des codes de réussite et d'échec est nécessaire, mais il ne prouve presque rien sur un flux d'approbation. Les défaillances qui frustrent les utilisateurs apparaissent lorsque la configuration matérielle, l'état du bureau et le rythme de l'agent se rencontrent.

Effectuez cette séquence sur un vrai Mac avant de considérer le flux comme terminé :

1. Démarrez une session d'agent avec l'ordinateur portable ouvert, soumettez une demande par appel, refusez-la, puis soumettez une nouvelle demande et approuvez-la. Vérifiez que la demande refusée ne s'exécute jamais.
2. Fermez le capot et branchez un écran externe ainsi qu'un clavier sans Touch ID. Vérifiez que l'interface ne demande pas à l'utilisateur de toucher un capteur inaccessible. Testez une approbation par clic et une demande utilisant le coffre.
3. Répétez l'opération avec un clavier Touch ID externe fonctionnel. Déconnectez-le ou éteignez-le pendant qu'une approbation est en attente. Vérifiez que la demande signale un matériel indisponible et non une tentative biométrique échouée.
4. Provoquez plusieurs lectures échouées jusqu'à ce que macOS passe en verrouillage. Vérifiez que les invites biométriques s'arrêtent, que les demandes utilisant des secrets ne sont pas mises en file d'attente et que la récupération exige une nouvelle action soumise par l'utilisateur.
5. Laissez une demande en attente jusqu'à son expiration. Modifiez la branche du dépôt, les arguments de la commande ou la charge utile de l'API avant d'envoyer une nouvelle demande. Vérifiez que la nouvelle proposition reçoit un autre identifiant et une nouvelle décision humaine.

Examinez les journaux après chaque test. Vous devez voir un événement de création, une suite de changements d'état et soit un événement d'exécution unique, soit aucun événement d'exécution. Plusieurs exécutions après un seul signal d'approbation révèlent un défaut de rejeu ou de nouvelle tentative. L'absence d'un événement terminal obligera le support à deviner si l'agent attend encore.

Testez aussi l'annulation. L'utilisateur doit pouvoir refuser lorsque Touch ID est indisponible, l'agent doit pouvoir abandonner une demande en attente et l'arrêt de l'application doit invalider les approbations actives. Apple distingue l'annulation par l'utilisateur, l'annulation par l'application et l'annulation par le système dans LocalAuthentication. Votre modèle d'audit doit conserver cette distinction même si l'interface les regroupe sous le message compréhensible « annulé ».

Une solution de secours fiable paraît presque ennuyeuse quand elle fonctionne. L'écran dit la vérité sur le capteur, l'agent reçoit un état qu'il peut respecter, les secrets restent dans le coffre et aucune action ne s'échappe parce que quelqu'un a fermé le capot d'un ordinateur. C'est la norme à viser : chaque défaillance physique doit conduire à un résultat précis qui n'élargit jamais les autorisations.
