---
title: "Se connecter avec OAuth"
description: "Autorisez les clients MCP et l’interface de ligne de commande ThunderPhone sans partager de clé API."
---

OAuth permet à une application de se connecter à une organisation ThunderPhone avec les autorisations que vous approuvez. Les clients d'annuaire doivent utiliser OAuth par défaut. Les clés API d'organisation restent disponibles pour les clients qui nécessitent un jeton Bearer configuré manuellement.

## Approuver une connexion

Démarrez la connexion dans votre client MCP. Connectez-vous à ThunderPhone, vérifiez le nom de l'application et les autorisations demandées, choisissez une organisation, puis sélectionnez **Approuver**. Sélectionnez **Refuser** si vous n'avez pas démarré la connexion ou si vous ne faites pas confiance à l'application. Les noms d'application sont fournis par leurs développeurs et ne constituent pas un badge de vérification.

Les autorisations de lecture donnent accès aux données indiquées, y compris aux enregistrements et aux transcriptions lorsque `calls:read` est demandé. Les autorisations d'écriture peuvent modifier ou supprimer des ressources ; les appels, les campagnes, les achats de numéros de téléphone et les déploiements peuvent entraîner des coûts ou affecter la production. Votre rôle dans l'organisation s'applique toujours.

Pour une connexion CLI, ouvrez le lien de vérification affiché dans votre terminal, comparez le code à huit caractères, sélectionnez votre organisation, puis approuvez. Le simple fait d'ouvrir le lien n'accorde pas l'accès. Les codes expirent après 15 minutes.

## Déconnecter une application

Ouvrez **Organisation → Clés API → Applications autorisées** dans le tableau de bord, puis choisissez **Révoquer**. Cette action révoque l'autorisation sélectionnée pour l'organisation actuelle, y compris ses jetons d'accès et d'actualisation. Reconnectez-vous depuis l'application si vous souhaitez l'autoriser à nouveau.

## Découverte pour les clients MCP

Utilisez l’URL du serveur `https://api.thunderphone.com/v1/mcp`. Une requête sans authentification valide reçoit `401` avec :

```http
WWW-Authenticate: Bearer resource_metadata="https://api.thunderphone.com/.well-known/oauth-protected-resource"
```

Récupérez ce document, puis récupérez les métadonnées du serveur d’autorisation à `https://api.thunderphone.com/.well-known/oauth-authorization-server`. Les métadonnées de ressource sont également disponibles à `/.well-known/oauth-protected-resource/v1/mcp`. Utilisez les points de terminaison renvoyés au lieu de les construire. L’identifiant de ressource est `https://api.thunderphone.com/v1/mcp`.

Le serveur prend en charge le code d’autorisation avec **S256 PKCE**, les jetons d’actualisation rotatifs, l’enregistrement dynamique de clients publics, la révocation et le flux d’autorisation d’appareil. Il n’existe ni secrets client ni flux implicites. La découverte OpenID est également disponible à `/.well-known/openid-configuration` ; elle inclut les mêmes champs de serveur d’autorisation, ainsi que `subject_types_supported: ["public"]` et le point de terminaison userinfo. Les jetons ID et les documents de métadonnées d’ID client (CIMD) ne sont pas pris en charge.

### Enregistrer un client public

Envoyez du JSON à `POST /v1/oauth/register` :

```json
{
  "client_name": "My MCP client",
  "redirect_uris": ["http://127.0.0.1:8765/callback"],
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"]
}
```

Enregistrez le `client_id` renvoyé. Les redirections doivent utiliser HTTPS, ou HTTP sur `127.0.0.1` ou `localhost`. Enregistrez l’URI de rappel exacte, y compris son port et son chemin. Les fragments et les identifiants intégrés sont rejetés. Les éléments facultatifs `client_uri` et `logo_uri` doivent utiliser HTTPS ; ThunderPhone ne les récupère pas lors de l’enregistrement. L’enregistrement est limité en débit. Les clients enregistrés n’expirent pas et restent enregistrés lorsqu’une connexion est révoquée. Les rappels HTTPS incluent `https://chatgpt.com/connector/oauth/<id>` et `https://chatgpt.com/connector_platform_oauth_redirect`. Chaque connexion peut enregistrer son propre client.

### Code d’autorisation

