OAuth en MCP explicado: el puntero 401, PKCE, registro dinámico y scopes, de principio a fin
La autorización MCP es OAuth 2.1: un 401 apunta a los metadatos del servidor, el cliente se registra.
En esta página
- Por qué MCP usa OAuth
- El flujo, paso a paso
- De qué protege cada pieza
- PKCE: por qué el código no sirve a un ladrón
- Registro dinámico de clientes: por qué nadie crea una app en una consola
- Scopes: estrechan, nunca amplían
- El mismo flujo en un servidor real
- Flujo PKCE frente a token pegado: lo que pierdes
- Preguntas frecuentes
- Siguiente paso
La autorización en MCP es OAuth 2.1 con unas pocas piezas obligatorias. Una petición sin autenticar a un servidor MCP remoto recibe un 401 que apunta a los metadatos del servidor; el cliente lo sigue hasta el servidor de autorización, se identifica, abre el navegador para que la persona consienta unos scopes, e intercambia un código protegido por PKCE por un token de acceso de corta vida y un token de refresco rotativo. El resultado es una credencial con scopes y revocable en lugar de una clave pegada en un archivo de configuración.
Ese es el flujo completo en un párrafo. El resto del artículo lo recorre paso a paso contra la especificación de autorización de MCP (revisión 2025-11-25), explica de qué protegen PKCE, el registro dinámico de clientes y los scopes, y muestra el flujo tal como ocurre contra un servidor real, el nuestro, para que veas cómo es cada etapa desde el lado del cliente.
Por qué MCP usa OAuth
Un servidor MCP local por stdio lee sus credenciales del entorno, y la especificación dice que no debería usar OAuth. Un servidor remoto es distinto: un despliegue sirve a muchas personas, cada una con permisos diferentes, desde apps de IA que el operador del servidor nunca ha visto. De ahí salen tres problemas.
- El servidor necesita saber quién pregunta, en cada llamada.
- La persona necesita limitar qué puede hacer la app, sin entregárselo todo.
- Ambos necesitan una forma de terminar el acceso más tarde, sin cambiar contraseñas ni rotar claves a mano.
Una clave de API pegada resuelve mal el primer problema y los otros dos no los resuelve. OAuth se diseñó exactamente para esta forma, una app que actúa por una persona ante un servicio, y OAuth 2.1 es su versión consolidada y más estricta. La entrada del glosario sobre OAuth 2.1 enumera lo que eliminó de 2.0; para MCP lo relevante es que PKCE es obligatorio, las URIs de redirección deben coincidir exactamente y los tokens no pueden viajar en la query string.
La especificación de autorización de MCP se apoya en cinco documentos: el borrador de OAuth 2.1 (draft-ietf-oauth-v2-1-13), Authorization Server Metadata (RFC 8414), Dynamic Client Registration (RFC 7591), Protected Resource Metadata (RFC 9728) y el borrador de Client ID Metadata Documents. No necesitas leerlos para usar un servidor MCP, pero cada uno responde a una de las preguntas de abajo.
El flujo, paso a paso
Esto es lo que ocurre entre el momento en que añades un servidor remoto y el momento en que el modelo hace su primera llamada a una herramienta. El cliente lo hace todo; tú ves una pestaña del navegador.
- El cliente llama al servidor sin token. El servidor, actuando como servidor de recursos OAuth, responde
401 Unauthorizedcon una cabeceraWWW-Authenticate: Bearerque contieneresource_metadata, una URL. La especificación exige a los servidores implementar RFC 9728 y a los clientes interpretar esta cabecera. Si falta, el cliente debe probar las URIs well-known. - El cliente obtiene los metadatos del recurso protegido. El documento nombra al menos una entrada en
authorization_serversy puede listarscopes_supported. - El cliente descubre el servidor de autorización. Obtiene sus metadatos vía RFC 8414 (
/.well-known/oauth-authorization-server) o OpenID Connect Discovery; los clientes deben soportar ambos. Los metadatos listan los endpoints de autorización, token y registro, los métodos PKCE soportados y, si existe,client_id_metadata_document_supported. - El cliente comprueba el soporte de PKCE. Si
code_challenge_methods_supportedno aparece en los metadatos, el cliente debe negarse a continuar. No hay degradación silenciosa. - El cliente se identifica. Tres opciones, en el orden de prioridad de la especificación: credenciales prerregistradas si las tiene; un Client ID Metadata Document, donde el ID de cliente es una URL HTTPS a un archivo JSON que el cliente aloja; o registro dinámico de clientes, un
POSTal endpoint de registro que devuelve unclient_idal instante. La especificación dice ahora que servidores y clientes deberían soportar los documentos de metadatos y pueden soportar el registro dinámico como alternativa. - El cliente abre el navegador. La URL de autorización lleva el ID de cliente, la URI de redirección, los scopes solicitados, un valor
state, elcode_challengede PKCE concode_challenge_method=S256y el parámetroresourceque nombra al servidor MCP (RFC 8707). La persona inicia sesión y ve una pantalla de consentimiento con el nombre del cliente y los scopes. - El servidor de autorización redirige de vuelta con un código. El código es de corta vida y de un solo uso.
- El cliente intercambia el código por tokens. La petición de token lleva el código, el
code_verifieroriginal de PKCE y el mismo parámetroresource. La respuesta es un token de acceso y, normalmente, un token de refresco. - Cada petición MCP lleva el token en
Authorization: Bearer, nunca en la URL, y el servidor valida en cada llamada que el token es válido y se emitió para este servidor en concreto. - Cuando una llamada necesita más scope, el servidor responde
403conerror="insufficient_scope"y los scopes necesarios. El cliente repite el flujo para el conjunto más amplio; la especificación lo llama autorización escalonada (step-up) y pide a los clientes limitar los reintentos.
De qué protege cada pieza
| Mecanismo | Definido en | El ataque que evita |
|---|---|---|
| Metadatos del recurso protegido | RFC 9728 | Que el cliente adivine o sea engañado para usar un servidor de autorización equivocado |
| Metadatos del servidor de autorización | RFC 8414 | Endpoints fijos en el código que se rompen cuando el servidor cambia |
| PKCE (S256) | RFC 7636, OAuth 2.1 | Que alguien canjee un código de autorización robado |
| Coincidencia exacta de la URI de redirección | OAuth 2.1 | Redirecciones abiertas que envían el código a un atacante |
Parámetro state | OAuth 2.1 | Falsificación de petición entre sitios en el callback |
Parámetro resource | RFC 8707 | Que un token de un servidor se reutilice en otro |
| Validación de audiencia en el servidor | RFC 8707, OAuth 2.1 | Reenvío de tokens y ataques de confused deputy |
| Tokens de acceso de corta vida | OAuth 2.1 | Que un token filtrado siga siendo útil mucho tiempo |
| Tokens de refresco rotativos | OAuth 2.1 | Que un token de refresco robado se reutilice en silencio |
| Scopes y consentimiento | OAuth 2.0/2.1 | Que una app haga más de lo que la persona aceptó |
Dos de ellas merecen una mirada más detenida porque son las que la gente pregunta.
PKCE: por qué el código no sirve a un ladrón
La mayoría de clientes MCP son clientes públicos: una app de escritorio, un editor o una CLI sin ningún secreto que pudieran guardar. En el flujo de código de autorización, el código vuelve mediante una redirección, y en un portátil o un móvil una redirección puede ser interceptada por otro programa registrado para la misma URL. Antes de PKCE, quien tuviera el código podía canjearlo.
PKCE añade un secreto de un solo uso por inicio de sesión. El cliente genera un code_verifier aleatorio, lo guarda y envía code_challenge = BASE64URL(SHA256(code_verifier)) con la petición de autorización. Al intercambiar el código, presenta el verifier original. El servidor de autorización lo hashea, lo compara con el challenge almacenado y rechaza el intercambio si difieren. Un ladrón con el código pero sin el verifier no obtiene nada. La entrada sobre PKCE explica el método plain y por qué OAuth 2.1 y MCP piden usar S256 siempre que sea posible.
Registro dinámico de clientes: por qué nadie crea una app en una consola
El OAuth clásico asumía que un desarrollador registraba cada app de antemano. Eso no escala en un mundo donde cualquiera de miles de clientes de IA puede conectarse a cualquiera de miles de servidores MCP sin relación previa. El registro dinámico de clientes (RFC 7591) permite al cliente enviar por POST sus propios metadatos, un nombre y las URIs de redirección, al endpoint de registro y recibir un client_id de inmediato.
La revisión 2025-11-25 añade una respuesta más limpia, los Client ID Metadata Documents: el ID de cliente es en sí mismo una URL HTTPS que apunta a un documento JSON que describe al cliente. El servidor de autorización lo obtiene, comprueba que el client_id del documento coincide exactamente con la URL y valida las URIs de redirección contra él. La especificación dice que clientes y servidores deberían soportarlo y pueden mantener el registro dinámico por compatibilidad. Como cualquier cliente puede registrarse, un servidor de autorización cuidadoso trata el registro como algo que no aporta confianza: muestra con claridad el nombre del cliente y el host de redirección en la pantalla de consentimiento y avisa cuando la redirección es un simple puerto de localhost, que la especificación señala que los documentos de metadatos por sí solos no pueden proteger del todo.
Scopes: estrechan, nunca amplían
Los scopes son los nombres de la pantalla de consentimiento: work.read, crm.write, etcétera. Una app pide un conjunto, la persona aprueba algunos o todos, y el token lleva el resultado. La especificación MCP indica a los clientes pedir los scopes del parámetro scope del desafío 401 si existe, y si no, los scopes_supported del servidor, y escalar después en lugar de pedirlo todo de golpe.
La regla que más importa es una que el estándar OAuth deja a cada API: un scope describe lo que la app puede pedir en nombre de la persona, y nunca debe exceder lo que esa persona puede hacer por sí misma. Un buen servidor comprueba ambas cosas en cada llamada: ¿el token tiene el scope, y la persona detrás tiene permiso para hacer esto con este registro?
El mismo flujo en un servidor real
Así se ven los pasos anteriores contra el servidor MCP de Atrix en https://atrix.dev/api/mcp, que operamos y usamos a diario, para que puedas emparejar el flujo abstracto con un comportamiento concreto. La guía para desarrolladores tiene las instrucciones cliente por cliente.
En Claude Code, toda la configuración es claude mcp add --transport http atrix https://atrix.dev/api/mcp, y después /mcp y Authenticate. El comando no lleva ninguna credencial a propósito. Lo que ocurre después:
- La primera petición recibe
401con el puntero de RFC 9728. El descubrimiento sigue RFC 8414 y RFC 9728. - El cliente se registra dinámicamente; no hay secreto de cliente en ningún sitio, porque todos los clientes, incluidas nuestras propias apps, son clientes públicos.
- PKCE es solo
S256. El servidor anuncia únicamenteS256en sus metadatos y rechazaplain. - Se abre el navegador; eliges un espacio de trabajo y apruebas scopes. Los códigos viven 60 segundos y funcionan una sola vez.
- El cliente recibe un token de acceso con el prefijo
atx_oat_, válido una hora, y un token de refresco con el prefijoatx_ort_, válido 90 días y rotado en cada uso. - Los scopes nunca añaden permisos. Se resuelven en cada llamada contra la cuenta detrás del token, así que si se revoca el acceso de un compañero, todos los tokens que creó dejan de llegar a nada en la siguiente petición. Nueve scopes requieren además el permiso correspondiente en la cuenta, como el valor de los deals o los registros laborales, y la nómina no se puede conceder en absoluto.
- Los límites son 120 llamadas y 30 escrituras por minuto por token. Cada llamada se audita por herramienta, resultado y sujeto, nunca por contenido.
Para trabajos de CI y servidores sin navegador existe en su lugar un token de acceso personal, con prefijo atx_pat_, enviado como --header "Authorization: Bearer ...". Funciona, y es la peor opción allí donde haya un navegador: vive en texto plano en disco, dura lo que elegiste al crearlo y se salta la pantalla de consentimiento. Mantenemos esa opción para pipelines y recomendamos OAuth para personas.
Flujo PKCE frente a token pegado: lo que pierdes
| OAuth 2.1 con PKCE | Token de acceso personal pegado | |
|---|---|---|
| Cómo te conectas | Añades la URL, apruebas en el navegador | Creas el token en ajustes, lo pegas en la configuración |
| Duración | Acceso 1 hora, refresco 90 días rotativo (Atrix) | De 30 días a para siempre, según elijas |
| Dónde se guarda | Almacén seguro del cliente | Archivo de configuración, a menudo en texto plano |
| Acotado por | Pantalla de consentimiento, escalado después | Elegido al crearlo |
| Se revoca | Revocando la concesión o la cuenta | Borrando el token |
| Necesita navegador | Sí | No |
| Adecuado para | Personas que usan una app de IA | CI, cron, servidores sin interfaz |
Preguntas frecuentes
¿MCP exige OAuth?
La autorización es opcional en el protocolo. Cuando un servidor usa un transporte HTTP y soporta autorización, la especificación dice que debería ajustarse a la especificación de autorización basada en OAuth 2.1. Los servidores stdio no deberían usarla y deberían tomar las credenciales del entorno.
¿Qué diferencia hay entre OAuth 2.0 y OAuth 2.1 para MCP?
OAuth 2.1 elimina los grants implícito y de contraseña, exige PKCE en el flujo de código de autorización, exige coincidencia exacta de la URI de redirección, prohíbe tokens bearer en la query string y exige rotación o vinculación al emisor de los tokens de refresco para clientes públicos. MCP hereda todo esto.
¿Por qué mi cliente MCP se niega a conectarse a un servidor de autorización?
La razón más común es la falta de soporte de PKCE: si code_challenge_methods_supported no aparece en los metadatos del servidor de autorización, un cliente MCP debe negarse a continuar. La segunda es una cabecera WWW-Authenticate ausente o mal formada en el 401.
¿Sigue siendo obligatorio el registro dinámico de clientes?
No. La revisión 2025-11-25 dice que clientes y servidores deberían soportar Client ID Metadata Documents y pueden soportar el registro dinámico como alternativa. Muchos servidores, incluido Atrix, siguen ofreciendo registro dinámico porque los clientes más antiguos dependen de él.
¿Puede un token MCP hacer más que la persona que lo creó?
No debería. Los scopes estrechan el acceso; no lo amplían. Un servidor bien construido resuelve los permisos de la persona en cada llamada, así que un token nunca puede llegar a más que su dueño, y pierde el acceso en el momento en que lo pierde el dueño.
¿Cuándo debería usar un token de acceso personal en vez de OAuth?
Solo donde no haya navegador: pipelines de CI, tareas cron, servidores sin interfaz. En todos los demás casos, OAuth te da una credencial de vida más corta, con scopes y revocable sin guardar un secreto en disco.
Siguiente paso
La guía para desarrolladores recorre el flujo de Atrix cliente por cliente, con los comandos exactos para Claude Code, Claude Desktop, ChatGPT y Cursor. Si algún término de arriba era nuevo, las entradas sobre OAuth 2.1 y PKCE son las explicaciones más cortas que conocemos.