# Human-in-the-Loop (HITL) for Transactional Verification avec CIBA : comment Okta protège les actions à risque des agents IA

**Comment Okta utilise le standard OpenID CIBA pour qu’un agent IA puisse initier une action sensible et qu’un humain l’approuve avant exécution.**

## Principe : l’agent agit, l’humain doit pouvoir dire non

Protéger la **porte d’entrée** d’une application — authentification OIDC ou SAML via un IdP de confiance reste indispensable. Ce n’est plus suffisant dès lors qu’un **agent IA** dispose déjà d’une session et d’outils : le risque se déplace de "qui s’est connecté ?" à "**quelle action l’agent tente-t-il de réaliser maintenant** ?"

Les agents ne se contentent plus de résumer un document. Ils appellent des outils, écrivent dans des SaaS, déclenchent des paiements, modifient des droits, suppriment des données. Leur donner des scopes et des tokens, c’est leur donner le **droit** d’agir. Pour autant, cela ne signifie pas leur donner le feu vert pour *chaque* opération (notamment à risque) sans validation.

Okta positionne explicitement le **Human-in-the-Loop (HITL)** pour ces cas : opérations sensibles (ex. supprimer des données de production) → workflow d’approbation humaine basé sur **CIBA** (*Client-Initiated Backchannel Authentication*). Chez Okta, ce mécanisme s’appelle aussi **Transactional Verification**.

*   **HITL** = le modèle (« l’agent attend un humain »).
    
*   **CIBA / Transactional Verification** = le protocole qui porte la confirmation.
    

L’idée :

> L’**agent** (ou son UI) démarre l’action sur un appareil.
> 
> L’**humain** l’approuve sur un autre.

* * *

### Comment on sécurise ces actions aujourd’hui

Pour l’authentification « front door », on s’appuie sur des standards matures. Pour la **vérification d’une action** une fois l’agent déjà en session, beaucoup de designs restent encore ad hoc.

Deux écueils reviennent souvent :

1.  **Expérience et confiance.** Demander à l’humain de "re-prouver" son identité par des questions de type PII, un OTP SMS, ou un second login navigateur au milieu d’une tâche agentique, freine l’usage - et reste fragile face à l’ingénierie sociale.
    
2.  **Privilège trop large.** Pour que l’agent (ou son backend) puisse exécuter l’opération, on lui confie souvent un credential large (jetons ou clés API avec des permissions trop étendues, compte de service trop large, clé API durable). S’il fuit, ou si l’agent enchaîne des appels aux outils de l'organisation, le risque est maximal.
    

On voudrait l’inverse : un mécanisme standard où l’agent **initie** la demande, l’humain **confirme "hors bande"**, et l’application ne reçoit **que le juste privilège** pour cette transaction - pas un droit permanent de tout faire.

CIBA répond exactement à ce découplage.

* * *

## Autorisation ≠ HITL

Un agent peut être correctement autorisé (scopes, policies, XAA, STS…) et malgré tout devoir s’arrêter avant une action destructive ou irréversible.