Ouvrez le point de terminaison d’autorisation découvert avec `client_id`, le `redirect_uri` exactement enregistré, `response_type=code`, un `state` aléatoire, `scope`, `code_challenge`, `code_challenge_method=S256` et `resource=https://api.thunderphone.com/v1/mcp`. Calculez le challenge comme le condensat SHA-256 base64url non rembourré d’un vérificateur PKCE inédit à forte entropie. Vérifiez les valeurs `state` et `iss` renvoyées avant d’échanger le code. Chaque réponse d’autorisation, y compris les refus et les erreurs de protocole, identifie l’émetteur avec `iss`, correspondant exactement à l’`issuer` découvert. Les erreurs liées à un client ou un rappel non valide sont renvoyées localement sans redirection vers ce rappel.

Effectuez l’échange sur `POST /v1/oauth/token` avec un encodage de formulaire (JSON est également accepté) :

```text
grant_type=authorization_code
client_id=<your client id>
code=<single-use authorization code>
redirect_uri=<exact registered redirect URI>
code_verifier=<original PKCE verifier>
resource=https://api.thunderphone.com/v1/mcp
```

Le paramètre facultatif `resource` est accepté dans les requêtes d’autorisation et de jeton, y compris les échanges d’actualisation et d’appareil. Lorsqu’il est omis, il utilise par défaut la ressource MCP découverte. Lorsqu’il est présent, il doit correspondre exactement à cette ressource ; les autres valeurs renvoient `invalid_target`. Les jetons d’accès portent cette audience, et MCP rejette les jetons dont l’audience est absente ou différente avec `401` et un challenge de découverte.

Les requêtes d’autorisation et les codes expirent après 10 minutes. Chaque réponse de jeton contient `access_token`, `token_type` (`Bearer`), `expires_in` (3600 secondes par défaut), `refresh_token`, `scope`, `organization_id` et `organization_name`. Envoyez les jetons d’accès uniquement via l’en-tête `Authorization: Bearer`. Ne placez jamais de jetons dans des URL, des journaux, le contrôle de source ou des discussions.

### Actualiser et révoquer

Actualisez avec `grant_type=refresh_token`, `client_id`, `refresh_token` et `resource` au point de terminaison de jeton. Enregistrez le nouveau jeton d’actualisation de manière atomique et cessez d’utiliser l’ancien. Les jetons d’actualisation expirent après 30 jours sans actualisation réussie. Un `scope` facultatif peut restreindre les autorisations accordées. `offline_access` est toujours inclus et des jetons d’actualisation sont toujours émis. Une requête initiale sans `scope` accorde uniquement `offline_access` ; les clients doivent donc demander les autorisations dont ils ont besoin.

La réutilisation d’un code et la réutilisation d’un jeton d’actualisation rotatif révoquent l’intégralité de l’autorisation. Sérialisez les actualisations au sein d’un client ; rejouer un identifiant échangé avec succès n’est pas une stratégie de nouvelle tentative sûre.

Pour déconnecter, envoyez `token` et `client_id` à `POST /v1/oauth/revoke`. Révoquer l’un ou l’autre jeton révoque son autorisation, y compris tous les jetons émis à partir de celui-ci. Un jeton inconnu renvoie une réussite sans indiquer s’il existe. Les sessions du tableau de bord peuvent lister leurs propres autorisations avec `GET /v1/oauth/grants` et en révoquer une avec `DELETE /v1/oauth/grants/<id>`, `X-ThunderPhone-Org` sélectionnant l’organisation.

### Autorisation d’appareil

L’autorisation d’appareil est limitée aux clients préenregistrés ; les clients enregistrés dynamiquement reçoivent `unauthorized_client`. Le client public préenregistré `thunderphone-cli` prend en charge l’autorisation d’appareil et l’actualisation. Envoyez `client_id=thunderphone-cli` et `scope` à `POST /v1/oauth/device/code`. Affichez `user_code` et `verification_uri` à l’utilisateur, ou ouvrez `verification_uri_complete`.

Interrogez le point de terminaison de jeton avec `grant_type=urn:ietf:params:oauth:grant-type:device_code`, `client_id` et `device_code`, en attendant au moins l’`interval` renvoyé (5 secondes). Continuez avec `authorization_pending`. En cas de `slow_down`, utilisez le nouvel `interval` renvoyé dans la réponse, augmenté de 5 secondes, pour chaque requête suivante. Arrêtez-vous sur `access_denied`, `expired_token` ou toute autre erreur. N’approuvez jamais automatiquement un code d’appareil.

Le client préenregistré `thunderphone-mcp` accepte `http://127.0.0.1/callback` et `http://localhost/callback` sur n’importe quel port. Le schéma, l’hôte, le chemin et la requête doivent correspondre ; l’échange de jeton doit utiliser l’URI de redirection exacte, y compris le port, issue de l’autorisation. Les clients enregistrés dynamiquement exigent une correspondance exacte de l’URI de redirection, y compris le port. Enregistrez dynamiquement un rappel différent si vous avez besoin d’un autre chemin.

