Pourquoi ne pas simplement bloquer les agents pour les actions sensibles ?
Certaines actions de votre application comptent davantage que d'autres. Changer un mot de passe, ajouter un membre à l'équipe, exporter des données, supprimer un projet, approuver une commande : si l'une de ces opérations se produit sans l'autorité appropriée, les dégâts sont réels.
Dans le même temps, une part croissante du travail légitime dans votre application est effectuée par l'automatisation. Un utilisateur demande à un assistant de mettre à jour ses paramètres. Une intégration partenaire crée des enregistrements selon un calendrier. Un agent interne ouvre des tickets de support. Bloquer tout client automatisé sur les routes sensibles casse ce travail et renvoie les personnes vers des tâches manuelles ou des contournements plus difficiles à sécuriser.
La réponse n'est ni "bloquer les agents" ni "faire confiance aux agents". C'est un modèle dans lequel le risque de l'action, l'autorité derrière la requête et les éléments d'évidence sur l'acteur jouent chacun un rôle distinct. Ce guide construit ce modèle. Pour une réflexion plus large, voir concevoir des politiques au niveau de l'application pour les humains et les agents IA.
Étape 1 : classez vos routes selon le risque
Commencez par classer les actions, pas les clients. Un schéma simple à trois niveaux fonctionne pour la plupart des applications :
| Niveau | Exemples | Exigence par défaut |
|---|---|---|
| Faible | Lire des données publiques, rechercher, afficher son propre tableau de bord | Authentification normale, le cas échéant |
| Moyen | Créer ou modifier son propre contenu, inviter un lecteur | Authentifié, autorisé pour la ressource |
| Élevé | Modifier des identifiants, changer la propriété, exporter en masse, supprimer, approuver | Autorisé plus contrôle de renforcement ou revue |
Inscrivez le niveau dans la configuration de la route afin qu'il soit visible lors de la revue de code :
type Tier = "low" | "medium" | "high";
export const routeTier: Record<string, Tier> = {
"GET /projects": "low",
"POST /projects": "medium",
"POST /projects/:id/members": "medium",
"POST /account/password": "high",
"POST /exports": "high",
"DELETE /projects/:id": "high",
};Les niveaux évoluent lentement et sont faciles à raisonner. Tout le reste de ce guide en dépend.
Étape 2 : en quoi identité, identifiants et autorisation diffèrent-ils ?
Trois questions sont souvent confondues :
- Qui ou quoi agit ? Il s'agit de l'évidence sur l'acteur : une personne en interaction, une personne assistée par l'IA, un agent ou Unknown, avec une base telle que Verified ou Declared.
- Quel identifiant a été présenté ? Un cookie de session, une clé API, un jeton d'accès OAuth.
- Cet identifiant est-il autorisé à effectuer cette action sur cette ressource ? Il s'agit de l'autorisation.
Une signature d'agent vérifiée (RFC 9421) répond bien à la première question. Elle ne dit rien sur la troisième, point abordé en détail dans ce que prouvent les requêtes signées d'agents IA, et ce qu'elles ne prouvent pas. Un jeton OAuth avec le bon scope répond à la troisième question. Il dit peu de chose sur la première. Votre décision pour une action de niveau élevé a toujours besoin d'une bonne réponse à la troisième question, et utilise la première pour déterminer combien d'assurance supplémentaire demander.
interface Context {
actor: { actor: "human" | "human_ai" | "agent" | "unknown"; basis: "verified" | "declared" | "inferred" | "unknown" };
credential: { type: "session" | "oauth" | "api_key"; scopes: string[]; subject: string };
}
function authorized(ctx: Context, action: string, resourceOwner: string): boolean {
return ctx.credential.subject === resourceOwner && ctx.credential.scopes.includes(action);
}L'autorisation n'est jamais relâchée à cause d'un résultat d'acteur favorable. Le contexte de l'acteur n'ajoute que des exigences supplémentaires ou choisit entre des chemins également sûrs.
Étape 3 : utilisez une autorité déléguée pour les agents qui agissent pour des personnes
Lorsqu'un agent agit au nom d'un utilisateur, donnez-lui son propre identifiant avec une autorité limitée au lieu de lui permettre de réutiliser la session de l'utilisateur. OAuth 2.0 est l'outil standard : l'utilisateur approuve des scopes spécifiques pour un client spécifique, et le jeton enregistre ce client. RFC 9700, le OAuth 2.0 Security Best Current Practice, décrit comment le déployer en toute sécurité. Les serveurs MCP suivent le même modèle : la spécification d'autorisation MCP traite un serveur MCP protégé comme un resource server OAuth 2.1. Notre article qui appelle votre serveur MCP ? applique cela aux appels d'outils.
Cela produit exactement ce qu'il faut pour les actions sensibles :
- Le jeton vous indique quel client agit et pour qui.
- Les scopes limitent ce qu'il peut faire. Un agent de planification avec
calendar:writene peut pas changer un mot de passe. - L'utilisateur peut révoquer l'octroi sans changer ses propres identifiants, et OAuth 2.0 Token Revocation (RFC 7009) définit comment un client indique au serveur d'autorisation qu'un jeton ne doit plus être valide.
- Les journaux d'audit montrent que l'agent, et non l'utilisateur, a effectué l'action.
Si votre application ne prend pas encore en charge les identifiants délégués, faites-en une priorité pour les actions dont les agents ont le plus souvent besoin. C'est le contrôle le plus efficace pour l'accès automatisé.
Étape 4 : quand faut-il demander un contrôle de renforcement ?
Le contrôle de renforcement consiste à demander une assurance supplémentaire au moment d'une action sensible. Pour les API protégées par OAuth, RFC 9470 définit comment un resource server indique à un client que l'authentification du jeton actuel n'est pas assez forte ou assez récente. Le bon contrôle dépend de qui agit :
- Personne en interaction (Human, Human + AI) : réauthentification (voir les exigences de réauthentification dans NIST SP 800-63B), une invite de passkey construite sur WebAuthn, ou un code à usage unique.
- Agent avec identifiant délégué : un scope dédié à l'action de niveau élevé, accordé explicitement par l'utilisateur, ou une confirmation envoyée à l'utilisateur hors bande.
- Unknown : le contrôle fort existant de la route, sans modification.
type Requirement = "none" | "reauth" | "explicit_scope" | "user_confirmation" | "review";
function stepUp(tier: Tier, ctx: Context): Requirement {
if (tier !== "high") return "none";
const { actor } = ctx.actor;
if (actor === "human" || actor === "human_ai") return "reauth";
if (actor === "agent" && ctx.credential.type === "oauth") return "explicit_scope";
if (actor === "agent") return "user_confirmation";
return "reauth";
}Remarquez qu'un résultat Human ne saute pas le contrôle de renforcement. Il sélectionne le contrôle de renforcement adapté à une personne en interaction. Les inférences peuvent être erronées, elles doivent donc modifier la forme du contrôle, pas le supprimer.
Étape 5 : orientez vers une revue humaine quand le jugement est nécessaire
Certaines actions sont rares, à fort impact et difficiles à automatiser en toute sécurité : export en masse de données clients, récupération de compte après perte d'identifiants, ou suppression d'une organisation. Pour celles-ci, une file de revue est souvent préférable à toute règle automatisée.
Le contexte de l'acteur accélère et rend la revue plus équitable en donnant au réviseur les éléments d'évidence : la classe, la base, l'identifiant utilisé, l'historique des demandes similaires. Le réviseur décide. La classification informe.
function shouldReview(route: string, ctx: Context): boolean {
const reviewRoutes = new Set(["POST /exports", "POST /account/recover"]);
if (!reviewRoutes.has(route)) return false;
return !(ctx.credential.type === "oauth" && ctx.credential.scopes.includes("export:bulk"));
}Étape 6 : choisissez le comportement en cas d'échec pour chaque niveau
L'évaluation de l'acteur peut expirer ou manquer d'éléments d'évidence. Décidez à l'avance de ce qui se passe.
- Niveau faible : fail open. La requête se poursuit normalement.
- Niveau moyen : fail open, avec l'autorisation normale en place.
- Niveau élevé : fail closed vers le contrôle fort existant. L'absence d'évidence sur l'acteur n'affaiblit jamais la protection, et elle ne bloque jamais une requête qui aurait autrement passé vos contrôles normaux.
La propriété clé est la symétrie. Un échec dans l'évaluation de l'acteur doit laisser l'action exactement aussi sûre, et exactement aussi disponible, qu'avant l'ajout du contexte d'acteur.
Étape 7 : ne prenez pas de décisions conséquentes sur des personnes à partir d'inférences
Une classification inférée est une évidence sur une interaction, pas un fait sur une personne. Ne refusez pas une candidature, une décision de crédit, un résultat d'examen, un logement ou l'accès à un service essentiel parce qu'une session semblait automatisée ou assistée par l'IA. Dans ces flux, utilisez le contexte de l'acteur pour choisir un processus, comme une revue humaine ou un parcours de vérification alternatif, et assurez-vous que la personne puisse le terminer.
C'est à la fois un principe d'équité et une mesure pratique. Les inférences comportent des faux positifs, et le coût d'un faux positif dans ces flux retombe sur quelqu'un qui n'a rien fait de mal.
Étape 8 : déployez d'abord en observation
Avant que tout cela n'affecte des requêtes réelles, exécutez-le en mode observation et preview :
- Enregistrez le niveau, la classe d'acteur, la base, le type d'identifiant et le contrôle de renforcement que votre politique exigerait.
- Examinez des échantillons de requêtes de niveau élevé par classe. Les agents légitimes utilisent-ils des identifiants délégués, ou réutilisent-ils les sessions utilisateur ?
- Corrigez les écarts que vous trouvez, souvent en proposant des identifiants limités, avant d'appliquer l'enforcement.
- Appliquez l'enforcement sur un petit échantillon d'une seule route, mesurez les taux de complétion, puis élargissez.
Étape 9 : conservez une piste d'audit qui répond à "qui et au nom de qui"
Pour chaque action de niveau élevé, consignez assez d'informations pour reconstituer ce qui s'est passé plus tard :
- la classe d'acteur et la base au moment de la requête,
- le type d'identifiant, le client auquel il a été délivré et le sujet qu'il représente (OAuth 2.0 Token Exchange, RFC 8693, définit une revendication
actpour enregistrer exactement ce type de relation "agissant pour"), - les scopes qui ont autorisé l'action,
- l'exigence de contrôle de renforcement ou de revue appliquée et son résultat.
audit.write({
action: "project.export",
actor: ctx.actor,
client: ctx.credential.type === "oauth" ? clientIdFromToken(req) : undefined,
subject: ctx.credential.subject,
scopes: ctx.credential.scopes,
requirement,
outcome: "approved",
at: new Date().toISOString(),
});Avec cette trace, une question comme "une personne ou un assistant a-t-il supprimé ce projet, et qui l'a approuvé ?" a une réponse claire. Cela accélère la réponse aux incidents et donne aux utilisateurs la confiance que l'aide automatisée dans votre application est traçable.
Un exemple concret
Une application de gestion de projet voit un assistant mettre à jour des dates d'échéance de tâches pour des utilisateurs, et tenter parfois d'exporter des projets entiers.
- La mise à jour d'une tâche est de niveau moyen. L'assistant utilise un jeton OAuth avec
tasks:write. Autorisé, pas de contrôle de renforcement. Autorisé. - L'export d'un projet est de niveau élevé. Le jeton de l'assistant ne contient pas
export:bulk. L'application envoie à l'utilisateur une demande de confirmation. L'utilisateur approuve, et l'application accorde le scope pour ce client. - Un client Unknown tente le même export avec un cookie de session réutilisé. L'invite de réauthentification existante s'applique, exactement comme auparavant.
Personne n'a été bloqué parce qu'il s'agissait d'un agent. Chaque action à fort impact portait une autorité explicite.
Où se situe Proof of Actor
Proof of Actor fournit la partie liée à l'acteur dans ce modèle : qui ou quoi agit et sur quelle base. Votre application conserve l'authentification, l'autorisation et la décision finale. Detect l'acteur, Understand comment chaque classe utilise vos routes sensibles, et Route chaque requête vers le bon niveau d'assurance.
Pour concevoir cela pour votre application avec notre équipe, faites une demande d'accès anticipé. Pour des scénarios SaaS plus détaillés, voir SaaS Apps & Websites.
Sources
- RFC 6749: The OAuth 2.0 Authorization Framework : identifiants délégués et limités par scope pour les clients
- RFC 9700: Best Current Practice for OAuth 2.0 Security : recommandations de sécurité actuelles pour les déploiements OAuth
- RFC 7009: OAuth 2.0 Token Revocation : révoquer un octroi sans changer les identifiants de l'utilisateur
- RFC 9470: OAuth 2.0 Step Up Authentication Challenge Protocol : signaler qu'une authentification plus forte ou plus récente est requise
- RFC 8693: OAuth 2.0 Token Exchange : la revendication
act(actor) pour la délégation - MCP specification 2026-07-28: Authorization : les serveurs MCP comme resource servers OAuth 2.1
- NIST SP 800-63B: Authentication and Authenticator Management : directives de réauthentification
- W3C Web Authentication (WebAuthn) Level 3 : la norme derrière les invites de passkey
- RFC 9421: HTTP Message Signatures : ce qu'établit une signature d'agent vérifiée