![](https://cdn.hashnode.com/uploads/covers/6516c056c08f5e5c38de58d9/35bb02ea-13a8-41b8-b1c0-854906326317.png align="center")

| Couche | Question | Mécanisme |
| --- | --- | --- |
| **Autorisation** | L’agent *peut-il* faire cette classe d’actions ? | Scopes / policies |
| **HITL** | L’humain *confirme-t-il cette* action ? | CIBA / Transactional Verification |

Sans HITL, un agent "bien scopé" peut encore enchaîner des opérations sensibles sous une session valide. Avec CIBA, l’agent reste dans un état **non privilégié** pour cette transaction jusqu’à l’Approbation requise.

* * *

### Découpler l’initiation et l’approbation avec CIBA

L’idée centrale est de **découpler** l’authentification / l’approbation de l’application qui consomme le résultat.

Avec CIBA, le client (ici : l’app ou le backend de l’**AI Agent**) initie le flux **en back-channel** vers le fournisseur OAuth 2.0, **sans** rediriger l’utilisateur via un navigateur sur l’appareil de consommation. La vérification se fait indépendamment sur un **autre** appareil (téléphone ou device enrôlé) en possession de l’humain responsable.

Pour un agent, cela signifie :

1.  L’agent détecte une action à risque (parce qu'on lui a "expliqué" au préalable ce qu'est une action à risque) et envoie une requête CIBA à Okta.
    
2.  Okta pousse une notification sur le mobile/device de la personne en charge d'approuver (pas une page de login sur l’UI de l’agent !).
    
3.  L’humain lit le contexte et Approuve ou Refuse.
    
4.  Okta délivre (ou non) un token **scopé utilisateur** à l’application, avec une durée de vie limitée : juste assez pour conclure *cette* opération.
    

Bénéfices directs :

*   **Expérience** : l’humain tranche sur son appareil de confiance, sans avoir à saisir de nouveau ses credentials sur le canal de l’agent.
    
*   **Sécurité** : le push enrôlé offre en pratique une meilleure garantie qu’un OTP SMS hors bande « faible ».
    
*   **Moindre privilège** : le token obtenu après l'action "Approve" est étroitement lié à l’utilisateur et à la transaction, plutôt qu’à un super-pouvoir permanent côté agent.
    
    * * *
    

## Deux appareils, un feu vert humain

CIBA étend OIDC : flux **découplé**, initié sur un appareil, vérifié sur un autre.

| Rôle | Nom Okta | Dans un scénario agent | Rôle |
| --- | --- | --- | --- |
| Qui initie | **Consumption device** | UI / backend de l’**AI Agent** (app Web OIDC) | Envoie `/bc/authorize`, poll le résultat |
| Qui valide | **Authentication device** | Mobile de l’humain + **Custom Authenticator** | Affiche le contexte, Approve / Deny |

![](https://cdn.hashnode.com/uploads/covers/6516c056c08f5e5c38de58d9/aa99a3c6-8e62-4713-9293-0b2bfd3ea80c.png align="center")

L’agent continue son raisonnement / son parcours UI. L’humain reçoit le push, lit le `binding_message`, tranche. Okta délivre (ou non) les tokens - seulement alors l’action peut aboutir (ou non).

* * *

## MFA vs CIBA : l’agent n’a pas besoin d’un second login — il a besoin d’un feu vert (approbation)

Le MFA protège surtout **l’entrée en session** (souvent celle de l’humain qui utilise l’agent, ou le sign-on associé).

CIBA protège **la transaction** que l’agent s’apprête à exécuter.

![](https://cdn.hashnode.com/uploads/covers/6516c056c08f5e5c38de58d9/a2c9e0be-5781-4546-9780-f7f2d698bd1e.png align="center")

Le "`binding_message`" est le texte affiché sur le mobile. Pour un agent, il doit décrire l’action tentée, pas un login générique :

*   *« L’agent demande à supprimer 12 enregistrements en production »*
    
*   *« L’agent demande un virement de 15 000 € »*
    
*   *« L’agent demande à exporter la base clients »*
    

Sans ce contexte, un push ressemble à une fatigue MFA. Avec lui, c’est une **preuve de volonté humaine** sur une action d’agent nommée.

* * *

## Le flux Agent IA → Okta → Humain → Agent IA

Quatre acteurs : l’**AI Agent** (qui initie), le *consumption device*, l’authorization server Okta, l’*authentication device* (humain).

![](https://cdn.hashnode.com/uploads/covers/6516c056c08f5e5c38de58d9/d8fea2b3-e244-46f0-85c8-2507d44ea124.png align="center")

### Déroulement

1.  L’**agent** détecte une action à haut risque et initie CIBA via le *consumption device* (son app / backend OIDC).
    
2.  Requête `POST` `/oauth2/.../v1/bc/authorize` avec :
    
    *   `scope` (dont `openid`, requis) ;
        
    *   **soit** `id_token_hint`, **soit** `login_hint` (pas les deux) pour identifier l’humain qui doit approuver ;
        
    *   `binding_message` décrivant l’action de l’agent ;
        
    *   optionnellement `request_expiry`.
        
3.  Okta notifie le Custom Authenticator **et** renvoie `auth_req_id` (+ `expires_in`, `interval`).
    
4.  L’humain est notifié sur le mobile.
    
5.  L’agent / le backend **poll** `/token` (`grant_type=urn:openid:params:grant-type:ciba` + `auth_req_id`).
    
6.  Tant que l’humain n’a pas répondu : `authorization_pending` — l’agent **n’exécute pas**.
    
7.  Après **Approve** : tokens délivrés.
    
8.  Seulement alors : appel outil / API métier. Si **Deny** ou timeout : l’agent s’arrête ou bascule sur un chemin de secours.
    

`id_token_hint` convient quand l’humain est déjà authentifié auprès du même authorization server (session liée à l’usage de l’agent). `login_hint` identifie typiquement l’humain par email.

Sans `request_expiry`, la validité documentée par défaut est **300 secondes**.

* * *

## CIBA, sur OAuth 2.0 et OIDC

CIBA est une **extension d’OIDC** (elle-même fondée sur OAuth 2.0). Elle ajoute le grant :

`urn:openid:params:grant-type:ciba`

Comme pour les autres flux OIDC, le document de *discovery* expose des métadonnées supplémentaires, notamment `backchannel_token_delivery_modes_supported` et `backchannel_authentication_endpoint`.

La spécification définit trois modes pour notifier le client que l’authentification / l’approbation est terminée :

| Mode | Principe |
| --- | --- |
| **Poll** | Le client (backend / app de l’agent) interroge l’AS jusqu’au succès ou timeout. |
| **Ping** | Callback statut vers une URL du client, puis demande de tokens. |
| **Push** | Callback avec les tokens. |

**Poll** est le plus simple à implémenter. **Ping** et **Push** réduisent les allers-retours réseau, mais demandent plus de métadonnées et d’implémentation côté client.

**Okta Workforce :** mode documenté pour Transactional Verification = **poll** uniquement (`backchannel_token_delivery_mode` = `poll`). Le serveur d'autorisation Okta expose le grant CIBA ; l’authentification hors bande s’appuie seulement sur un **Custom Authenticator** (Devices SDK), embarqué dans l’application mobile métier ou dans une application compagnon.

Parce que la requête CIBA part en back-channel, le serveur d'autorisation doit pouvoir **identifier l’humain** à notifier : via `login_hint` ou `id_token_hint` (l’un ou l’autre, pas les deux).

* * *

## Configurer CIBA pour un agent (org Okta)

Trois briques - le *consumption device* est l’app OIDC de l’**AI Agent** (ou de sa plateforme) :

![](https://cdn.hashnode.com/uploads/covers/6516c056c08f5e5c38de58d9/35b3112f-1b76-4d58-9212-5a4ed3a27819.png align="center")

### 1\. App OIDC Web de l’agent (consumption)

1.  Admin Console → **Applications** → Create App Integration.
    
2.  **OIDC** → **Web Application** (type supporté pour CIBA dans le guide).
    
3.  Grant types → Advanced → **Client-initiated backchannel authentication (CIBA)**.
    
4.  **Preferred authenticator for CIBA** → Custom Authenticator.
    
5.  Redirect URIs, Client ID / secret (ou autre auth client documentée).
    

API : `grant_types` inclut `urn:openid:params:grant-type:ciba`, plus `backchannel_custom_authenticator_id`, et optionnellement `backchannel_token_delivery_mode=poll`.

### 2\. Authorization Server (AS)

Org AS ou custom AS (`default`, etc.) :

1.  **Security** → **API** → Authorization Servers.
    
2.  Access policy → Advanced → activer le grant **CIBA**.
    
3.  Update rule.
    

### 3\. Custom Authenticator (côté humain)

1.  Custom Authenticator via Devices SDK.
    
2.  Device enrôlé pour l’utilisateur responsable.
    
3.  Transactions CIBA activées (option utilisateur dans le sample Okta, ou activées par défaut dans une app métier).
    

### Tant que CIBA n’a pas abouti, l’agent n’exécute pas

Le push seul ne sécurise rien si l’agent exécute quand même.

1.  L’agent (ou sa gateway) **bloque** l’outil à risque tant que CIBA n’a pas réussi.
    
2.  Après Approve, il dispose des tokens.
    
3.  L’**API / outil** cible n’accepte l’opération sensible qu’avec une preuve valide - en plus des scopes d’autorisation habituels.
    

**Autorisation + HITL**, pas l’un ou l’autre.

* * *

## Au-delà des agents : les mêmes fondations métier

CIBA n’a pas été inventé uniquement pour l’IA. Le blog Developer Okta décrit des cas où le login classique ne suffit pas : mise à jour d’email en centre d’appels, paiement POS sur terminal partagé, helpdesk, kiosques, contextes réglementaires (FAPI / Open Banking selon les régions).

La même mécanique sert l’actualité agents : **initiation découplée, approbation humaine out-of-band, token en sortie**.

* * *

## Points de vigilance

**Fatigue MFA / pushes répétés.** Un attaquant (ou un agent mal configuré qui re-tente en boucle) peut saturer l’humain de demandes. Risque : Approve par lassitude. Côté agent, le rate limiting, un timeout clair et l’absence de retry agressif après Deny limitent ce risque.

**Initiation forcée.** Okta rappelle qu’un attaquant peut forcer l’initiation d’événements d’autorisation (guess d’identifiants, listes). Pour un agent, cela renforce l’intérêt de ne déclencher CIBA que sur une **liste courte** d’actions à haut risque - pas sur chaque tool call.

**Same-device.**

> « CIBA should not be used in a same-device scenario where the consumption and authentication devices are the same. »

Source : [Introducing CIBA for Secure Transaction Verification](https://developer.okta.com/blog/2025/07/31/ciba-okta).

Si l’humain utilise l’agent **sur le même téléphone** que le Custom Authenticator, ce n’est pas le scénario recommandé pour CIBA.

Dans certains cas, Okta évoque le *device code flow* (initiation active par l’utilisateur via QR / code) comme alternative plus sûre face à l’initiation forcée.

* * *

## Quand déclencher CIBA dans un agent ?

| Situation | CIBA / HITL |
| --- | --- |
| Tool call destructif (delete, wipe, revoke) | Oui |
| Montant / volume au-delà d’un seuil | Oui |
| Export de données sensibles | Oui |
| Lecture / recherche à faible impact | Non (scopes suffisent en général) |
| MFA de login utilisateur | Non - autre mécanisme |
| App agent + Approve sur le même device | Non recommandé |

* * *

## Ce que ça change pour sécuriser les agents

Protéger la porte d’entrée ne suffit plus. Dès qu’un agent opère avec des outils, il faut une vigilance **continue** : zero trust aussi *pendant* l’exécution, pas seulement au login.

Au départ la question est : *quel token l’agent possède-t-il ?* Elle devient : *pour cette action-là, un humain a-t-il confirmé et le backend peut-il le prouver ?*

*   **Identité & autorisation** : qui est l’agent, quels scopes ?
    
*   **HITL via CIBA** : feu vert ponctuel, contextuel, out-of-band.
    
*   **Preuve API** : pas d’exécution sensible sans token / preuve après Approve.
    

Transactional Verification ne remplace pas XAA, STS ou les policies. Elle les **complète** - là où l’autonomie de l’agent croise le risque métier.

* * *

## Pour aller plus loin

*   [Transactional verification using CIBA - Okta Developer](https://developer.okta.com/docs/guides/configure-ciba/main/)
    
*   [Introducing CIBA for Secure Transaction Verification - Okta Developer Blog](https://developer.okta.com/blog/2025/07/31/ciba-okta)
    
*   [The role of AI in IAM: Securing the agentic frontier - Okta (HITL + CIBA pour actions à haut risque)](https://www.okta.com/identity-101/role-of-ai-in-iam/)