### Vérifications de l’identité du compte et du domaine de l’espace de travail

Demandez `openid email` en plus des scopes opérationnels dont votre client a besoin. Appelez le `userinfo_endpoint` découvert (`GET /v1/oauth/userinfo`) avec le jeton d’accès dans l’en-tête Bearer. Une réponse réussie contient :

```json
{
  "sub": "123",
  "email": "person@example.com",
  "email_verified": true,
  "name": "Example User",
  "org_id": 456
}
```

`sub` est l’identifiant utilisateur stable ; `org_id` est l’organisation sélectionnée lors du consentement. Le point de terminaison exige les deux scopes d’identité et renvoie `403` si l’un d’eux est absent. Il renvoie `403` avec `error=access_denied` si le compte ne possède pas de profil d’adresse e-mail vérifiée, plutôt que d’affirmer qu’une adresse e-mail non vérifiée est fiable. Les jetons non valides, expirés, révoqués ou destinés à une mauvaise audience renvoient `401`. Les jetons de session humaine et les clés API d’organisation ne peuvent pas appeler userinfo. Aucun jeton ID n’est émis.

## Autorisations disponibles

| Famille | Portées |
| --- | --- |
| Agents et importations d’agents | `agents:read`, `agents:write` |
| Appels | `calls:read`, `calls:write` |
| Numéros de téléphone, appelants bloqués et VoIP | `numbers:read`, `numbers:write` |
| Connaissances | `knowledge:read`, `knowledge:write` |
| Campagnes | `campaigns:read`, `campaigns:write` |
| Intégrations, points de terminaison webhook, serveurs MCP | `integrations:read`, `integrations:write` |
| Voix | `voices:read` |
| Scénarios de test, exécutions de test, validation | `testing:read`, `testing:write` |
| Facturation | `billing:read` |
| Identité du compte et e-mail vérifié | `openid`, `email` (tous deux requis pour userinfo) |
| Connexion persistante | `offline_access` (toujours inclus) |

GET, HEAD et OPTIONS utilisent des scopes de lecture ; les autres méthodes utilisent des scopes d’écriture. Les modifications de voix et de facturation ne sont pas disponibles via OAuth ; `POST /v1/voices/preview` utilise `voices:read` car il prévisualise une voix sans modifier sa configuration. Les outils MCP appliquent le scope de leur opération REST sous-jacente. Les transferts entre organisations sont refusés, même lorsque l’utilisateur qui autorise appartient aux deux organisations. Les autres familles d’API, notamment la gestion des clés API et les paramètres des comptes humains, ne sont pas disponibles via OAuth. Une autorisation manquante sur une opération REST renvoie `403` avec `WWW-Authenticate: Bearer error="insufficient_scope", scope="..."`. Les jetons d’accès non valides ou expirés renvoient `401`.


### Signaux d’authentification des outils MCP

Chaque outil dans `tools/list` inclut `securitySchemes: [{"type": "oauth2", "scopes": ["agents:read"]}]`, avec le scope de l’opération REST de cet outil. Les outils de documentation publique utilisent une liste de scopes vide et nécessitent toujours une connexion authentifiée.

Un jeton valide auquel il manque le scope d’un outil reçoit HTTP `200` avec un `result` JSON-RPC contenant `isError: true`, un texte explicatif dans `content` et `_meta["mcp/www_authenticate"]`. Ce dernier est un tableau contenant un défi Bearer avec `resource_metadata`, `error="insufficient_scope"`, `error_description` et le `scope` requis. L’outil n’est pas exécuté. Utilisez ce défi pour demander un consentement étendu. Une authentification absente ou non valide continue de renvoyer HTTP `401` avec `WWW-Authenticate` ; les échecs de scope REST continuent de renvoyer HTTP `403`.

## Conservation des identifiants

L’API exécute `python manage.py oauth_cleanup` toutes les heures dans les environnements de préproduction et de production. Cette commande supprime les demandes d’autorisation et les codes d’appareil expirés. Les jetons d’accès expirés, les codes d’autorisation et les hachages de jetons de rafraîchissement ne sont purgés qu’après la révocation de leur autorisation ou lorsque toute leur famille est inactive. Les hachages utilisés sont conservés tant que la famille dispose d’un jeton de rafraîchissement, d’un code d’autorisation, d’un code d’appareil approuvé ou d’un jeton d’accès utilisable, afin que le nettoyage ne puisse pas désactiver la détection des réutilisations.
