Arquitectura de autorización MCP para empresas: un diseño de referencia multi-tenant
La especificación del Model Context Protocol por fin dice algo concreto sobre autorización: usar OAuth 2.1, vincular cada token a una audiencia específica con resource indicators y no reenviar nunca un token del cliente a una API downstream. Los requisitos son reales. El problema es que están repartidos entre la especificación central, un documento de buenas prácticas de seguridad y media docena de RFC del IETF, y casi toda página que rankea para estos términos es una checklist de un proveedor de seguridad que se detiene en el caso de un solo servidor.
Este es el diseño de referencia que usamos cuando un cliente necesita un servidor MCP que sirva a más de un tenant, se conecte a un proveedor de identidad empresarial y sobreviva a una revisión de seguridad de compras. Es neutral respecto al proveedor, está anclado a la revisión 2025-11-25 de la especificación y dibuja el diagrama que nadie más dibuja: toda la frontera de confianza, del proveedor de identidad al plano de datos por tenant. Escrito a partir del trabajo de Wavect en productos de IA y endurecimiento de seguridad.
¿Diseñas un servidor MCP empresarial?
Habla con WavectQuién es responsable de qué: el servidor MCP es un resource server, no un authorization server
El error más común es construir el servidor MCP como su propio sistema de login. No lo es. Bajo la especificación actual, el servidor MCP es un resource server de OAuth 2.1. Su trabajo es validar tokens, no emitirlos. Un authorization server aparte, que en una empresa es tu proveedor de identidad (Entra ID, Okta, Auth0, Keycloak, Ping), interactúa con el usuario y emite los tokens de acceso.
Esta separación es reciente. La primera especificación de autorización (2025-03-26) esperaba que el servidor MCP actuara como authorization server y resource server a la vez. La revisión 2025-06-18 separó los roles mediante la pull request 284, y esa separación es lo que hace posible el single sign-on empresarial. El authorization server aún puede estar junto al resource server, pero la responsabilidad obligatoria del servidor MCP es validar tokens. Acierta con este modelo de roles y todo lo demás encaja. Fállalo y reconstruyes identidad, mal.
¿Qué exige realmente la especificación actual?
Dos advertencias antes de la tabla. Primera, la autorización en MCP es opcional y solo está definida para transportes HTTP. Un servidor tras un transporte STDIO no debería seguir esta especificación; toma las credenciales del entorno. Segunda, OAuth 2.1 sigue siendo un borrador del IETF, no un RFC publicado, así que no cites un número de RFC para él. Esta es la superficie de estándares de la revisión 2025-11-25.
| Estándar | Referencia | Rol en MCP | Nivel |
|---|---|---|---|
| OAuth 2.1 | draft-ietf-oauth-v2-1 | Framework central | Auth server MUST |
| Protected Resource Metadata | RFC 9728 | El servidor apunta al cliente hacia su auth server | Resource server MUST |
| Resource Indicators | RFC 8707 | Vincula el token a una audiencia | Cliente MUST |
| Authorization Server Metadata | RFC 8414 | Descubrimiento del auth server | MUST (esto u OIDC) |
| OpenID Connect Discovery | OIDC Core 1.0 | Descubrimiento alternativo del auth server | MUST (esto o 8414) |
| PKCE | OAuth 2.1 Sec 7.5.2 | Protege el authorization code | Cliente MUST, S256 |
| Client ID Metadata Documents | draft-ietf-oauth-client-id-metadata-document | URL como client_id | SHOULD (nuevo en 2025-11-25) |
| Dynamic Client Registration | RFC 7591 | Registro automático de clientes | MAY (era SHOULD) |
| Uso de bearer token | RFC 6750 | Challenge e insufficient_scope | Referenciado |
Fíjate en los dos cambios de versión que hacen tropezar a la gente. La revisión 2025-11-25 rebajó Dynamic Client Registration de SHOULD a MAY y añadió un método PKCE S256 obligatorio, además del requisito de que el cliente verifique el soporte de PKCE antes de empezar. Si el metadata del auth server no anuncia code_challenge_methods_supported, un cliente conforme debe rechazar iniciar el flujo.
¿Cómo funciona el flujo de autorización de principio a fin?
Todo el handshake es un paso de descubrimiento seguido de un authorization code flow estándar. El cliente llega al servidor sin token, le dicen dónde autenticarse, se autentica y vuelve con un token acuñado para este servidor concreto.
Dos detalles obligatorios viven dentro de ese diagrama. En el challenge, el servidor debe devolver 401 Unauthorized con una cabecera WWW-Authenticate que lleva la URL resource_metadata, según RFC 9728. La revisión 2025-11-25 también deja al cliente sondear directamente la URI well-known. En las peticiones de authorization y de token, el cliente debe enviar un parámetro resource con la URI canónica del servidor MCP, y debe enviarlo aunque el auth server no diga soportarlo.
¿Por qué se prohíbe el token passthrough y cómo validas la audiencia?
Este es el corazón de seguridad de la especificación, y es donde fallan la mayoría de los servidores caseros. La regla es contundente: el servidor MCP debe validar que cada token de acceso se emitió para él como audiencia prevista, y debe rechazar cualquier otra cosa. No debe aceptar un token acuñado para otro servicio ni reenviar el token que recibió a una API downstream. Ese último antipatrón es el token passthrough, y la especificación lo prohíbe sin rodeos.
La razón es el problema del confused deputy. Si tu servidor acepta y reenvía tokens que no verificó, un token filtrado o emitido para un servicio se convierte en llave maestra de otro, se saltan los rate limits basados en audiencia y el rastro de auditoría downstream muestra la identidad equivocada. Validar la audiencia es una comprobación de un claim, y es la línea entre un resource server y una responsabilidad legal.
on every tool call:
token = bearer_from_authorization_header() # never from a session
claims = verify_signature(token, jwks_of(trusted_issuer))
assert claims.iss == expected_issuer # pinned per tenant
assert this_server_uri in claims.aud # RFC 8707 audience
assert not expired(claims) and not before(claims)
assert required_scope_for(tool) in claims.scope
tenant = claims["tenant"] or claims["org_id"]
enforce_tenant(tenant) # every query, every secret
Fíjate en lo que no aparece: ninguna búsqueda de sesión para autenticar. La especificación es explícita en que los servidores deben verificar cada petición entrante y no deben usar sesiones para autenticar. Los identificadores de sesión, si los usas, deben ser no deterministas y estar vinculados a la identidad del usuario, para que un identificador adivinado no pueda suplantar a otro tenant.
¿Cómo llega el servidor a las APIs downstream sin filtrar el token?
Si el servidor MCP necesita llamar a una API downstream, actúa como cliente OAuth ante esa API y usa un token aparte acuñado por el authorization server upstream. La especificación te dice qué no hacer (no reenviar el token entrante) pero deja el cómo al ecosistema. El patrón dominante es el token exchange, definido en RFC 8693: el servidor cambia el token entrante, cuya audiencia es el servidor MCP, por un token nuevo cuya audiencia es la API downstream.
El servidor juega aquí un doble rol: resource server ante el cliente, cliente OAuth ante la API. Prefiere la delegación a la suplantación, para que el usuario original quede en el claim sub y la cadena de actuación quede registrada en el claim act, que es lo que mantiene honesto tu rastro de auditoría a través del salto. El token exchange y los flujos on-behalf-of quedan fuera de la especificación central de MCP, así que trátalos como responsabilidad del proveedor de identidad o del gateway, no como un requisito de MCP.
¿Cómo es la arquitectura de referencia multi-tenant?
Esta es la topología. Un proveedor de identidad emite tokens vinculados a una audiencia, con un realm por tenant. Los clientes presentan esos tokens a un resource server o gateway compartido dentro de la frontera de confianza empresarial. El servidor valida la audiencia, aplica el scope por herramienta, fija el realm del tenant y solo entonces llega a herramientas, datos y APIs downstream, cada uno aislado por tenant.
El error en la mayoría de los textos multi-tenant es tratar el aislamiento como un asunto de base de datos. No lo es. La multitenencia hay que aplicarla en cada capa, y la identidad del tenant sale de un claim de token verificado, nunca de una entrada de herramienta que el modelo pueda influir. La tabla de abajo es la checklist que recorremos por capa.
| Capa | Control de aislamiento | Fallo si se omite |
|---|---|---|
| Identidad | Fijar issuer y realm por tenant; rechazar tokens de otros realms | El tenant A entra por el realm del tenant B |
| Token | Validar audiencia (RFC 8707) y caducidad en cada petición | Se acepta un token acuñado en otro sitio |
| Scope | Scopes de mínimo privilegio mapeados desde grupos y roles del IdP | Un rol de lectura ejecuta una escritura |
| Herramienta | Filtrar las herramientas visibles por tier de tenant y scope | El agente descubre una herramienta que no puede usar con seguridad |
| Credencial | Secretos por tenant en un vault, opacos al modelo y al cliente | La API key de un tenant sirve a otro |
| Datos | Row-level security basada en el claim del tenant | Lectura de filas entre tenants |
| Auditoría | Logs por tenant, inmutables, con correlation IDs | Sin atribución durante un incidente |

