@nevermined-io/payments 1.10.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/dist/api/nvm-api.d.ts +4 -0
  2. package/dist/api/nvm-api.d.ts.map +1 -1
  3. package/dist/api/nvm-api.js +7 -0
  4. package/dist/api/nvm-api.js.map +1 -1
  5. package/dist/api/organizations-api/organizations-api.d.ts +19 -1
  6. package/dist/api/organizations-api/organizations-api.d.ts.map +1 -1
  7. package/dist/api/organizations-api/organizations-api.js +49 -0
  8. package/dist/api/organizations-api/organizations-api.js.map +1 -1
  9. package/dist/api/organizations-api/types.d.ts +27 -0
  10. package/dist/api/organizations-api/types.d.ts.map +1 -1
  11. package/dist/api/organizations-api/types.js.map +1 -1
  12. package/dist/common/api-version.d.ts +1 -1
  13. package/dist/common/api-version.js +1 -1
  14. package/dist/common/api-version.js.map +1 -1
  15. package/dist/common/types.d.ts +1 -1
  16. package/dist/common/types.js.map +1 -1
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +2 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/mcp/core/server-manager.d.ts.map +1 -1
  22. package/dist/mcp/core/server-manager.js +3 -0
  23. package/dist/mcp/core/server-manager.js.map +1 -1
  24. package/dist/mcp/http/mcp-handler.d.ts +7 -0
  25. package/dist/mcp/http/mcp-handler.d.ts.map +1 -1
  26. package/dist/mcp/http/mcp-handler.js +8 -1
  27. package/dist/mcp/http/mcp-handler.js.map +1 -1
  28. package/dist/mcp/http/oauth-metadata.d.ts.map +1 -1
  29. package/dist/mcp/http/oauth-metadata.js +31 -6
  30. package/dist/mcp/http/oauth-metadata.js.map +1 -1
  31. package/dist/mcp/http/oauth-router.d.ts +17 -16
  32. package/dist/mcp/http/oauth-router.d.ts.map +1 -1
  33. package/dist/mcp/http/oauth-router.js +56 -28
  34. package/dist/mcp/http/oauth-router.js.map +1 -1
  35. package/dist/mpp/codec.d.ts +92 -0
  36. package/dist/mpp/codec.d.ts.map +1 -0
  37. package/dist/mpp/codec.js +405 -0
  38. package/dist/mpp/codec.js.map +1 -0
  39. package/dist/mpp/errors.d.ts +165 -0
  40. package/dist/mpp/errors.d.ts.map +1 -0
  41. package/dist/mpp/errors.js +177 -0
  42. package/dist/mpp/errors.js.map +1 -0
  43. package/dist/mpp/fetch.d.ts +196 -0
  44. package/dist/mpp/fetch.d.ts.map +1 -0
  45. package/dist/mpp/fetch.js +493 -0
  46. package/dist/mpp/fetch.js.map +1 -0
  47. package/dist/mpp/index.d.ts +11 -0
  48. package/dist/mpp/index.d.ts.map +1 -0
  49. package/dist/mpp/index.js +20 -0
  50. package/dist/mpp/index.js.map +1 -0
  51. package/dist/mpp/mpp-api.d.ts +153 -0
  52. package/dist/mpp/mpp-api.d.ts.map +1 -0
  53. package/dist/mpp/mpp-api.js +298 -0
  54. package/dist/mpp/mpp-api.js.map +1 -0
  55. package/dist/mpp/types.d.ts +43 -0
  56. package/dist/mpp/types.d.ts.map +1 -0
  57. package/dist/mpp/types.js +9 -0
  58. package/dist/mpp/types.js.map +1 -0
  59. package/dist/payments.d.ts +13 -0
  60. package/dist/payments.d.ts.map +1 -1
  61. package/dist/payments.js +3 -0
  62. package/dist/payments.js.map +1 -1
  63. package/dist/x402/delegation-api.d.ts +3 -1
  64. package/dist/x402/delegation-api.d.ts.map +1 -1
  65. package/dist/x402/delegation-api.js.map +1 -1
  66. package/dist/x402/express/index.d.ts +2 -0
  67. package/dist/x402/express/index.d.ts.map +1 -1
  68. package/dist/x402/express/index.js +2 -0
  69. package/dist/x402/express/index.js.map +1 -1
  70. package/dist/x402/express/middleware.d.ts +33 -1
  71. package/dist/x402/express/middleware.d.ts.map +1 -1
  72. package/dist/x402/express/middleware.js +831 -1
  73. package/dist/x402/express/middleware.js.map +1 -1
  74. package/dist/x402/express/mpp-support.d.ts +58 -0
  75. package/dist/x402/express/mpp-support.d.ts.map +1 -0
  76. package/dist/x402/express/mpp-support.js +67 -0
  77. package/dist/x402/express/mpp-support.js.map +1 -0
  78. package/dist/x402/express/raw-body.d.ts +24 -0
  79. package/dist/x402/express/raw-body.d.ts.map +1 -0
  80. package/dist/x402/express/raw-body.js +34 -0
  81. package/dist/x402/express/raw-body.js.map +1 -0
  82. package/dist/x402/token-request.d.ts +15 -0
  83. package/dist/x402/token-request.d.ts.map +1 -0
  84. package/dist/x402/token-request.js +66 -0
  85. package/dist/x402/token-request.js.map +1 -0
  86. package/dist/x402/token.d.ts.map +1 -1
  87. package/dist/x402/token.js +7 -51
  88. package/dist/x402/token.js.map +1 -1
  89. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"oauth-router.js","sourceRoot":"","sources":["../../../src/mcp/http/oauth-router.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Express Router for OAuth 2.1 endpoints.\n * Provides a pre-configured router with all OAuth discovery and registration endpoints\n * that can be mounted on any Express application.\n */\nimport express, { type Router, type Request, type Response, type NextFunction } from 'express'\nimport type { Payments } from '../../payments.js'\nimport type { HttpRouterConfig } from '../types/http.types.js'\nimport {\n buildProtectedResourceMetadata,\n buildMcpProtectedResourceMetadata,\n buildAuthorizationServerMetadata,\n buildOidcConfiguration,\n buildServerInfoResponse,\n} from './oauth-metadata.js'\nimport {\n isClientRegistrationRequest,\n processClientRegistration,\n ClientRegistrationError,\n} from './client-registration.js'\n\n/**\n * Options for creating the OAuth router.\n */\nexport interface OAuthRouterOptions extends HttpRouterConfig {\n /** Payments instance for authentication */\n payments: Payments\n /** Server version for info endpoint */\n version?: string\n /** Server description for info endpoint */\n description?: string\n /** Optional logging callback (no logs by default) */\n onLog?: (message: string) => void\n}\n\n/**\n * Create an Express router with OAuth 2.1 discovery and registration endpoints.\n * This router can be mounted on any Express application to add OAuth support.\n *\n * @param options - Router configuration options\n * @returns Express Router with OAuth endpoints\n *\n * @example\n * ```typescript\n * import express from 'express'\n * import { Payments } from '@nevermined-io/payments'\n *\n * const app = express()\n * const payments = Payments.getInstance({ nvmApiKey: '...', environment: 'staging_sandbox' })\n *\n * // Create and mount the OAuth router\n * const oauthRouter = createOAuthRouter({\n * payments,\n * baseUrl: 'http://localhost:5001',\n * agentId: 'agent_123',\n * environment: 'staging_sandbox',\n * serverName: 'my-mcp-server',\n * tools: ['hello_world']\n * })\n *\n * app.use(oauthRouter)\n * app.listen(5001)\n * ```\n */\nexport function createOAuthRouter(options: OAuthRouterOptions): Router {\n const router: Router = express.Router()\n\n const {\n baseUrl,\n agentId,\n environment,\n serverName = 'mcp-server',\n tools = [],\n resources = [],\n prompts = [],\n scopes,\n oauthUrls,\n protocolVersion,\n enableOAuthDiscovery = true,\n enableClientRegistration = true,\n enableHealthCheck = true,\n enableServerInfo = true,\n version = '1.0.0',\n description,\n onLog,\n } = options\n\n // Optional logging helper (no-op if onLog not provided)\n const log =\n onLog ||\n (() => {\n // Intentionally empty - no logging when onLog not provided\n })\n\n // Build config object for metadata generators\n const config = {\n baseUrl,\n agentId,\n environment,\n serverName,\n tools,\n resources,\n prompts,\n scopes,\n oauthUrls,\n protocolVersion,\n }\n\n // --- OAuth Discovery Endpoints ---\n\n if (enableOAuthDiscovery) {\n /**\n * Protected Resource Metadata (RFC 9728)\n * GET /.well-known/oauth-protected-resource\n */\n router.get('/.well-known/oauth-protected-resource', (req: Request, res: Response) => {\n log(`GET /.well-known/oauth-protected-resource`)\n const metadata = buildProtectedResourceMetadata(config)\n res.setHeader('Content-Type', 'application/json')\n res.setHeader('Cache-Control', 'public, max-age=3600')\n res.json(metadata)\n })\n\n /**\n * MCP-specific Protected Resource Metadata\n * GET /.well-known/oauth-protected-resource/mcp\n */\n router.get('/.well-known/oauth-protected-resource/mcp', (req: Request, res: Response) => {\n log(`GET /.well-known/oauth-protected-resource/mcp`)\n const metadata = buildMcpProtectedResourceMetadata(config)\n res.setHeader('Content-Type', 'application/json')\n res.setHeader('Cache-Control', 'public, max-age=3600')\n res.json(metadata)\n })\n\n /**\n * OAuth Authorization Server Metadata (RFC 8414)\n * GET /.well-known/oauth-authorization-server\n */\n router.get('/.well-known/oauth-authorization-server', (req: Request, res: Response) => {\n log(`GET /.well-known/oauth-authorization-server`)\n const metadata = buildAuthorizationServerMetadata(config)\n res.setHeader('Content-Type', 'application/json')\n res.setHeader('Cache-Control', 'public, max-age=3600')\n res.json(metadata)\n })\n\n /**\n * OpenID Connect Discovery (for OIDC compatibility)\n * GET /.well-known/openid-configuration\n */\n router.get('/.well-known/openid-configuration', (req: Request, res: Response) => {\n log(`GET /.well-known/openid-configuration`)\n const metadata = buildOidcConfiguration(config)\n res.setHeader('Content-Type', 'application/json')\n res.setHeader('Cache-Control', 'public, max-age=3600')\n res.json(metadata)\n })\n }\n\n // --- Dynamic Client Registration ---\n\n if (enableClientRegistration) {\n /**\n * OAuth Dynamic Client Registration (RFC 7591)\n * POST /register\n */\n router.post('/register', async (req: Request, res: Response) => {\n log(`POST /register`)\n try {\n // Check if this is an OAuth registration request\n if (!isClientRegistrationRequest(req.body)) {\n log(`Invalid registration request`)\n // Not a registration request - return error\n res.status(400).json({\n error: 'invalid_request',\n error_description: 'Request body is not a valid client registration request',\n })\n return\n }\n\n // Process registration\n const response = await processClientRegistration(req.body, config)\n log(`Client registered: ${response.client_id}`)\n\n // Return successful registration (201 Created)\n res.status(201).json(response)\n } catch (error: any) {\n log(`Registration error: ${error?.message || error}`)\n if (error instanceof ClientRegistrationError) {\n res.status(error.statusCode).json(error.toJSON())\n return\n }\n\n // Unexpected error\n res.status(500).json({\n error: 'server_error',\n error_description: error?.message || 'Internal server error during client registration',\n })\n }\n })\n }\n\n // --- Health Check ---\n\n if (enableHealthCheck) {\n /**\n * Health check endpoint\n * GET /health\n */\n router.get('/health', (req: Request, res: Response) => {\n log(`GET /health`)\n res.json({\n status: 'ok',\n service: serverName,\n timestamp: new Date().toISOString(),\n })\n })\n }\n\n // --- Server Info ---\n\n if (enableServerInfo) {\n /**\n * Server info endpoint\n * GET /\n */\n router.get('/', (req: Request, res: Response) => {\n log(`GET /`)\n const info = buildServerInfoResponse(config, { version, description })\n res.json(info)\n })\n }\n\n return router\n}\n\n/**\n * Apply CORS headers to an Express application.\n * This is a utility function for setting up CORS in MCP servers.\n *\n * @param origins - Allowed origins (default: '*')\n * @returns Express middleware for CORS\n */\nexport function createCorsMiddleware(origins: string | string[] = '*') {\n return function corsMiddleware(req: Request, res: Response, next: NextFunction) {\n const origin = Array.isArray(origins) ? origins.join(', ') : origins\n\n res.setHeader('Access-Control-Allow-Origin', origin)\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')\n res.setHeader(\n 'Access-Control-Allow-Headers',\n 'Origin, X-Requested-With, Content-Type, Accept, Authorization, mcp-session-id, mcp-protocol-version',\n )\n res.setHeader('Access-Control-Expose-Headers', 'mcp-session-id')\n\n if (req.method === 'OPTIONS') {\n res.status(200).end()\n return\n }\n\n next()\n }\n}\n\n/**\n * Create a JSON body parser middleware that ensures JSON responses.\n * This wraps express.json() and adds response formatting.\n *\n * @returns Express middleware for JSON parsing\n */\nexport function createJsonMiddleware() {\n return function jsonMiddleware(req: Request, res: Response, next: NextFunction) {\n // Ensure JSON content type on responses\n const originalJson = res.json.bind(res)\n res.json = function (body: any) {\n res.setHeader('Content-Type', 'application/json')\n return originalJson(body)\n }\n\n // Parse JSON body\n express.json()(req, res, (err: any) => {\n if (err) {\n res.status(400).json({\n jsonrpc: '2.0',\n id: null,\n error: {\n code: -32700,\n message: 'Parse error: Invalid JSON',\n },\n })\n return\n }\n next()\n })\n }\n}\n\n/**\n * Create a middleware that requires a Bearer token in the Authorization header.\n * Returns HTTP 401 if the token is missing or malformed.\n *\n * This is a lightweight check that only verifies presence of a token.\n * Full validation (is the token valid? does the user have access?) is handled\n * by withPaywall() when a tool is actually called.\n *\n * @returns Express middleware for token requirement\n *\n * @example\n * ```typescript\n * const requireAuth = createRequireAuthMiddleware()\n *\n * // Protect MCP endpoints\n * app.post('/mcp', requireAuth, mcpHandler)\n * app.get('/mcp', requireAuth, sseHandler)\n * ```\n */\nexport function createRequireAuthMiddleware() {\n return function requireAuthMiddleware(req: Request, res: Response, next: NextFunction) {\n const authHeader = req.headers.authorization\n\n if (!authHeader) {\n res.status(401).json({\n error: 'unauthorized',\n error_description: 'Authorization header required',\n })\n return\n }\n\n if (!authHeader.startsWith('Bearer ')) {\n res.status(401).json({\n error: 'unauthorized',\n error_description: 'Bearer token required',\n })\n return\n }\n\n const token = authHeader.slice(7).trim()\n if (!token) {\n res.status(401).json({\n error: 'unauthorized',\n error_description: 'Bearer token cannot be empty',\n })\n return\n }\n\n // Token present and well-formed, continue\n // Full validation happens in withPaywall() when tools are called\n next()\n }\n}\n\n/**\n * Create a middleware that logs all HTTP requests to endpoints.\n * Logs method, URL, IP address, and relevant headers.\n *\n * @param onLog - Optional logging callback. If not provided, does nothing.\n * @returns Express middleware for HTTP request logging\n *\n * @example\n * ```typescript\n * const logMiddleware = createHttpLoggingMiddleware((msg) => console.log(msg))\n * app.use(logMiddleware)\n * ```\n */\nexport function createHttpLoggingMiddleware(onLog?: (message: string) => void) {\n const log =\n onLog ||\n ((_message: string) => {\n // Intentionally empty - no logging when onLog not provided\n })\n\n return function httpLoggingMiddleware(req: Request, res: Response, next: NextFunction) {\n const method = req.method\n const url = req.originalUrl || req.url\n const ip = req.ip || req.socket.remoteAddress || 'unknown'\n const timestamp = new Date().toISOString()\n\n // Extract relevant headers\n const authHeader = req.headers.authorization\n const sessionId = req.headers['mcp-session-id']\n const protocolVersion = req.headers['mcp-protocol-version']\n const userAgent = req.headers['user-agent']\n\n // Build log message\n const logParts: string[] = [`[${timestamp}]`, `${method}`, url, `IP: ${ip}`]\n\n if (authHeader) {\n const tokenPreview = authHeader.startsWith('Bearer ')\n ? `Bearer ${authHeader.slice(7, 20)}...`\n : 'Bearer [present]'\n logParts.push(`Auth: ${tokenPreview}`)\n }\n\n if (sessionId) {\n const sessionValue = Array.isArray(sessionId) ? sessionId[0] : sessionId\n logParts.push(`Session: ${sessionValue}`)\n }\n\n if (protocolVersion) {\n const versionValue = Array.isArray(protocolVersion) ? protocolVersion[0] : protocolVersion\n logParts.push(`MCP-Version: ${versionValue}`)\n }\n\n if (userAgent) {\n const uaValue = Array.isArray(userAgent) ? userAgent[0] : userAgent\n logParts.push(`User-Agent: ${uaValue}`)\n }\n\n log(`HTTP Request: ${logParts.join(' | ')}`)\n\n next()\n }\n}\n"]}
