Aller au contenu
Docs

Guide

Permettez aux utilisateurs de connecter votre application à leur espace de travail Emailit avec OAuth 2.1 et PKCE : enregistrement du client, actualisation des jetons, révocation et erreurs.

Mis à jour le 1 oct. 2026

Emailit exploite un serveur d’autorisation OAuth 2.1 à l’adresse https://api.emailit.com. Utilisez-le quand vous créez une intégration (un CRM, un outil no-code ou un client IA, par exemple) qui agit au nom d’utilisateurs Emailit. Vos utilisateurs se connectent et approuvent l’accès dans le navigateur, et vous obtenez des jetons pour les espaces de travail qu’ils choisissent sans jamais manipuler leurs clés API. C’est le même flux que celui du serveur MCP.

Fonctionnement

  1. Enregistrez un client avec l’enregistrement dynamique de client, ou hébergez un document de métadonnées d’ID client.

  2. Envoyez l’utilisateur vers l’URL d’autorisation avec un défi de code PKCE (code challenge).

  3. L’utilisateur se connecte à Emailit, choisit les espaces de travail que votre application peut utiliser et approuve la portée demandée.

  4. Emailit redirige vers votre URI de redirection avec un code d’autorisation à usage unique.

  5. Échangez le code contre un jeton d’accès valable 15 minutes et un jeton d’actualisation.

  6. Appelez l’API avec le jeton d’accès, et actualisez-le quand il expire.

Endpoints

Méthode Chemin Rôle
GET /.well-known/oauth-authorization-server Métadonnées du serveur d’autorisation (RFC 8414)
GET /.well-known/oauth-protected-resource Métadonnées de ressource protégée pour l’API REST
GET /.well-known/oauth-protected-resource/mcp Métadonnées de ressource protégée pour le serveur MCP
POST /oauth/register Enregistrement dynamique de client (RFC 7591)
GET /oauth/authorize Connexion et consentement dans le navigateur
POST /oauth/token Échanger un code ou un jeton d’actualisation
POST /oauth/revoke Révoquer une autorisation avec son jeton d’actualisation (RFC 7009)
GET /oauth/grants Lister les autorisations (celles de l’utilisateur connecté, ou celles d’un espace de travail avec une clé API)
POST /oauth/grants/:id/revoke Révoquer une autorisation
PUT /oauth/grants/:id/workspaces Modifier les espaces de travail qu’une autorisation peut utiliser (par l’utilisateur qui a connecté l’application)

Tous les chemins se trouvent sur https://api.emailit.com.

Découvrir le serveur

Terminal
curl https://api.emailit.com/.well-known/oauth-authorization-server
JSON
{
  "issuer": "https://api.emailit.com",
  "authorization_endpoint": "https://api.emailit.com/oauth/authorize",
  "token_endpoint": "https://api.emailit.com/oauth/token",
  "registration_endpoint": "https://api.emailit.com/oauth/register",
  "revocation_endpoint": "https://api.emailit.com/oauth/revoke",
  "jwks_uri": "https://api.emailit.com/.well-known/jwks.json",
  "code_challenge_methods_supported": ["S256"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "response_types_supported": ["code"],
  "token_endpoint_auth_methods_supported": ["none", "client_secret_basic", "client_secret_post"],
  "client_id_metadata_document_supported": true,
  "authorization_response_iss_parameter_supported": true,
  "scopes_supported": ["sending", "full"]
}

Les clients MCP partent plutôt des métadonnées de ressource protégée. Une requête non authentifiée vers https://api.emailit.com/mcp renvoie 401 avec WWW-Authenticate: Bearer resource_metadata="https://api.emailit.com/.well-known/oauth-protected-resource/mcp", et ce document pointe vers le serveur d’autorisation :

JSON
{
  "resource": "https://api.emailit.com/mcp",
  "authorization_servers": ["https://api.emailit.com"],
  "bearer_methods_supported": ["header"],
  "scopes_supported": ["sending", "full"]
}

Portées

Portée Accès accordé
sending Envoi et transfert d’e-mails, et reprogrammation, annulation et relance des envois. Le même accès qu’une clé API Sending Only.
full Tous les endpoints de l’API REST et tous les outils MCP. Le même accès qu’une clé API Full Access.

full inclut déjà tout ce que permet sending. Demandez sending si votre application ne fait qu’envoyer, et full sinon ; la valeur sending full, séparée par une espace, est aussi acceptée. Si vous omettez scope, Emailit utilise toutes les portées enregistrées par le client.

Enregistrer votre client

Vous pouvez enregistrer un client de deux façons. Les deux fonctionnent avec tous les flux de cette page.

Enregistrement dynamique de client

Envoyez une requête d’enregistrement. Aucune authentification n’est nécessaire, et chaque adresse IP peut enregistrer jusqu’à 20 clients par heure.

Terminal
curl https://api.emailit.com/oauth/register \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "Acme CRM",
    "redirect_uris": ["https://crm.acme.com/oauth/emailit/callback"],
    "grant_types": ["authorization_code", "refresh_token"],
    "response_types": ["code"],
    "scope": "full",
    "token_endpoint_auth_method": "client_secret_basic",
    "client_uri": "https://crm.acme.com",
    "logo_uri": "https://crm.acme.com/logo.png"
  }'