"La identidad del tenant es un claim de token verificado. En el momento en que sale de un argumento de herramienta que el modelo puede escribir, tu aislamiento es puro teatro."
¿Cómo funcionan los scopes por herramienta y la autorización step-up?
El protocolo central gestiona los scopes a nivel de OAuth, no por herramienta individual, así que la autorización por herramienta real la construyes encima. La especificación te da las primitivas. Empieza en mínimo y evita scopes ómnibus como all o full-access. Cuando una herramienta necesita más de lo que concede el token actual, la revisión 2025-11-25 define un step-up limpio: el servidor devuelve 403 con WWW-Authenticate: Bearer error="insufficient_scope" y el scope requerido, el cliente ejecuta una autorización incremental solo para ese scope y reintenta.
En la práctica mapeamos grupos del proveedor de identidad y roles SCIM a scopes, y luego condicionamos cada herramienta a un scope requerido. Un rol de analista lleva invoices:read y ve las herramientas de lectura; nunca lleva ledger:write. Es la misma disciplina que aplicamos en una revisión de autorización, y es lo que un equipo de compras de verdad prueba.
¿Cómo añades SSO empresarial a un servidor MCP?
Hay dos formas, y la elección determina la mayor parte de tu coste operativo.
La primera es OAuth por servidor: cada servidor MCP apunta su metadata authorization_servers al proveedor de identidad corporativo. Limpio para uno o dos servidores, repetitivo a escala de flota. La segunda es un gateway MCP: una única entrada con políticas aplicadas delante de cada servidor, que centraliza descubrimiento, validación de tokens, mapeo de scopes, token exchange y auditoría. Para una empresa con muchos servidores y muchos tenants, el gateway suele ser la respuesta correcta porque te da un solo sitio donde aplicar el aislamiento y un solo sitio donde auditar.
Sobre protocolos: OIDC es de primera clase en la revisión 2025-11-25, que exige que el auth server soporte metadata RFC 8414 u OpenID Connect Discovery, y que los clientes intenten ambos. En MCP no hay SAML nativo. Una empresa solo-SAML hace de puente a través de su proveedor de identidad o de un broker que expone una fachada OAuth u OIDC para el servidor MCP.
¿Cómo gobiernas el registro de clientes y los tiempos de vida de las credenciales?
Dynamic Client Registration era el punto débil de los primeros despliegues porque dejaba que clientes arbitrarios se registraran. La revisión 2025-11-25 lo rebaja a MAY y prefiere un modelo de tres niveles: credenciales preregistradas para clientes conocidos, luego Client ID Metadata Documents donde el cliente usa una URL HTTPS como client_id, y luego DCR como fallback. Si permites cualquiera de los dos últimos, mete los issuers en una allowlist y vigila los riesgos de SSRF y de redirect a localhost que menciona la especificación. Las empresas deberían atar el registro a un tenant y pasar los clientes desconocidos por una cola de revisión de admin.
Los tiempos de vida de las credenciales siguen OAuth 2.1: emitir tokens de acceso de vida corta, rotar los refresh tokens de los clientes públicos, guardar los tokens de forma segura, mantener los tokens fuera de los query strings de la URL y enviar el bearer token en cada petición. Valores por defecto concretos con los que arrancamos:
| Credencial | Vida típica | Rotación |
|---|---|---|
| Token de acceso (cliente a servidor) | 5 a 15 minutos | Reacuñar desde el refresh token |
| Refresh token (cliente público) | Horas a días, deslizante | Rotar en cada uso |
| Token downstream (RFC 8693) | Minutos, por llamada o sesión | Reintercambiar, no cachear en amplio |
| Secreto por tenant en el vault | Larga, pero revocable | Programada más ante sospecha de fuga |
¿Qué eventos de auditoría debes capturar?
La especificación central no tiene un requisito propio de logging de auditoría, pero el análisis del token passthrough construye el argumento por ti: el passthrough rompe el rastro de auditoría porque los logs downstream muestran la identidad equivocada. En un sistema multi-tenant, la auditoría es cómo demuestras que el aislamiento aguantó. Registra estos, por tenant, con un correlation ID que sobreviva al salto downstream.
- Resultados de validación de tokens: aceptado, rechazado por audiencia, rechazado por issuer, caducado.
- Decisiones de autorización: herramienta invocada, scope requerido, scope concedido, allow o deny.
- Eventos de step-up: scope solicitado y el subconjunto concedido.
- Token exchange: qué audiencia downstream se acuñó, para qué subject y qué actor.
- Contexto de tenant: el claim de tenant verificado en cada entrada, para que una consulta sea atribuible a un tenant, un usuario y una herramienta.
Por servidor, gateway o identidad gestionada: ¿cuál eliges?
Una matriz de decisión rápida para las tres topologías viables.
| Enfoque | Mejor cuando | Coste |
|---|---|---|
| OAuth por servidor | Uno o dos servidores, un tenant | Poco setup, flojo a escala |
| Gateway MCP | Muchos servidores o muchos tenants | Más setup, un punto de aplicación |
| Integración con IdP gestionado | Ya vives en Entra u Okta | Lock-in de proveedor, compliance más rápido |
Pasar de un servidor de un solo tenant a uno multi-tenant no es una reescritura si lo secuencias: primero introduce el claim de tenant y fija el issuer, mueve los secretos a un vault por tenant, añade row-level security basada en el claim, luego filtra la visibilidad de herramientas y enciende la auditoría por tenant. Cada paso se puede entregar y probar por su cuenta. Prueba el aislamiento de forma adversaria: reproduce el token del tenant A contra los datos del tenant B y confirma que se rechaza en la capa de token, no solo que se filtra en la capa de consulta.
Reflexiones finales
La especificación de autorización de MCP te da tres requisitos duros: OAuth 2.1 como marco, tokens vinculados a una audiencia mediante resource indicators y nada de token passthrough. Todo lo demás que importa a una empresa, aislamiento multi-tenant, scopes por herramienta, acceso delegado downstream y auditoría, vive por encima de la especificación y es tuyo para diseñarlo. Trata el servidor MCP como un resource server que valida y aplica, mantén la identidad del tenant en un claim verificado y registra lo suficiente para demostrar que el aislamiento aguantó. Haz eso y un servidor MCP deja de ser el eslabón más débil de tu stack de agentes.
Fuentes y lecturas adicionales
- Model Context Protocol, Authorization specification (2025-11-25 revision)
- Model Context Protocol, Authorization specification (2025-06-18 revision)
- Model Context Protocol, Security Best Practices (token passthrough, confused deputy)
- MCP pull request 284, separating the resource server and authorization server roles
- RFC 9728, OAuth 2.0 Protected Resource Metadata
- RFC 8707, Resource Indicators for OAuth 2.0
- RFC 8414, OAuth 2.0 Authorization Server Metadata
- RFC 7591, OAuth 2.0 Dynamic Client Registration
- RFC 8693, OAuth 2.0 Token Exchange
- RFC 9068, JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens
- RFC 6750, OAuth 2.0 Bearer Token Usage
- OAuth 2.1 Authorization Framework (IETF draft)