Aller à la documentation
API

Portées

Ce qu'une clé a le droit de faire.

Le vocabulaire

Un ensemble clos, resource:action. Assez petit pour être montré à un humain sous forme de cases à cocher, et assez stable pour qu'un octroi stocké signifie encore la même chose un an plus tard. C'est dans ce même vocabulaire qu'est écrit un RÔLE d'espace de travail et c'est lui qui garde les outils MCP, si bien qu'un client en lecture seule ne voit même pas un outil d'envoi. Un alphabet, trois surfaces.

PortéeAccorde
emails:sendEnvoyer des e-mails
emails:readLire les messages envoyés et leur statut de livraison
drafts:readLire les brouillons
drafts:writeCréer et modifier des brouillons
threads:readLire les fils et les messages
threads:writeÉtiqueter, marquer comme lus et archiver des fils
labels:readLire les libellés
labels:writeCréer et modifier des libellés
contacts:readLire les contacts
contacts:writeAjouter, modifier et supprimer des contacts
audiences:readLire les audiences et qui s'y trouve
audiences:writeCréer et modifier des audiences, et changer qui s'y trouve
calendar:readLire les événements et invitations du calendrier
calendar:writeCréer et modifier des événements de calendrier, et y répondre
templates:readLire les modèles d'e-mail et les prévisualiser
templates:writeCréer et modifier des modèles d'e-mail, et envoyer avec
domains:readLire les domaines et leur statut DNS
domains:writeVérifier et configurer des domaines
webhooks:readLire les endpoints de webhook et les livraisons
webhooks:writeCréer, modifier et tester des webhooks
rules:readLire les règles de courrier et les tester
rules:writeCréer, modifier et réordonner les règles de courrier
connections:readLire quelles boîtes aux lettres sont connectées
members:readVoir qui est dans l'espace de travail et ce que chacun détient
members:writeAjouter et retirer des personnes, et changer ce à quoi elles ont accès
roles:readLire les rôles que cet espace de travail définit
roles:writeCréer, modifier et supprimer des rôles
settings:readLire les réglages de la boîte, y compris la signature
settings:writeModifier les réglages de la boîte et la signature
keys:writeRemplacer son propre secret sans que personne n'ouvre la console

Une clé créée sans liste de portées réfléchie reçoit emails:send et rien d'autre. La valeur par défaut sûre pour un identifiant est la plus étroite qui le rende utile.

Une clé est plafonnée par le rôle qui la porte

Une clé peut être émise sous un RÔLE, et un rôle est un plafond plutôt qu'un second octroi. Ce que la clé peut réellement faire, ce sont ses propres portées INTERSECTÉES avec les permissions de ce rôle (key.scopes ∩ role.permissions), calculé une fois à la frontière, à chaque requête, avant d'atteindre le moindre endpoint. Rien en aval ne sait que les rôles existent : une portée que le rôle ne détient pas est tout simplement absente de la liste que consultent les contrôles de portée.

Les deux listes se lisent donc ensemble et aucune ne l'emporte seule. Une clé portant emails:send sous un rôle qui ne le détient pas ne peut pas envoyer ; un rôle détenant emails:send n'apporte rien à une clé qui ne l'a jamais demandé. Cocher une portée, c'est demander une autorité, et le rôle décide de la part de ce que vous avez demandé que vous obtenez.

Une clé SANS rôle n'a pas de plafond et est donc aussi large que l'espace de travail sous lequel elle a été émise. C'est ce que porte toute clé créée avant l'existence des rôles et ce qu'un propriétaire obtient encore en laissant le champ tel quel : un rôle null est donc l'état le PLUS large d'une clé, pas le plus étroit. C'est aussi pourquoi supprimer un rôle vous oblige à dire où doivent aller ses clés : les laisser orphelines les promouvrait toutes en silence.

L'intersection est résolue par requête plutôt qu'estampillée sur la clé à l'émission. Cela fait de la restriction d'un rôle une révocation en direct, en vigueur dès l'appel suivant de l'appelant sans rotation de la clé, et de son élargissement quelque chose d'exactement aussi immédiat, ce qui est la moitié qu'il vaut la peine de retenir.

GET /ping et GET /keys/self renvoient scopes à côté de grantedScopes et roleId pour un échec bien précis. scopes est la liste effective et la seule qui autorise quoi que ce soit ; grantedScopes est ce avec quoi la clé a été émise. Tout ce qui figure dans la seconde et manque dans la première a été pris par le rôle, et cet écart est toute la réponse à « ma clé a emails:send et je reçois insufficient_scope ». Le correctif est un changement de rôle, pas une nouvelle clé.

Une clé que le rôle a restreinte
curl "$OE/ping" -H "$AUTH" {  "ok": true,  "keyId": "4c1b257a66287fd113bd89d0",  "mode": "live",  "scopes": ["emails:read", "threads:read"],  "roleId": "role_c40a95f21cc65d31c2a89e07",  "grantedScopes": ["emails:send", "emails:read", "threads:read"],  "workspaceId": "10417196-e324-4283-af98-66ec62167c47"}

Cinq permissions ne peuvent jamais atteindre une clé : api-keys:read, api-keys:write, billing:read, billing:write et workspace:manage. Ce sont des permissions mais pas des portées : aucun rôle, si généreux soit-il, ne peut les poser sur un jeton, car émettre une autre clé, changer ce qu'une autre clé peut faire ou modifier le plan sont des choses que seule une personne connectée fait. La seule chose qu'une clé peut se faire à elle-même est de remplacer son propre secret, derrière la portée keys:write. GET /roles/permissions marque les cinq avec scope: false, ce qui permet à un seul composant de rendre à la fois la matrice des rôles et la liste de cases à cocher de création de clé.

roles:write équivaut en pratique à tout le vocabulaire, et prétendre le contraire serait la documentation la plus dangereuse. Une clé qui le détient peut faire un PATCH sur le rôle même qui la plafonne et s'octroyer tout le reste, et comme le plafond est résolu par requête, le plus large s'applique dès l'appel suivant. Ce n'est pas une faille à combler, puisqu'un éditeur de rôles qui ne peut pas modifier les rôles n'est pas un éditeur de rôles. C'est une raison de ne pas mettre roles:write sur une clé qui n'avait jamais eu besoin que de lire la liste des membres.

Portée d'envoi

Indépendamment des portées, une clé peut être restreinte dans ce en tant que quoi elle peut envoyer. Elle porte deux listes. domainAllowlist contient des domaines entiers, et une clé détenant un domaine peut envoyer depuis n'importe quelle adresse de ce domaine, y compris des adresses créées après la clé. addressAllowlist contient des adresses individuelles. Laissez les deux vides et la clé est aussi large que l'espace de travail, jamais plus. GET /keys/self montre les deux listes et GET /addresses indique ce qu'une clé donnée peut réellement utiliser, ce qui est la réponse à un from_address_forbidden inexpliqué.

Le même ensemble restreint ce que la clé lit. Le courrier envoyé, le suivi et le calendrier ne répondent que pour les adresses depuis lesquelles la clé peut envoyer : une clé limitée à un domaine n'envoie ni ne lit pour le compte d'un autre. Un domaine entier permet aussi à la clé de définir l'hôte de suivi de ce domaine, ce qu'une clé limitée à des adresses individuelles ne peut pas faire.

Trois restrictions, donc, et elles se composent plutôt qu'elles ne se remplacent : les portées de la clé, les permissions du rôle au-dessus d'elle, et les domaines et adresses qu'elle peut mettre dans un en-tête From. Un envoi a besoin des trois, et un refus ne nomme que la première rencontrée.