MCP Fusion/Security and governance/Autenticación

Autenticación

Pregunta a la IA sobre Vinkius

Tres paquetes de autenticación para conectores: verificación de JWT con JWKS, claves de API con comparación segura en tiempo y el flujo de autorización de dispositivo OAuth 2.0 convertido en herramientas que el agente puede manejar.

Un conector que actúa sobre la cuenta de alguien debe saber quién está haciendo la solicitud. MCP Fusion incluye tres paquetes de autenticación, cada uno con un middleware y una herramienta de autenticación opcional, para que el agente pueda ser bloqueado, desafiado y después desbloqueado sin que tu handler note la diferencia.

De dónde viene la identidad

La autenticación lee la identidad de ctx, y ctx se crea por solicitud mediante contextFactory. La configuración canónica:

typescript
registry.attachToServer(server, {
  contextFactory: async (extra) => ({
    token: extra.session?.authToken ?? '',
    headers: extra.headers ?? {},
  }),
});

Todo lo que sigue lee ese contexto.

JWT

bash
npm install @mcpfusion/jwt
typescript
import { requireJwt } from '@mcpfusion/jwt';

f.query('billing.list_invoices')
  .use(requireJwt({
    jwksUri: 'https://auth.example.com/.well-known/jwks.json',
    issuer: 'https://auth.example.com/',
    audience: 'my-connector',
    requiredClaims: ['sub', 'tenant_id'],
    onVerified: (ctx, payload) => {
      ctx.tenantId = payload.tenant_id;   // derived identity for the handler
    },
  }))
  .handle(async (input, ctx) => listInvoices(ctx.tenantId));

JwtVerifier acepta secret (HS256), publicKey (PEM) o jwksUri (conjunto de claves remoto, almacenado en caché por verificador). Con jose instalado funcionan RS256, ES256 y JWKS; sin él, una ruta nativa HS256 verifica con timingSafeEqual y rechaza cualquier otro algoritmo. issuer, audience, clockTolerance (60 segundos) y requiredClaims se validan en cada token. verifyDetailed() devuelve { valid, payload, reason } cuando quieres el motivo en lugar de null.

Orden predeterminado para extraer el token: ctx.token, después ctx.jwt, después ctx.headers.authorization (se elimina el prefijo Bearer ). Los fallos devuelven un toolError autocurable con el código JWT_INVALID y una acción auth para recuperarse.

El paquete JWT verifica tokens, pero no los renueva. El código fuente no admite refresh tokens, así que un conector que necesite acceso duradero al proveedor debe usar el flujo OAuth de abajo o el almacén de credenciales.

La herramienta opcional createJwtAuthTool() expone verify y status como acciones que el agente puede invocar cuando el propio cliente tiene el token.

Claves de API

bash
npm install @mcpfusion/api-key
typescript
import { requireApiKey } from '@mcpfusion/api-key';

f.mutation('billing.refund')
  .use(requireApiKey({
    keys: [process.env.SERVICE_KEY!],
    onValidated: (ctx) => { ctx.service = 'billing-service'; },
  }))
  .handle(...);

Las claves pueden ser texto plano, con hash interno mediante SHA-256 al construirlas, o estar prehasheadas, o pueden validarse con tu función asíncrona validator. El orden de comprobación es: no vacía, minLength (16 por defecto), prefix opcional y después el validador o el conjunto de hashes. El orden de extracción es: ctx.apiKey, ctx.headers['x-api-key'] y luego ctx.headers.authorization (se eliminan los prefijos ApiKey y Bearer ).

La comparación busca ser constante en el tiempo: el gestor compara los hashes de forma que no se detiene en el primer byte diferente. No trates la longitud de la clave como un secreto.

Flujo de dispositivo OAuth para el agente

Cuando el conector necesita una cuenta que el agente aún no tiene, el flujo debe poder conducirse desde un chat: el servidor entrega un código y una URL, la persona aprueba en un navegador y el agente vuelve a preguntar. Es RFC 8628, y el paquete lo convierte en herramientas:

bash
npm install @mcpfusion/oauth
typescript
import { createAuthTool, requireAuth } from '@mcpfusion/oauth';

const auth = createAuthTool({
  clientId: process.env.OAUTH_CLIENT_ID!,
  authorizationEndpoint: 'https://auth.example.com/device/code',
  tokenEndpoint: 'https://auth.example.com/device/token',
  onAuthenticated: (token) => { /* cache or forward the token */ },
});

f.query('analytics.report')
  .use(requireAuth())          // blocks with AUTH_REQUIRED until a token exists
  .handle(...);

La herramienta expone cuatro acciones:

ActionBehavior
loginsolicita un código de dispositivo y devuelve la URL de verificación y el código
completeintercambia el código una vez; authorization_pending significa "todavía no, pregunta otra vez"
statusinforma de si existe un token y, opcionalmente, a qué usuario pertenece
logoutelimina el token

El bucle de sondeo respeta RFC 8628: el primer sondeo es inmediato, authorization_pending continúa, slow_down añade cinco segundos, cualquier otro error termina con la descripción del proveedor y el plazo lanza "Device authorization expired. Start a new flow." El verificador no envía un scope por ti y no hay renovación automática: el almacén de tokens es un archivo bajo el directorio personal (.mcpfusion/token.json, modo 0600, con un fallback de icacls en Windows), así que considéralo el valor predeterminado local para un solo usuario y configura tu propia persistencia para un conector alojado.

requireAuth() comprueba la presencia de un token, no su validez. Combínalo con requireJwt cuando el token upstream deba verificarse criptográficamente.

Notas para producción

  • Prefiere credenciales de plataforma a tokens de usuario para llamadas entre servidores: decláralas con defineCredentials y léelas con requireCredential. Consulta Credentials.
  • En Vinkius Cloud, invoca con un token de conexión y usa Connection Tokens en la consola para revocar el acceso por cliente.
  • El middleware de autenticación se combina con el resto de la cadena: colócalo en el exterior para que las llamadas no autorizadas nunca lleguen a la validación, como se muestra en Middleware and context.

Próximos pasos