OAuth pour les clients MCP
Référence pour créer un client MCP qui connecte les utilisateurs à TwexAPI : découverte, enregistrement dynamique des clients, PKCE, scopes, jetons, renouvellement et révocation.
TwexAPI exploite un serveur d’autorisation OAuth 2.1 pour son serveur MCP. Les clients MCP comme Claude, ChatGPT et Cursor l’utilisent automatiquement. Consultez cette page lorsque vous créez votre propre client ou en déboguez un.
- Flux code d’autorisation avec PKCE (
S256uniquement). - Clients publics uniquement, sans client secret. Enregistrez-les via l’enregistrement dynamique des clients (RFC 7591).
- Les utilisateurs se connectent avec leur compte TwexAPI et approuvent l’accès sur
https://twexapi.io/oauth/consent. - Les jetons sont des chaînes opaques qui ne fonctionnent que sur
https://api.twexapi.io/mcp. Ce ne sont pas des clés API TwexAPI et ils ne permettent pas d’appeler l’API REST. - Les appels sont facturés sur les crédits de l’utilisateur via sa clé API : l’approbation exige donc une clé active avec des crédits.
Si vous voulez seulement appeler TwexAPI depuis un script ou un backend, utilisez une clé API.
Découverte
Une requête sans identifiants vers https://api.twexapi.io/mcp renvoie 401 avec cet en-tête :
WWW-Authenticate: Bearer realm="twexapi", resource_metadata="https://api.twexapi.io/.well-known/oauth-protected-resource/mcp", scope="mcp:read mcp:write"
Métadonnées de la ressource protégée
GET https://api.twexapi.io/.well-known/oauth-protected-resource/mcp (identique à /.well-known/oauth-protected-resource) :
{
"resource": "https://api.twexapi.io/mcp",
"authorization_servers": ["https://api.twexapi.io"],
"bearer_methods_supported": ["header"],
"scopes_supported": ["mcp:read", "mcp:write"],
"resource_name": "TwexAPI"
}
Métadonnées du serveur d’autorisation
GET https://api.twexapi.io/.well-known/oauth-authorization-server :
{
"issuer": "https://api.twexapi.io",
"authorization_endpoint": "https://api.twexapi.io/oauth/authorize",
"token_endpoint": "https://api.twexapi.io/oauth/token",
"registration_endpoint": "https://api.twexapi.io/oauth/register",
"revocation_endpoint": "https://api.twexapi.io/oauth/revoke",
"response_types_supported": ["code"],
"response_modes_supported": ["query"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"code_challenge_methods_supported": ["S256"],
"token_endpoint_auth_methods_supported": ["none"],
"revocation_endpoint_auth_methods_supported": ["none"],
"scopes_supported": ["mcp:read", "mcp:write"],
"authorization_response_iss_parameter_supported": true
}
Scopes
| Scope | Autorise |
|---|---|
mcp:read |
explore et twexapi_request (endpoints en lecture seule). Toujours accordé. |
mcp:write |
twexapi_write (publier des tweets et des réponses). L’utilisateur peut le refuser sur la page de consentement. |
Demandez mcp:read mcp:write pour obtenir les deux. Les scopes inconnus sont ignorés ; si aucun scope reconnu n’est présent, la demande est traitée comme si elle demandait les deux. Le champ scope de la réponse de jeton indique ce que l’utilisateur a réellement accordé.
1. Enregistrer un client
POST https://api.twexapi.io/oauth/register
Content-Type: application/json
{
"client_name": "My Agent",
"redirect_uris": ["http://127.0.0.1:53682/callback"]
}
{
"client_id": "twx_client_...",
"client_id_issued_at": 1792600000,
"client_name": "My Agent",
"redirect_uris": ["http://127.0.0.1:53682/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "none"
}
| Champ | Règles |
|---|---|
redirect_uris |
Obligatoire. De 1 à 10 URI, chacune parmi : https://, http:// sur localhost, 127.0.0.1 ou [::1], ou un schéma privé comme cursor://. Ni fragment ni informations utilisateur. |
client_name |
Facultatif, 100 caractères maximum. Affiché sur la page de consentement et dans la liste Authorized apps (applications autorisées) de l’utilisateur. |
token_endpoint_auth_method |
Facultatif. Seul none est accepté. |
grant_types |
Facultatif. Un sous-ensemble de authorization_code et refresh_token. |
response_types |
Facultatif. Seul code est accepté. |
L’enregistrement est limité à 30 clients par adresse IP et par heure. Un enregistrement de client dure 180 jours et est prolongé chaque fois qu’un utilisateur lance une autorisation avec lui. Enregistrez-vous une fois et réutilisez le client_id.
2. Créer une paire PKCE
Node.js :
import crypto from "node:crypto";
const verifier = crypto.randomBytes(32).toString("base64url");
const challenge = crypto.createHash("sha256").update(verifier).digest("base64url");
Python :
import base64, hashlib, secrets
verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).rstrip(b"=").decode()
challenge = base64.urlsafe_b64encode(hashlib.sha256(verifier.encode()).digest()).rstrip(b"=").decode()
Le verifier doit comporter de 43 à 128 caractères parmi A-Z a-z 0-9 - . _ ~.
3. Envoyer l’utilisateur vers l’autorisation
Ouvrez cette URL dans le navigateur de l’utilisateur :
https://api.twexapi.io/oauth/authorize
?response_type=code
&client_id=twx_client_...
&redirect_uri=http%3A%2F%2F127.0.0.1%3A53682%2Fcallback
&code_challenge=CHALLENGE
&code_challenge_method=S256
&state=RANDOM_STATE
&scope=mcp%3Aread%20mcp%3Awrite
&resource=https%3A%2F%2Fapi.twexapi.io%2Fmcp
| Paramètre | Obligatoire | Remarques |
|---|---|---|
response_type |
Oui | code. |
client_id |
Oui | Issu de l’enregistrement. |
redirect_uri |
Oui | Doit correspondre exactement à une URI enregistrée. Les URI http de loopback acceptent n’importe quel port. |
code_challenge, code_challenge_method |
Oui | S256 uniquement. |
state |
Recommandé | Renvoyé tel quel. Vérifiez-le pour vous protéger contre le CSRF. |
scope |
Non | Voir Scopes. |
resource |
Non | S’il est envoyé, il doit valoir https://api.twexapi.io/mcp. |
TwexAPI envoie l’utilisateur vers la page de consentement sur twexapi.io. L’utilisateur se connecte, voit le nom de l’application et l’hôte vers lequel il sera renvoyé, choisit d’accorder ou non l’accès en écriture, puis approuve ou refuse. La demande expire au bout de 10 minutes.
4. Recevoir le code
Après approbation, le navigateur revient sur votre URI de redirection :
http://127.0.0.1:53682/callback?code=CODE&state=RANDOM_STATE&iss=https%3A%2F%2Fapi.twexapi.io
Vérifiez que state correspond et que iss vaut https://api.twexapi.io. En cas de refus, vous recevez error=access_denied au lieu de code. Le code est à usage unique et expire au bout de 2 minutes.
Si le client_id est inconnu ou si le redirect_uri n’est pas enregistré, TwexAPI affiche une erreur 400 au lieu de rediriger : un client mal configuré n’envoie donc jamais l’utilisateur vers une adresse non vérifiée.
5. Échanger le code contre des jetons
POST https://api.twexapi.io/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=CODE
&redirect_uri=http%3A%2F%2F127.0.0.1%3A53682%2Fcallback
&client_id=twx_client_...
&code_verifier=VERIFIER
{
"access_token": "twx_at_...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "twx_rt_...",
"scope": "mcp:read mcp:write"
}
redirect_uri et client_id doivent correspondre à la demande d’autorisation. Vous pouvez aussi envoyer resource=https://api.twexapi.io/mcp. Les corps JSON sont acceptés.
6. Appeler le serveur MCP
POST https://api.twexapi.io/mcp
Authorization: Bearer twx_at_...
Content-Type: application/json
Accept: application/json, text/event-stream
Les jetons d’accès ne fonctionnent que sur /mcp. Un jeton expiré, révoqué ou inconnu reçoit 401 avec error="invalid_token" dans l’en-tête WWW-Authenticate. Renouvelez le jeton ou demandez à l’utilisateur de se reconnecter.
Renouveler les jetons
POST https://api.twexapi.io/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=twx_rt_...
&client_id=twx_client_...
Chaque renouvellement renvoie un nouveau jeton d’accès et un nouveau refresh token, et l’ancien refresh token cesse de fonctionner. Enregistrez toujours la nouvelle paire. Si un ancien refresh token est réutilisé, TwexAPI le considère comme compromis et révoque l’autorisation entière : tous ses jetons cessent de fonctionner et l’utilisateur doit se reconnecter.
Révocation
POST https://api.twexapi.io/oauth/revoke
Content-Type: application/x-www-form-urlencoded
token=twx_rt_...
Révoquer un refresh token met fin à toute l’autorisation. Révoquer un jeton d’accès invalide uniquement ce jeton. L’endpoint renvoie toujours 200, même pour des jetons inconnus. Les utilisateurs peuvent aussi révoquer une application sous Authorized apps (applications autorisées) dans le tableau de bord, avec effet immédiat.
Durée de vie des jetons
| Élément | Durée de vie |
|---|---|
| Demande d’autorisation (page de consentement) | 10 minutes |
| Code d’autorisation | 2 minutes, usage unique |
| Jeton d’accès | 1 heure |
| Refresh token | 30 jours, renouvelé à chaque utilisation |
| Autorisation inutilisée pendant 30 jours | Expire |
| Enregistrement de client | 180 jours, prolongé à chaque autorisation |
Erreurs
Les erreurs suivent le format OAuth standard :
{
"error": "invalid_grant",
"error_description": "The authorization code is invalid, expired or already used"
}
| Endpoint | error |
Cause |
|---|---|---|
| Enregistrement | invalid_redirect_uri |
URI de redirection absente ou mal formée, ou utilisant un schéma ou un hôte non autorisé. |
| Enregistrement | invalid_client_metadata |
token_endpoint_auth_method, grant_types ou response_types non pris en charge. |
| Enregistrement | temporarily_unavailable (429) |
Trop d’enregistrements depuis votre adresse IP. Réutilisez votre client_id. |
| Autorisation | invalid_client (400, sans redirection) |
client_id inconnu. Enregistrez-vous d’abord. |
| Autorisation | invalid_request (400, sans redirection) |
Le redirect_uri n’est pas enregistré pour ce client. |
| Autorisation | unsupported_response_type |
response_type ne vaut pas code. |
| Autorisation | invalid_request |
Paramètres PKCE absents ou invalides. |
| Autorisation | invalid_target |
resource ne vaut pas https://api.twexapi.io/mcp. |
| Autorisation | access_denied |
L’utilisateur a refusé l’accès. |
| Jeton | invalid_grant |
Code ou refresh token invalide, expiré, déjà utilisé ou émis pour un autre client ; ou échec de la vérification PKCE ; ou autorisation révoquée. |
| Jeton | invalid_target |
resource ne correspond pas. |
| Jeton | unsupported_grant_type |
Utilisez authorization_code ou refresh_token. |
Si le compte de l’utilisateur n’a pas de clé API active ni de crédits, la page de consentement le bloque avant l’approbation : votre client ne reçoit donc jamais un jeton incapable d’effectuer des appels.