1
+ {"version":3,"file":"oauth-router.js","sourceRoot":"","sources":["../../../src/mcp/http/oauth-router.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Express Router for OAuth 2.1 endpoints.\n * Provides a pre-configured router with all OAuth discovery and registration endpoints\n * that can be mounted on any Express application.\n */\nimport express, { type Router, type Request, type Response, type NextFunction } from 'express'\nimport type { Payments } from '../../payments.js'\nimport type { HttpRouterConfig } from '../types/http.types.js'\nimport {\n buildProtectedResourceMetadata,\n buildMcpProtectedResourceMetadata,\n buildAuthorizationServerMetadata,\n buildOidcConfiguration,\n buildServerInfoResponse,\n} from './oauth-metadata.js'\nimport {\n isClientRegistrationRequest,\n processClientRegistration,\n ClientRegistrationError,\n} from './client-registration.js'\n\n/**\n * Options for creating the OAuth router.\n */\nexport interface OAuthRouterOptions extends HttpRouterConfig {\n /** Payments instance for authentication */\n payments: Payments\n /** Server version for info endpoint */\n version?: string\n /** Server description for info endpoint */\n description?: string\n /** Optional logging callback (no logs by default) */\n onLog?: (message: string) => void\n}\n\n/**\n * Create an Express router with OAuth 2.1 discovery and registration endpoints.\n * This router can be mounted on any Express application to add OAuth support.\n *\n * @param options - Router configuration options\n * @returns Express Router with OAuth endpoints\n *\n * @example\n * ```typescript\n * import express from 'express'\n * import { Payments } from '@nevermined-io/payments'\n *\n * const app = express()\n * const payments = Payments.getInstance({ nvmApiKey: '...', environment: 'staging_sandbox' })\n *\n * // Create and mount the OAuth router\n * const oauthRouter = createOAuthRouter({\n * payments,\n * baseUrl: 'http://localhost:5001',\n * agentId: 'agent_123',\n * environment: 'staging_sandbox',\n * serverName: 'my-mcp-server',\n * tools: ['hello_world']\n * })\n *\n * app.use(oauthRouter)\n * app.listen(5001)\n * ```\n */\nexport function createOAuthRouter(options: OAuthRouterOptions): Router {\n const router: Router = express.Router()\n\n const {\n baseUrl,\n agentId,\n environment,\n serverName = 'mcp-server',\n tools = [],\n resources = [],\n prompts = [],\n scopes,\n oauthUrls,\n protocolVersion,\n enableOAuthDiscovery = true,\n enableClientRegistration = true,\n enableHealthCheck = true,\n enableServerInfo = true,\n version = '1.0.0',\n description,\n onLog,\n } = options\n\n // Optional logging helper (no-op if onLog not provided)\n const log =\n onLog ||\n (() => {\n // Intentionally empty - no logging when onLog not provided\n })\n\n // Build config object for metadata generators\n const config = {\n baseUrl,\n agentId,\n environment,\n serverName,\n tools,\n resources,\n prompts,\n scopes,\n oauthUrls,\n protocolVersion,\n }\n\n // --- OAuth Discovery Endpoints ---\n\n if (enableOAuthDiscovery) {\n /**\n * Protected Resource Metadata (RFC 9728)\n * GET /.well-known/oauth-protected-resource\n */\n router.get('/.well-known/oauth-protected-resource', (req: Request, res: Response) => {\n log(`GET /.well-known/oauth-protected-resource`)\n const metadata = buildProtectedResourceMetadata(config)\n res.setHeader('Content-Type', 'application/json')\n res.setHeader('Cache-Control', 'public, max-age=3600')\n res.json(metadata)\n })\n\n /**\n * MCP-specific Protected Resource Metadata\n * GET /.well-known/oauth-protected-resource/mcp\n */\n router.get('/.well-known/oauth-protected-resource/mcp', (req: Request, res: Response) => {\n log(`GET /.well-known/oauth-protected-resource/mcp`)\n const metadata = buildMcpProtectedResourceMetadata(config)\n res.setHeader('Content-Type', 'application/json')\n res.setHeader('Cache-Control', 'public, max-age=3600')\n res.json(metadata)\n })\n\n /**\n * OAuth Authorization Server Metadata (RFC 8414)\n * GET /.well-known/oauth-authorization-server\n */\n router.get('/.well-known/oauth-authorization-server', (req: Request, res: Response) => {\n log(`GET /.well-known/oauth-authorization-server`)\n const metadata = buildAuthorizationServerMetadata(config)\n res.setHeader('Content-Type', 'application/json')\n res.setHeader('Cache-Control', 'public, max-age=3600')\n res.json(metadata)\n })\n\n /**\n * OpenID Connect Discovery (for OIDC compatibility)\n * GET /.well-known/openid-configuration\n */\n router.get('/.well-known/openid-configuration', (req: Request, res: Response) => {\n log(`GET /.well-known/openid-configuration`)\n const metadata = buildOidcConfiguration(config)\n res.setHeader('Content-Type', 'application/json')\n res.setHeader('Cache-Control', 'public, max-age=3600')\n res.json(metadata)\n })\n }\n\n // --- Dynamic Client Registration ---\n\n if (enableClientRegistration) {\n /**\n * OAuth Dynamic Client Registration (RFC 7591)\n * POST /register\n */\n router.post('/register', async (req: Request, res: Response) => {\n log(`POST /register`)\n try {\n // Check if this is an OAuth registration request\n if (!isClientRegistrationRequest(req.body)) {\n log(`Invalid registration request`)\n // Not a registration request - return error\n res.status(400).json({\n error: 'invalid_request',\n error_description: 'Request body is not a valid client registration request',\n })\n return\n }\n\n // Process registration\n const response = await processClientRegistration(req.body, config)\n log(`Client registered: ${response.client_id}`)\n\n // Return successful registration (201 Created)\n res.status(201).json(response)\n } catch (error: any) {\n log(`Registration error: ${error?.message || error}`)\n if (error instanceof ClientRegistrationError) {\n res.status(error.statusCode).json(error.toJSON())\n return\n }\n\n // Unexpected error\n res.status(500).json({\n error: 'server_error',\n error_description: error?.message || 'Internal server error during client registration',\n })\n }\n })\n }\n\n // --- Health Check ---\n\n if (enableHealthCheck) {\n /**\n * Health check endpoint\n * GET /health\n */\n router.get('/health', (req: Request, res: Response) => {\n log(`GET /health`)\n res.json({\n status: 'ok',\n service: serverName,\n timestamp: new Date().toISOString(),\n })\n })\n }\n\n // --- Server Info ---\n\n if (enableServerInfo) {\n /**\n * Server info endpoint\n * GET /\n */\n router.get('/', (req: Request, res: Response) => {\n log(`GET /`)\n const info = buildServerInfoResponse(config, { version, description })\n res.json(info)\n })\n }\n\n return router\n}\n\n/**\n * Apply CORS headers to an Express application.\n * This is a utility function for setting up CORS in MCP servers.\n *\n * @param origins - Allowed origins (default: '*')\n * @returns Express middleware for CORS\n */\nexport function createCorsMiddleware(origins: string | string[] = '*') {\n return function corsMiddleware(req: Request, res: Response, next: NextFunction) {\n const origin = Array.isArray(origins) ? origins.join(', ') : origins\n\n res.setHeader('Access-Control-Allow-Origin', origin)\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')\n res.setHeader(\n 'Access-Control-Allow-Headers',\n 'Origin, X-Requested-With, Content-Type, Accept, Authorization, mcp-session-id, mcp-protocol-version',\n )\n res.setHeader('Access-Control-Expose-Headers', 'mcp-session-id')\n\n if (req.method === 'OPTIONS') {\n res.status(200).end()\n return\n }\n\n next()\n }\n}\n\n/**\n * Create a JSON body parser middleware that ensures JSON responses.\n * This wraps express.json() and adds response formatting.\n *\n * @returns Express middleware for JSON parsing\n */\nexport function createJsonMiddleware() {\n return function jsonMiddleware(req: Request, res: Response, next: NextFunction) {\n // Ensure JSON content type on responses\n const originalJson = res.json.bind(res)\n res.json = function (body: any) {\n res.setHeader('Content-Type', 'application/json')\n return originalJson(body)\n }\n\n // Parse JSON body\n express.json()(req, res, (err: any) => {\n if (err) {\n res.status(400).json({\n jsonrpc: '2.0',\n id: null,\n error: {\n code: -32700,\n message: 'Parse error: Invalid JSON',\n },\n })\n return\n }\n next()\n })\n }\n}\n\n/**\n * A well-formed HTTP Host value: `hostname[:port]` or `[ipv6][:port]`. Crucially\n * it contains none of the characters (`\"`, `\\`, whitespace, `;`, `,`) that could\n * break out of the `WWW-Authenticate` quoted-string and inject extra auth-params.\n */\nconst SAFE_HOST_RE = /^(\\[[0-9a-fA-F:]+\\]|[A-Za-z0-9._-]+)(:\\d{1,5})?$/\n\n/**\n * Create a middleware that requires a Bearer token, emitting an RFC 9728 §5.1\n * `WWW-Authenticate` challenge on 401 so a client can discover the PRM.\n *\n * `options.baseUrl` (optional) — operator-known public base URL of THIS MCP\n * server. When set, the `resource_metadata` pointer is built from it (never from\n * client input) — the correct choice behind a TLS-terminating proxy. When omitted,\n * the pointer is derived from the request, but ONLY if the (forwarded) Host is a\n * safe, well-formed value; otherwise the challenge header is omitted rather than\n * emitting a broken/injectable one.\n *\n * `options.resourceMetadataPath` (optional) — the PRM path to advertise; defaults\n * to the root `/.well-known/oauth-protected-resource`. A caller mounting this on a\n * sub-resource (e.g. `/mcp`) should pass the scoped document\n * (`/.well-known/oauth-protected-resource/mcp`) so the client gets the correctly\n * scoped `resource` (RFC 8707 audience) and the resource's capabilities.\n */\nexport function createRequireAuthMiddleware(\n options: { baseUrl?: string; resourceMetadataPath?: string } = {},\n) {\n const pinnedBase = options.baseUrl?.replace(/\\/+$/, '')\n const prmPath = options.resourceMetadataPath ?? '/.well-known/oauth-protected-resource'\n\n // Origin to advertise in the PRM pointer, or undefined if it can't be trusted.\n const prmOrigin = (req: Request): string | undefined => {\n if (pinnedBase) return pinnedBase\n const fwdHost = req.headers['x-forwarded-host']\n const rawHost = (Array.isArray(fwdHost) ? fwdHost[0] : fwdHost) ?? req.get('host')\n const host = typeof rawHost === 'string' ? rawHost.trim() : undefined\n if (!host || !SAFE_HOST_RE.test(host)) return undefined // forged/broken → omit\n const fwdProto = req.headers['x-forwarded-proto']\n const proto =\n (Array.isArray(fwdProto) ? fwdProto[0] : fwdProto)?.split(',')[0]?.trim() ||\n req.protocol ||\n 'https'\n if (proto !== 'http' && proto !== 'https') return undefined\n return `${proto}://${host}`\n }\n\n return function requireAuthMiddleware(req: Request, res: Response, next: NextFunction) {\n // RFC 9728 §5.1: a 401 from a protected resource advertises where its PRM\n // lives so a client can discover the authorization server. `error` (RFC 6750\n // §3.1) is added only when a credential WAS presented but is malformed — a\n // request with no credential at all omits it (§3).\n const send401 = (error_description: string, error?: string): void => {\n const origin = prmOrigin(req)\n if (origin) {\n const params = [`resource_metadata=\"${origin}${prmPath}\"`]\n if (error) params.push(`error=\"${error}\"`)\n res.setHeader('WWW-Authenticate', `Bearer ${params.join(', ')}`)\n }\n res.status(401).json({ error: 'unauthorized', error_description })\n }\n\n const authHeader = req.headers.authorization\n if (!authHeader) {\n send401('Authorization header required')\n return\n }\n if (!authHeader.startsWith('Bearer ')) {\n send401('Bearer token required', 'invalid_request')\n return\n }\n const token = authHeader.slice(7).trim()\n if (!token) {\n send401('Bearer token cannot be empty', 'invalid_request')\n return\n }\n\n // Token present and well-formed, continue\n // Full validation happens in withPaywall() when tools are called\n next()\n }\n}\n\n/**\n * Create a middleware that logs all HTTP requests to endpoints.\n * Logs method, URL, IP address, and relevant headers.\n *\n * @param onLog - Optional logging callback. If not provided, does nothing.\n * @returns Express middleware for HTTP request logging\n *\n * @example\n * ```typescript\n * const logMiddleware = createHttpLoggingMiddleware((msg) => console.log(msg))\n * app.use(logMiddleware)\n * ```\n */\nexport function createHttpLoggingMiddleware(onLog?: (message: string) => void) {\n const log =\n onLog ||\n ((_message: string) => {\n // Intentionally empty - no logging when onLog not provided\n })\n\n return function httpLoggingMiddleware(req: Request, res: Response, next: NextFunction) {\n const method = req.method\n const url = req.originalUrl || req.url\n const ip = req.ip || req.socket.remoteAddress || 'unknown'\n const timestamp = new Date().toISOString()\n\n // Extract relevant headers\n const authHeader = req.headers.authorization\n const sessionId = req.headers['mcp-session-id']\n const protocolVersion = req.headers['mcp-protocol-version']\n const userAgent = req.headers['user-agent']\n\n // Build log message\n const logParts: string[] = [`[${timestamp}]`, `${method}`, url, `IP: ${ip}`]\n\n if (authHeader) {\n const tokenPreview = authHeader.startsWith('Bearer ')\n ? `Bearer ${authHeader.slice(7, 20)}...`\n : 'Bearer [present]'\n logParts.push(`Auth: ${tokenPreview}`)\n }\n\n if (sessionId) {\n const sessionValue = Array.isArray(sessionId) ? sessionId[0] : sessionId\n logParts.push(`Session: ${sessionValue}`)\n }\n\n if (protocolVersion) {\n const versionValue = Array.isArray(protocolVersion) ? protocolVersion[0] : protocolVersion\n logParts.push(`MCP-Version: ${versionValue}`)\n }\n\n if (userAgent) {\n const uaValue = Array.isArray(userAgent) ? userAgent[0] : userAgent\n logParts.push(`User-Agent: ${uaValue}`)\n }\n\n log(`HTTP Request: ${logParts.join(' | ')}`)\n\n next()\n }\n}\n"]}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * The MPP wire format, hand-written so the SDK takes no protocol dependency.
3
+ *
4
+ * The one rule that matters: the backend re-derives the challenge id as
5
+ * `HMAC(secret, realm|method|intent|canonicalize(request)|expires|digest|opaque)`
6
+ * from the fields carried inside the credential. `request` and `opaque` are
7
+ * therefore passed through as the exact base64url strings received — this file
8
+ * never re-encodes them.
9
+ */
10
+ import type { MppChallenge, MppReceipt } from './types.js';
11
+ /**
12
+ * Extracts the `Payment` scheme from a header value that may carry several
13
+ * schemes comma-separated (RFC 9110 §11.6.1).
14
+ *
15
+ * The `Payment` scheme takes one of two shapes on our wire: a bare token68
16
+ * credential (`Payment <base64url>`, which cannot itself contain a comma or
17
+ * a quote) or a structured challenge (`Payment id="...", realm="...", ...`,
18
+ * comma-separated `key="value"` auth-params, where a value MAY contain a
19
+ * comma). Bounding the match at the next literal "Payment " occurrence — or
20
+ * at end-of-string otherwise — corrupts the first shape whenever a
21
+ * *different* trailing scheme follows: e.g. `Payment <token>, Bearer <jwt>`
22
+ * used to extract the whole remainder including the trailing scheme.
23
+ * Bounding a structured challenge with a naive, quote-unaware comma split
24
+ * corrupts the second shape whenever a value contains a comma: it truncates
25
+ * mid-quote and silently drops every param after it. Instead: a bare
26
+ * token68 is bounded by its first top-level comma; a structured challenge is
27
+ * bounded by {@link findStructuredChallengeEnd}'s quote-aware scan.
28
+ */
29
+ export declare function extractPaymentScheme(headerValue: string): string | null;
30
+ /**
31
+ * The stable identity of a credential: the challenge id it carries.
32
+ *
33
+ * The header bytes are NOT that identity. `PAYMENT_SCHEME_BOUNDARY` is
34
+ * case-insensitive and {@link extractPaymentScheme} returns the matched slice
35
+ * verbatim, so `Payment x`, `payment x` and `Payment x` are three different
36
+ * strings for one credential — and the body is base64url of JSON the BUYER
37
+ * assembles, so re-ordering its keys yields more. The backend collapses every
38
+ * one of them onto a single burn, because its own idempotency key is the
39
+ * decoded challenge id. Anything at the seller edge enforcing single-use has
40
+ * to key on the same thing, or a buyer flips one byte of case and buys the
41
+ * response again.
42
+ *
43
+ * @returns the `challenge.id` a token68 credential decodes to, or `null` when
44
+ * the credential cannot be decoded into one — a structured `key="value"`
45
+ * scheme (that shape is a challenge, never a credential), undecodable
46
+ * base64url JSON, or JSON without a non-empty string `challenge.id`. The
47
+ * backend rejects all of those anyway; returning `null` lets the caller refuse
48
+ * them without a round-trip rather than fall back to a key it cannot trust.
49
+ */
50
+ export declare function extractCredentialChallengeId(credential: string): string | null;
51
+ /**
52
+ * Parses a `WWW-Authenticate` header into a challenge.
53
+ *
54
+ * @returns `null` when there is no usable `Payment` challenge to parse: the
55
+ * header carries no `Payment` scheme at all, OR the scheme is present but
56
+ * missing one of the structural auth-params (`id`/`realm`/`method`/`intent`/
57
+ * `request`) needed to even attempt decoding it. Both cases mean the same
58
+ * thing to a caller deciding whether to retry: there is nothing here to pay.
59
+ *
60
+ * A `Payment` scheme that has all of those structural params present, but
61
+ * whose `request` value fails to decode as JSON or decodes to something
62
+ * that is not a usable `{ planId, credits }` object, raises a typed
63
+ * {@link MppError} instead — that is a seller who tried to speak MPP and
64
+ * sent something broken, a distinct failure from "there is no challenge
65
+ * here to parse".
66
+ */
67
+ export declare function parseChallengeHeader(headerValue: string): MppChallenge | null;
68
+ /**
69
+ * Builds the `Authorization: Payment …` value for a challenge.
70
+ *
71
+ * Key order in the JSON is irrelevant — the server parses it — but the two
72
+ * base64url strings are emitted untouched, which is what keeps the HMAC valid.
73
+ */
74
+ export declare function buildCredentialHeader(challenge: MppChallenge, payload: {
75
+ accessToken: string;
76
+ }): string;
77
+ /**
78
+ * Decodes a `Payment-Receipt` header value.
79
+ *
80
+ * Raises a typed {@link MppError} on malformed input — undecodable base64url
81
+ * JSON, or JSON that decodes to something that is not a usable
82
+ * `{ method, reference, status, timestamp }` object — rather than a raw
83
+ * `SyntaxError` or an untyped receipt. This function does not decide whether
84
+ * that failure is fatal for its caller (the receipt is "unsigned by design,
85
+ * and carries no balance", so a caller may reasonably treat a decode
86
+ * failure as non-fatal and simply omit the receipt); it only guarantees the
87
+ * failure is typed and raised at the boundary, not as a later `TypeError`
88
+ * on a field access with nothing pointing back at the header that caused it
89
+ * — the same asymmetry {@link parseChallengeHeader} closes for `request=`.
90
+ */
91
+ export declare function parseReceiptHeader(headerValue: string): MppReceipt;
92
+ //# sourceMappingURL=codec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../../src/mpp/codec.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAuB,UAAU,EAAE,MAAM,YAAY,CAAA;AAgF/E;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAwBvE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAiB9E;AAiJD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CA4B7E;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,YAAY,EACvB,OAAO,EAAE;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,GAC/B,MAAM,CAgBR;AAmBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,UAAU,CASlE"}
@@ -0,0 +1,405 @@
1
+ /**
2
+ * The MPP wire format, hand-written so the SDK takes no protocol dependency.
3
+ *
4
+ * The one rule that matters: the backend re-derives the challenge id as
5
+ * `HMAC(secret, realm|method|intent|canonicalize(request)|expires|digest|opaque)`
6
+ * from the fields carried inside the credential. `request` and `opaque` are
7
+ * therefore passed through as the exact base64url strings received — this file
8
+ * never re-encodes them.
9
+ */
10
+ import { MppError } from './errors.js';
11
+ /** A bare token (RFC 9110 §5.6.2, used for auth-scheme names and auth-param keys). */
12
+ const TOKEN = "[!#$%&'*+\\-.^_`|~0-9A-Za-z]+";
13
+ /** Matches the start of a `key=` auth-param, i.e. a Payment challenge continuing. */
14
+ const AUTH_PARAM_START = new RegExp(`^${TOKEN}\\s*=`);
15
+ /**
16
+ * Finds where a structured challenge's auth-param list ends within `rest`
17
+ * (the content right after "Payment "), scanning quote- and escape-aware so
18
+ * neither a comma nor a `\"` inside a `"…"` value is mistaken for a
19
+ * boundary. mppx serializes quoted values with
20
+ * `value.replace(/\\/g,'\\\\').replace(/"/g,'\\"')` (`Challenge.ts:316-319`),
21
+ * so a seller-supplied `description` like `5" screen replacement plan` wire-
22
+ * encodes as `description="5\" screen replacement plan"`. A scanner that
23
+ * toggles quote state on every literal `"` — escaped or not — desyncs on
24
+ * that `\"`, stays stuck "inside" a quote for the rest of the header, and
25
+ * swallows a genuine trailing scheme. This mirrors mppx's own reference
26
+ * parser's `escaped` flag (`Challenge.ts:362-379`) rather than inventing new
27
+ * semantics.
28
+ *
29
+ * A top-level (non-quoted, non-escaped) comma only ends the scheme if what
30
+ * follows it does NOT itself look like a continuing `key=value` auth-param —
31
+ * which is also how a following literal "Payment " (the merged-challenge
32
+ * case) is recognized as a new scheme: "Payment" followed by whitespace
33
+ * never matches `AUTH_PARAM_START`, since that requires "=" immediately
34
+ * (modulo optional whitespace) after the leading token.
35
+ *
36
+ * An unterminated quote is not an error here: the loop simply runs out of
37
+ * input without finding a boundary and falls back to treating the whole
38
+ * remainder as this scheme — a safe, total (never-throwing) O(n) fallback.
39
+ */
40
+ function findStructuredChallengeEnd(rest) {
41
+ let inQuotes = false;
42
+ let escaped = false;
43
+ for (let i = 0; i < rest.length; i++) {
44
+ const ch = rest[i];
45
+ if (inQuotes) {
46
+ if (escaped) {
47
+ escaped = false;
48
+ }
49
+ else if (ch === '\\') {
50
+ escaped = true;
51
+ }
52
+ else if (ch === '"') {
53
+ inQuotes = false;
54
+ }
55
+ continue;
56
+ }
57
+ if (ch === '"') {
58
+ inQuotes = true;
59
+ continue;
60
+ }
61
+ if (ch === ',' && !AUTH_PARAM_START.test(rest.slice(i + 1).trimStart())) {
62
+ return i;
63
+ }
64
+ }
65
+ return rest.length;
66
+ }
67
+ /**
68
+ * Matches the `Payment` scheme name at a genuine scheme boundary: start of
69
+ * the header value, or right after a top-level comma (RFC 9110 §11.6.1
70
+ * separates comma-separated challenges/credentials that way). Unanchored,
71
+ * `/Payment\s+/i` matches mid-token — `XPayment abc`, `NotPayment abc`,
72
+ * `Bearer prepayment xyz` — and also matches "Payment" text embedded inside
73
+ * a *different*, preceding scheme's quoted value (e.g.
74
+ * `Digest username="my payment plan", …`), since plain whitespace alone is
75
+ * not a scheme boundary. Anchoring on comma-or-start (not bare whitespace)
76
+ * closes both: a scheme name is never preceded by an arbitrary space, only
77
+ * by the start of the header or the comma that separates it from what came
78
+ * before.
79
+ *
80
+ * This decides which protocol handles a request:
81
+ * `extractCredential` → `extractPaymentScheme` feeds the MPP-vs-x402 routing
82
+ * predicate in `middleware.ts`, so an unanchored match would divert an
83
+ * x402 buyer carrying a perfectly valid `payment-signature` token, plus an
84
+ * unrelated `Authorization` header that happens to contain "payment" text,
85
+ * onto the MPP path and challenge them instead of serving the request.
86
+ */
87
+ const PAYMENT_SCHEME_BOUNDARY = /(?:^|,)\s*(Payment\s+)/i;
88
+ /**
89
+ * Extracts the `Payment` scheme from a header value that may carry several
90
+ * schemes comma-separated (RFC 9110 §11.6.1).
91
+ *
92
+ * The `Payment` scheme takes one of two shapes on our wire: a bare token68
93
+ * credential (`Payment <base64url>`, which cannot itself contain a comma or
94
+ * a quote) or a structured challenge (`Payment id="...", realm="...", ...`,
95
+ * comma-separated `key="value"` auth-params, where a value MAY contain a
96
+ * comma). Bounding the match at the next literal "Payment " occurrence — or
97
+ * at end-of-string otherwise — corrupts the first shape whenever a
98
+ * *different* trailing scheme follows: e.g. `Payment <token>, Bearer <jwt>`
99
+ * used to extract the whole remainder including the trailing scheme.
100
+ * Bounding a structured challenge with a naive, quote-unaware comma split
101
+ * corrupts the second shape whenever a value contains a comma: it truncates
102
+ * mid-quote and silently drops every param after it. Instead: a bare
103
+ * token68 is bounded by its first top-level comma; a structured challenge is
104
+ * bounded by {@link findStructuredChallengeEnd}'s quote-aware scan.
105
+ */
106
+ export function extractPaymentScheme(headerValue) {
107
+ const schemeMatch = PAYMENT_SCHEME_BOUNDARY.exec(headerValue);
108
+ if (!schemeMatch || schemeMatch.index === undefined)
109
+ return null;
110
+ // Group 1 ("Payment\s+") is the tail of the whole match, so the full
111
+ // match's end position is also where the captured keyword ends; its start
112
+ // is offset back by the keyword's own length, which skips the leading
113
+ // comma/whitespace the boundary alternation consumed.
114
+ const keyword = schemeMatch[1];
115
+ const contentStart = schemeMatch.index + schemeMatch[0].length;
116
+ const start = contentStart - keyword.length;
117
+ const rest = headerValue.slice(contentStart);
118
+ if (!AUTH_PARAM_START.test(rest)) {
119
+ // Bare token68 credential: bounded by its first top-level comma, since a
120
+ // token68 cannot itself contain one (or a quote, so no quote-tracking
121
+ // is needed here).
122
+ const commaIndex = rest.indexOf(',');
123
+ const end = commaIndex === -1 ? headerValue.length : contentStart + commaIndex;
124
+ return headerValue.slice(start, end).trim();
125
+ }
126
+ const end = contentStart + findStructuredChallengeEnd(rest);
127
+ return headerValue.slice(start, end).replace(/,\s*$/, '').trim();
128
+ }
129
+ /**
130
+ * The stable identity of a credential: the challenge id it carries.
131
+ *
132
+ * The header bytes are NOT that identity. `PAYMENT_SCHEME_BOUNDARY` is
133
+ * case-insensitive and {@link extractPaymentScheme} returns the matched slice
134
+ * verbatim, so `Payment x`, `payment x` and `Payment x` are three different
135
+ * strings for one credential — and the body is base64url of JSON the BUYER
136
+ * assembles, so re-ordering its keys yields more. The backend collapses every
137
+ * one of them onto a single burn, because its own idempotency key is the
138
+ * decoded challenge id. Anything at the seller edge enforcing single-use has
139
+ * to key on the same thing, or a buyer flips one byte of case and buys the
140
+ * response again.
141
+ *
142
+ * @returns the `challenge.id` a token68 credential decodes to, or `null` when
143
+ * the credential cannot be decoded into one — a structured `key="value"`
144
+ * scheme (that shape is a challenge, never a credential), undecodable
145
+ * base64url JSON, or JSON without a non-empty string `challenge.id`. The
146
+ * backend rejects all of those anyway; returning `null` lets the caller refuse
147
+ * them without a round-trip rather than fall back to a key it cannot trust.
148
+ */
149
+ export function extractCredentialChallengeId(credential) {
150
+ const token68 = credential.replace(/^Payment\s+/i, '').trim();
151
+ if (!token68 || AUTH_PARAM_START.test(token68))
152
+ return null;
153
+ let decoded;
154
+ try {
155
+ decoded = JSON.parse(Buffer.from(token68, 'base64url').toString('utf8'));
156
+ }
157
+ catch {
158
+ return null;
159
+ }
160
+ if (typeof decoded !== 'object' || decoded === null || Array.isArray(decoded))
161
+ return null;
162
+ const { challenge } = decoded;
163
+ if (typeof challenge !== 'object' || challenge === null || Array.isArray(challenge))
164
+ return null;
165
+ const { id } = challenge;
166
+ return typeof id === 'string' && id !== '' ? id : null;
167
+ }
168
+ /**
169
+ * Reads a quoted-string value starting right after its opening `"`,
170
+ * unescaping `\"` to a literal `"` and `\\` to a literal `\` (mirrors
171
+ * mppx's `readQuotedAuthParamValue`, `Challenge.ts:461-465`).
172
+ *
173
+ * An unterminated quote is not an error here (unlike mppx's reference
174
+ * parser, which throws): the loop runs out of input and returns whatever was
175
+ * accumulated, matching {@link findStructuredChallengeEnd}'s equally
176
+ * permissive fallback for the same input shape.
177
+ */
178
+ function readQuotedValue(input, start) {
179
+ let i = start;
180
+ let value = '';
181
+ let escaped = false;
182
+ while (i < input.length) {
183
+ const ch = input[i];
184
+ i++;
185
+ if (escaped) {
186
+ value += ch;
187
+ escaped = false;
188
+ continue;
189
+ }
190
+ if (ch === '\\') {
191
+ escaped = true;
192
+ continue;
193
+ }
194
+ if (ch === '"')
195
+ return [value, i];
196
+ value += ch;
197
+ }
198
+ return [value, i];
199
+ }
200
+ /**
201
+ * Splits `key="value", key2="value2"` into a map. Quoted values are read
202
+ * escape-aware and unescaped ({@link readQuotedValue}), so a value may
203
+ * itself contain a comma, a quote (`\"`) or a backslash (`\\`) without
204
+ * corrupting the param that follows it. Unquoted values are tolerated too.
205
+ */
206
+ function parseAuthParams(input) {
207
+ const params = {};
208
+ let i = 0;
209
+ while (i < input.length) {
210
+ while (i < input.length && /[\s,]/.test(input[i]))
211
+ i++;
212
+ if (i >= input.length)
213
+ break;
214
+ const keyStart = i;
215
+ while (i < input.length && /[a-zA-Z0-9_-]/.test(input[i]))
216
+ i++;
217
+ const key = input.slice(keyStart, i);
218
+ if (!key)
219
+ break;
220
+ while (i < input.length && /\s/.test(input[i]))
221
+ i++;
222
+ if (input[i] !== '=')
223
+ break;
224
+ i++;
225
+ while (i < input.length && /\s/.test(input[i]))
226
+ i++;
227
+ let value;
228
+ if (input[i] === '"') {
229
+ const [decoded, nextIndex] = readQuotedValue(input, i + 1);
230
+ value = decoded;
231
+ i = nextIndex;
232
+ }
233
+ else {
234
+ const valueStart = i;
235
+ while (i < input.length && input[i] !== ',')
236
+ i++;
237
+ value = input.slice(valueStart, i).trim();
238
+ }
239
+ params[key] = value;
240
+ }
241
+ return params;
242
+ }
243
+ /**
244
+ * Decodes a base64url string as JSON, raising a typed {@link MppError}
245
+ * rather than a raw `SyntaxError` on malformed input.
246
+ *
247
+ * `Buffer.from(x, 'base64url')` never throws — it silently drops invalid
248
+ * characters — so garbage would otherwise reach `JSON.parse` and escape as a
249
+ * bare `SyntaxError` that names neither MPP nor payment, invisible to a
250
+ * caller writing `catch (e) { if (e instanceof MppError) … }` exactly as our
251
+ * own docs tell them to.
252
+ */
253
+ function decodeBase64UrlJson(encoded, context) {
254
+ try {
255
+ return JSON.parse(Buffer.from(encoded, 'base64url').toString('utf8'));
256
+ }
257
+ catch (error) {
258
+ throw new MppError(`Could not decode the ${context}: ${error instanceof Error ? error.message : String(error)}`);
259
+ }
260
+ }
261
+ /**
262
+ * Validates a decoded `request` param's shape before it is trusted.
263
+ *
264
+ * The raw decode only guarantees valid JSON, not the right shape: a remote
265
+ * challenge's `request=` can decode to `null`, an array, or `{}`, all of
266
+ * which would otherwise sail through a bare type cast and reach
267
+ * `payments.mpp.fetch` with an unusable or `undefined` `planId`.
268
+ *
269
+ * `planId` is the field with the documented failure mode (an unusable or
270
+ * `undefined` value reaching the mint) and is checked strictly: a non-empty
271
+ * string, full stop. `agentId` is unchecked structurally but IS load-bearing
272
+ * once present — the buyer helper forwards it straight into the token mint
273
+ * (`options.agentId ?? challenge.request.agentId`) — so a wrong-typed value
274
+ * is rejected here rather than reaching that spend path. `credits`, by
275
+ * contrast, is not what anything spends: the amount the backend re-derives
276
+ * comes from `requestEncoded`, forwarded byte-verbatim, so rejecting a
277
+ * perfectly reasonable JSON-number encoding of "credits" would make a valid
278
+ * third-party seller wholly unpayable over a field this SDK does not itself
279
+ * act on — it is coerced by {@link toChallengeRequest} instead.
280
+ */
281
+ function isValidChallengeRequestShape(value) {
282
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
283
+ return false;
284
+ const { planId, credits, agentId } = value;
285
+ if (typeof planId !== 'string' || planId === '')
286
+ return false;
287
+ if (typeof credits !== 'string' && typeof credits !== 'number')
288
+ return false;
289
+ if (agentId !== undefined && typeof agentId !== 'string')
290
+ return false;
291
+ return true;
292
+ }
293
+ /** Normalizes a validated decoded request into {@link MppChallengeRequest}'s
294
+ * declared shape, coercing a numeric `credits` to the decimal string the
295
+ * type promises. */
296
+ function toChallengeRequest(shape) {
297
+ return {
298
+ planId: shape.planId,
299
+ credits: String(shape.credits),
300
+ ...(shape.agentId !== undefined && { agentId: shape.agentId }),
301
+ };
302
+ }
303
+ /**
304
+ * Parses a `WWW-Authenticate` header into a challenge.
305
+ *
306
+ * @returns `null` when there is no usable `Payment` challenge to parse: the
307
+ * header carries no `Payment` scheme at all, OR the scheme is present but
308
+ * missing one of the structural auth-params (`id`/`realm`/`method`/`intent`/
309
+ * `request`) needed to even attempt decoding it. Both cases mean the same
310
+ * thing to a caller deciding whether to retry: there is nothing here to pay.
311
+ *
312
+ * A `Payment` scheme that has all of those structural params present, but
313
+ * whose `request` value fails to decode as JSON or decodes to something
314
+ * that is not a usable `{ planId, credits }` object, raises a typed
315
+ * {@link MppError} instead — that is a seller who tried to speak MPP and
316
+ * sent something broken, a distinct failure from "there is no challenge
317
+ * here to parse".
318
+ */
319
+ export function parseChallengeHeader(headerValue) {
320
+ const scheme = extractPaymentScheme(headerValue);
321
+ if (!scheme)
322
+ return null;
323
+ const params = parseAuthParams(scheme.replace(/^Payment\s+/i, ''));
324
+ const { id, realm, method, intent, request, expires, digest, opaque, description } = params;
325
+ if (!id || !realm || !method || !intent || !request)
326
+ return null;
327
+ const decodedRequest = decodeBase64UrlJson(request, 'MPP challenge request parameter');
328
+ if (!isValidChallengeRequestShape(decodedRequest)) {
329
+ throw new MppError('The MPP challenge names a request parameter that is not a valid ' +
330
+ '{ planId: string, credits: string | number, agentId?: string } object.');
331
+ }
332
+ return {
333
+ id,
334
+ realm,
335
+ method,
336
+ intent,
337
+ request: toChallengeRequest(decodedRequest),
338
+ requestEncoded: request,
339
+ ...(expires && { expires }),
340
+ ...(digest && { digest }),
341
+ ...(opaque && { opaque }),
342
+ ...(description && { description }),
343
+ };
344
+ }
345
+ /**
346
+ * Builds the `Authorization: Payment …` value for a challenge.
347
+ *
348
+ * Key order in the JSON is irrelevant — the server parses it — but the two
349
+ * base64url strings are emitted untouched, which is what keeps the HMAC valid.
350
+ */
351
+ export function buildCredentialHeader(challenge, payload) {
352
+ const wire = {
353
+ challenge: {
354
+ id: challenge.id,
355
+ realm: challenge.realm,
356
+ method: challenge.method,
357
+ intent: challenge.intent,
358
+ ...(challenge.expires && { expires: challenge.expires }),
359
+ ...(challenge.digest && { digest: challenge.digest }),
360
+ ...(challenge.description && { description: challenge.description }),
361
+ ...(challenge.opaque && { opaque: challenge.opaque }),
362
+ request: challenge.requestEncoded,
363
+ },
364
+ payload,
365
+ };
366
+ return `Payment ${Buffer.from(JSON.stringify(wire), 'utf8').toString('base64url')}`;
367
+ }
368
+ /** Validates a decoded `Payment-Receipt` body against {@link MppReceipt}'s
369
+ * shape — mirrors {@link isValidChallengeRequestShape}'s strictness for the
370
+ * same reason: without it, `null`, an array, or `{}` all sail through a
371
+ * bare type cast and surface later as an untyped `TypeError` on a field
372
+ * access, with nothing pointing back at the header that caused it. */
373
+ function isValidReceipt(value) {
374
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
375
+ return false;
376
+ const { method, reference, status, timestamp } = value;
377
+ return (typeof method === 'string' &&
378
+ typeof reference === 'string' &&
379
+ reference !== '' &&
380
+ typeof status === 'string' &&
381
+ typeof timestamp === 'string');
382
+ }
383
+ /**
384
+ * Decodes a `Payment-Receipt` header value.
385
+ *
386
+ * Raises a typed {@link MppError} on malformed input — undecodable base64url
387
+ * JSON, or JSON that decodes to something that is not a usable
388
+ * `{ method, reference, status, timestamp }` object — rather than a raw
389
+ * `SyntaxError` or an untyped receipt. This function does not decide whether
390
+ * that failure is fatal for its caller (the receipt is "unsigned by design,
391
+ * and carries no balance", so a caller may reasonably treat a decode
392
+ * failure as non-fatal and simply omit the receipt); it only guarantees the
393
+ * failure is typed and raised at the boundary, not as a later `TypeError`
394
+ * on a field access with nothing pointing back at the header that caused it
395
+ * — the same asymmetry {@link parseChallengeHeader} closes for `request=`.
396
+ */
397
+ export function parseReceiptHeader(headerValue) {
398
+ const decoded = decodeBase64UrlJson(headerValue.trim(), 'MPP receipt');
399
+ if (!isValidReceipt(decoded)) {
400
+ throw new MppError('The Payment-Receipt header is not a valid ' +
401
+ '{ method: string, reference: string, status: string, timestamp: string } object.');
402
+ }
403
+ return decoded;
404
+ }
405
+ //# sourceMappingURL=codec.js.map