Champ Obligatoire Description
client_name Oui Affiché sur la page de consentement. 200 caractères au maximum.
redirect_uris Oui De 1 à 10 URI de redirection. Voir Règles des URI de redirection.
grant_types Non Doit inclure authorization_code ; peut inclure refresh_token. Par défaut : les deux.
response_types Non Uniquement code.
scope Non Portées séparées par des espaces. Par défaut : sending full.
token_endpoint_auth_method Non none (par défaut) pour les clients publics comme les applications de bureau, mobiles et web ; client_secret_basic ou client_secret_post pour les applications côté serveur.
client_uri Non Page d’accueil de votre application.
logo_uri Non Logo affiché sur la page de consentement.

La réponse 201 reprend les métadonnées et ajoute un client_id. Les clients confidentiels reçoivent aussi un client_secret, affiché une seule fois :

JSON
{
  "client_id": "3e8a1f52-7c4b-4d29-8e6f-1a5b9c0d2e73",
  "client_id_issued_at": 1790847000,
  "client_name": "Acme CRM",
  "redirect_uris": ["https://crm.acme.com/oauth/emailit/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "scope": "full",
  "token_endpoint_auth_method": "client_secret_basic",
  "client_uri": "https://crm.acme.com",
  "logo_uri": "https://crm.acme.com/logo.png",
  "client_secret": "Ky7WI2z5HxoT6q1z19tuiIev_U1zX0_FKhIIfh0OBco",
  "client_secret_expires_at": 0
}

client_secret_expires_at vaut toujours 0 : les secrets n’expirent pas. Tous les clients, publics ou confidentiels, doivent utiliser PKCE.

Document de métadonnées d’ID client

Si votre application peut héberger un fichier JSON statique, vous pouvez vous passer de l’enregistrement. Publiez un document à une URL HTTPS et utilisez cette URL comme client_id :

https://crm.acme.com/oauth/client.json
{
  "client_id": "https://crm.acme.com/oauth/client.json",
  "client_name": "Acme CRM",
  "client_uri": "https://crm.acme.com",
  "logo_uri": "https://crm.acme.com/logo.png",
  "redirect_uris": ["https://crm.acme.com/oauth/emailit/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_method": "none",
  "scope": "full"
}
  • Le client_id du document doit être exactement égal à l’URL du document.
  • token_endpoint_auth_method doit valoir none. Ce sont des clients publics qui s’appuient sur PKCE.
  • Les URI de redirection HTTPS doivent se trouver sur le même hôte que le document.
  • Emailit récupère le document pendant l’autorisation, avec un timeout de 5 secondes et sans suivre les redirections, et le met en cache pendant 5 minutes.
  • La page de consentement affiche l’hôte du document, par exemple crm.acme.com, au lieu de client_name.

Les clients IA utilisent cette méthode : ChatGPT, Claude et Grok s’identifient avec leurs propres documents de métadonnées d’ID client et se connectent donc sans enregistrement préalable. Pour Grok, Emailit accepte aussi les redirections vers console.x.ai.

Règles des URI de redirection

  • Les URI https:// sont autorisées.
  • http:// n’est autorisé que pour les hôtes de bouclage (loopback) : 127.0.0.1, localhost et [::1]. Pour les URI de bouclage, le port peut différer au moment de l’autorisation, mais l’hôte, le chemin et la chaîne de requête doivent correspondre. localhost et 127.0.0.1 sont des hôtes différents.
  • Les schémas à usage privé comme cursor://, vscode:// ou com.acme.crm:// sont autorisés pour les applications natives.
  • Les URI file, ftp, data, javascript, blob, about et vbscript, ainsi que toute URI contenant un #fragment, sont rejetées.
  • En dehors des ports de bouclage, le redirect_uri que vous envoyez doit correspondre exactement à une URI enregistrée.

Envoyer l’utilisateur vers Emailit

Pour chaque autorisation, créez un vérificateur de code (code verifier) et un défi PKCE, ainsi qu’un state aléatoire :

JavaScript
import { createHash, randomBytes } from 'node:crypto';

const codeVerifier = randomBytes(32).toString('base64url');
const codeChallenge = createHash('sha256').update(codeVerifier).digest('base64url');
const state = randomBytes(16).toString('base64url');
// Store codeVerifier and state in the user's session.

Redirigez ensuite le navigateur de l’utilisateur vers l’endpoint d’autorisation :

Text
https://api.emailit.com/oauth/authorize
  ?response_type=code
  &client_id=3e8a1f52-7c4b-4d29-8e6f-1a5b9c0d2e73
  &redirect_uri=https%3A%2F%2Fcrm.acme.com%2Foauth%2Femailit%2Fcallback
  &scope=full
  &state=Jq3k9V0n2xR7bLm1
  &code_challenge=zsrvXVr2pbLDHbccAx_NY9osQ6nwqTnDZlIeFWKQOsg
  &code_challenge_method=S256
  &resource=https%3A%2F%2Fapi.emailit.com
Paramètre Obligatoire Description
response_type Oui code
client_id Oui Votre ID client, ou l’URL de votre document de métadonnées.
redirect_uri Oui L’une de vos URI de redirection enregistrées.
code_challenge Oui SHA-256 du vérificateur de code, encodé en Base64url.
code_challenge_method Recommandé S256. plain n’est pas pris en charge.
scope Recommandé sending ou full. Doit être une portée enregistrée par le client.
state Recommandé Une valeur aléatoire que vous vérifiez au retour (callback).
resource Non La ressource que vous voulez appeler (RFC 8707) : https://api.emailit.com pour l’API REST ou https://api.emailit.com/mcp pour MCP. Dans les deux cas, les jetons fonctionnent sur les deux.

Ouvrez cette URL dans le navigateur de l’utilisateur. Ne la récupérez pas depuis votre backend.

Ce que voit l’utilisateur

  1. Connexion à Emailit. L’utilisateur saisit son adresse e-mail et son mot de passe. S’il utilise une application d’authentification pour l’authentification à deux facteurs, il saisit ensuite son code ou un code de récupération.
  2. Choix des espaces de travail. La page affiche votre logo, le nom de votre client (ou l’hôte de votre document de métadonnées) et les portées demandées. L’utilisateur choisit All my workspaces, qui inclut les espaces de travail qu’il créera ou rejoindra plus tard, ou Only these workspaces avec ceux qu’il coche, puis choisit l’espace de travail dans lequel votre application démarre.
  3. Autorisation de l’accès. Il sélectionne Allow access ou Deny.

Une autorisation peut couvrir plusieurs espaces de travail. L’utilisateur peut modifier la liste plus tard sous Account > Connected apps dans le tableau de bord, et votre application voit la modification à sa requête suivante.

Après approbation, Emailit redirige vers votre URI de redirection :

Text
https://crm.acme.com/oauth/emailit/callback?code=XvRKyG5Pkplrd3xkRNaEayKkpkebEMdF&state=Jq3k9V0n2xR7bLm1&iss=https%3A%2F%2Fapi.emailit.com

Vérifiez que state correspond à la valeur enregistrée et que iss vaut https://api.emailit.com. Le code est valable 10 minutes et ne peut être utilisé qu’une fois. Si l’utilisateur sélectionne Deny, la redirection contient error=access_denied à la place.

Échanger le code contre des jetons

Envoyez une requête POST à l’endpoint de jeton au format application/x-www-form-urlencoded (le JSON est aussi accepté). Envoyez le même redirect_uri et le vérificateur de code d’origine :

Terminal
curl https://api.emailit.com/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=authorization_code \
  -d code="$CODE" \
  -d redirect_uri=https://crm.acme.com/oauth/emailit/callback \
  -d code_verifier="$CODE_VERIFIER"

Avec client_secret_basic, envoyez l’ID client et le secret dans l’en-tête Authorization: Basic. Avec client_secret_post, envoyez plutôt client_id et client_secret comme champs de formulaire. N’envoyez pas le secret des deux façons.

JSON
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im9hdXRoLWhzMjU2In0…",
  "token_type": "Bearer",
  "expires_in": 900,
  "refresh_token": "XvRKyG5Pkplrd3xkRNaEayKkpkebEMdFn0h1VIuqixAOXybMtJfK8w",
  "scope": "full"
}

Le jeton d’accès est valable 15 minutes (expires_in est en secondes). Traitez-le comme une chaîne opaque : ne l’analysez pas et ne vous fiez pas à son contenu.

Appeler l’API

Utilisez le jeton d’accès comme jeton bearer sur l’API REST et le serveur MCP, exactement comme une clé API :

Terminal
curl https://api.emailit.com/v2/domains \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Les requêtes REST s’exécutent dans l’espace de travail par défaut de l’autorisation : celui dans lequel l’utilisateur a choisi de démarrer, ou celui qu’il a choisi ensuite. Les outils MCP peuvent aussi cibler un autre espace de travail autorisé avec leur argument workspace. Chaque requête utilise la portée accordée et le rôle de l’utilisateur dans l’espace de travail : une requête hors de la portée, ou une route réservée au rôle Admin appelée par un membre ayant le rôle Member, renvoie 403. Si l’utilisateur quitte un espace de travail, l’autorisation le perd aussi.

Actualiser les jetons

Avant l’expiration du jeton d’accès, ou quand une requête renvoie 401, obtenez-en un nouveau :

Terminal
curl https://api.emailit.com/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=refresh_token \
  -d refresh_token="$REFRESH_TOKEN"
  • Rotation. Chaque actualisation renvoie un nouveau refresh_token valable 60 jours. Enregistrez-le et supprimez l’ancien. Tant que vous actualisez dans les 60 jours, la connexion n’expire pas.
  • Détection de réutilisation. Si un ancien jeton d’actualisation est réutilisé plus d’une minute après son remplacement, Emailit renvoie invalid_grant (Refresh token reuse detected) et révoque toute l’autorisation. L’utilisateur doit alors autoriser de nouveau l’application. Sérialisez les actualisations pour que deux workers n’utilisent jamais le même jeton d’actualisation.
  • Portée plus restreinte. Vous pouvez transmettre scope pour obtenir un jeton d’accès avec moins de portées que l’autorisation. Vous ne pouvez pas en demander davantage.

Révoquer une autorisation

Quand un utilisateur déconnecte Emailit de votre application, révoquez l’autorisation avec son jeton d’actualisation :

Terminal
curl https://api.emailit.com/oauth/revoke \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d token="$REFRESH_TOKEN"

L’endpoint authentifie votre client de la même façon que l’endpoint de jeton et renvoie toujours 200 avec un corps vide. Révoquer le jeton d’actualisation révoque toute l’autorisation : ses jetons d’accès cessent de fonctionner dès la requête suivante. Les jetons d’accès ne peuvent pas être révoqués seuls ; token_type_hint=access_token renvoie invalid_request.

Les utilisateurs peuvent aussi révoquer l’accès de votre application de leur côté, sous Applications connectées dans le tableau de bord, ou avec l’API des autorisations ci-dessous.

Gérer les autorisations

L’API des autorisations liste et modifie les autorisations du côté de l’utilisateur. Elle accepte la session du tableau de bord de l’utilisateur ou une clé API Full Access :

Appelant GET /oauth/grants POST /oauth/grants/:id/revoke PUT /oauth/grants/:id/workspaces
L’utilisateur qui a connecté l’application Ses autorisations dans tous les espaces de travail Révoque toute l’autorisation Modifie les espaces de travail de l’autorisation
Clé API Full Access Autorisations qui incluent l’espace de travail de la clé Retire l’espace de travail de la clé de l’autorisation ; l’autorisation est révoquée quand il ne reste plus aucun espace de travail Non autorisé (403)

Chaque autorisation de la liste a cette forme :

JSON
{
  "id": "9b1f6c2e-4d3a-4f8e-9a7b-2c5d8e1f0a3b",
  "client_id": "https://chatgpt.com/oauth/client.json",
  "client": { "name": "ChatGPT", "uri": "https://chatgpt.com", "logo_uri": null },
  "default_workspace": { "id": "w3f9a1c2e", "name": "Acme" },
  "workspace_access": "selected",
  "workspaces": [{ "id": "w3f9a1c2e", "name": "Acme" }],
  "scopes": ["sending", "full"],
  "resource": "https://api.emailit.com/mcp",
  "created_at": "2026-10-01T09:30:12Z",
  "revoked_at": null,
  "revoked_reason": null
}

Pour modifier les espaces de travail d’une autorisation, envoyez access (all ou selected), workspace_ids pour selected, et éventuellement default_workspace_id, qui doit être l’un des espaces de travail autorisés :

Terminal
curl -X PUT https://api.emailit.com/oauth/grants/9b1f6c2e-4d3a-4f8e-9a7b-2c5d8e1f0a3b/workspaces \
  -H "Authorization: Bearer $DASHBOARD_SESSION" \
  -H "Content-Type: application/json" \
  -d '{ "access": "selected", "workspace_ids": ["w3f9a1c2e"], "default_workspace_id": "w3f9a1c2e" }'

L’utilisateur doit être membre de chaque espace de travail qu’il sélectionne. Une autorisation révoquée ne peut pas être modifiée (409) ; l’application doit se connecter de nouveau.

Erreurs

Les endpoints OAuth renvoient les erreurs au format OAuth, et non au format d’erreur de l’API REST :

JSON
{
  "error": "invalid_grant",
  "error_description": "Authorization code has expired"
}
Erreur Statut Cas
invalid_request 400 Un paramètre est absent ou invalide, le client est inconnu lors de l’autorisation, le redirect_uri n’est pas enregistré, code_challenge_method ne vaut pas S256, ou un secret client a été envoyé à la fois dans l’en-tête et dans le corps.
invalid_client 401 L’endpoint de jeton ou de révocation ne peut pas authentifier le client : client inconnu, secret absent ou incorrect.
invalid_grant 400 Le code est invalide, déjà utilisé ou expiré ; le redirect_uri ou le vérificateur PKCE ne correspond pas ; ou le jeton d’actualisation est invalide, expiré, révoqué ou réutilisé.
invalid_scope 400 La portée n’est pas prise en charge, n’a pas été enregistrée pour le client ou dépasse l’autorisation lors d’une actualisation.
unsupported_response_type 400 response_type ne vaut pas code.
unsupported_grant_type 400 grant_type ne vaut ni authorization_code ni refresh_token.
access_denied Redirection L’utilisateur a sélectionné Deny.
too_many_requests 429 Plus de 20 enregistrements par heure depuis une même adresse IP.
server_error 500 Un problème est survenu du côté d’Emailit. Réessayez.

Tant que le client_id et le redirect_uri ne sont pas validés, les erreurs d’autorisation s’affichent en JSON dans le navigateur et ne sont jamais redirigées. Ensuite, les erreurs ne sont redirigées vers votre callback (avec error, error_description, state et iss) que pour les URI de redirection de bouclage et à usage privé, et pour les clients à document de métadonnées ; les autres clients reçoivent une page d’erreur JSON. Un Deny de l’utilisateur est toujours redirigé.

Liste de contrôle de sécurité

  • Gardez client_secret et les jetons d’actualisation sur votre serveur, chiffrés au repos. Ne livrez jamais de secret client dans une application mobile, de bureau ou web ; utilisez plutôt un client public avec PKCE.
  • Générez un nouveau state et un nouveau vérificateur de code pour chaque autorisation, et vérifiez state et iss au retour (callback).
  • Enregistrez des URI de redirection exactes. N’utilisez pas de redirecteurs ouverts comme callbacks.
  • Enregistrez chaque autorisation avec l’espace de travail auquel elle appartient, et gérez invalid_grant en demandant à l’utilisateur de se reconnecter.
  • Demandez sending si vous ne faites qu’envoyer des e-mails.
Autorisations, durée de vie des jetons et révocation.
Les endpoints que vos jetons peuvent appeler.
Pour vos propres scripts, les clés sont plus simples.
Le flux OAuth en pratique.

Cette page vous a-t-elle été utile ?

Merci pour votre retour.

Merci, nous lisons chaque message.