{"version":3,"file":"mpp.cjs","sources":["../../../src/402/mpp/utils.ts","../../../src/402/mpp/mpp.ts"],"sourcesContent":["export interface MppChallenge {\n  id: string;\n  realm: string;\n  method: string;\n  intent: string;\n  request: string;\n  expires?: string;\n}\n\nexport interface MppChargeRequest {\n  amount: string;\n  currency: string;\n  description?: string;\n  recipient?: string;\n  externalId?: string;\n  methodDetails: {\n    invoice: string;\n    paymentHash?: string;\n    network?: string;\n  };\n}\n\n/**\n * Parse a `WWW-Authenticate: Payment …` header produced by a\n * draft-lightning-charge-00 server. Expected format:\n *\n *   Payment id=\"<id>\", realm=\"<realm>\", method=\"lightning\",\n *           intent=\"charge\", request=\"<base64url>\" [, expires=\"<rfc3339>\"]\n *\n * Returns null when the header is not a Payment lightning/charge challenge.\n */\nexport const parseMppChallenge = (header: string): MppChallenge | null => {\n  if (!header.trimStart().toLowerCase().startsWith(\"payment\")) {\n    return null;\n  }\n  const rest = header\n    .slice(header.toLowerCase().indexOf(\"payment\") + \"payment\".length)\n    .trim();\n  const result: Record<string, string> = {};\n  const regex = /(\\w+)=(\"([^\"]*)\"|'([^']*)'|([^,\\s]*))/g;\n  let match;\n  while ((match = regex.exec(rest)) !== null) {\n    result[match[1]] = match[3] ?? match[4] ?? match[5] ?? \"\";\n  }\n\n  if (\n    result.method !== \"lightning\" ||\n    result.intent !== \"charge\" ||\n    !result.id ||\n    !result.realm ||\n    !result.request\n  ) {\n    return null;\n  }\n\n  return {\n    id: result.id,\n    realm: result.realm,\n    method: result.method,\n    intent: result.intent,\n    request: result.request,\n    ...(result.expires ? { expires: result.expires } : {}),\n  };\n};\n\n/** Decode a base64url string (no padding required) to a UTF-8 string. */\nexport const decodeBase64url = (input: string): string => {\n  const base64 = input.replace(/-/g, \"+\").replace(/_/g, \"/\");\n  const binary = atob(base64);\n  const bytes = new Uint8Array(binary.length);\n  for (let i = 0; i < binary.length; i++) {\n    bytes[i] = binary.charCodeAt(i);\n  }\n  return new TextDecoder(\"utf-8\").decode(bytes);\n};\n\n/** Encode a UTF-8 string to base64url without padding. */\nconst encodeBase64url = (input: string): string => {\n  const bytes = new TextEncoder().encode(input);\n  let binary = \"\";\n  for (let i = 0; i < bytes.length; i++) {\n    binary += String.fromCharCode(bytes[i]);\n  }\n  return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=/g, \"\");\n};\n\n/**\n * JSON Canonicalization Scheme (RFC 8785).\n * Produces compact JSON with object keys sorted lexicographically.\n */\nconst jcs = (value: unknown): string => {\n  if (value === null || typeof value !== \"object\") {\n    return JSON.stringify(value);\n  }\n  if (Array.isArray(value)) {\n    return \"[\" + (value as unknown[]).map(jcs).join(\",\") + \"]\";\n  }\n  const keys = Object.keys(value as object).sort();\n  return (\n    \"{\" +\n    keys\n      .map(\n        (k) =>\n          JSON.stringify(k) + \":\" + jcs((value as Record<string, unknown>)[k]),\n      )\n      .join(\",\") +\n    \"}\"\n  );\n};\n\n/**\n * Build the base64url-encoded credential token for the `Authorization` header.\n *\n * Per the spec the credential is a JCS-serialised JSON object that echoes all\n * challenge auth-params (id, realm, method, intent, request, expires) and\n * carries the HTLC preimage that proves payment:\n *\n *   {\n *     \"challenge\": { \"id\": \"…\", \"intent\": \"charge\",\n *                    \"method\": \"lightning\", \"realm\": \"…\", \"request\": \"…\" },\n *     \"payload\":   { \"preimage\": \"<64-char lowercase hex>\" }\n *   }\n *\n * Keys are sorted lexicographically at every level per JCS.\n */\nexport const buildMppCredential = (\n  challenge: MppChallenge,\n  preimage: string,\n  source?: string,\n): string => {\n  const challengeEcho: Record<string, string> = {\n    id: challenge.id,\n    intent: challenge.intent,\n    method: challenge.method,\n    realm: challenge.realm,\n    request: challenge.request,\n  };\n  if (challenge.expires) {\n    challengeEcho.expires = challenge.expires;\n  }\n\n  const credential: Record<string, unknown> = {\n    challenge: challengeEcho,\n    payload: { preimage },\n  };\n  if (source) {\n    credential.source = source;\n  }\n\n  return encodeBase64url(jcs(credential));\n};\n\n/**\n * Construct a `WWW-Authenticate` header for testing / server implementations.\n *\n * The auth scheme is `Payment` per [I-D.httpauth-payment].\n */\nexport const makeMppWwwAuthenticateHeader = (args: {\n  id: string;\n  realm: string;\n  request: string;\n  expires?: string;\n}): string => {\n  let header =\n    `Payment id=\"${args.id}\", realm=\"${args.realm}\", method=\"lightning\",` +\n    ` intent=\"charge\", request=\"${args.request}\"`;\n  if (args.expires) {\n    header += `, expires=\"${args.expires}\"`;\n  }\n  return header;\n};\n\n/** Encode an MppChargeRequest as a base64url string suitable for the `request` auth-param. */\nexport const encodeMppChargeRequest = (request: MppChargeRequest): string =>\n  encodeBase64url(jcs(request));\n","import { Wallet } from \"../utils\";\nimport {\n  buildMppCredential,\n  decodeBase64url,\n  MppChargeRequest,\n  parseMppChallenge,\n} from \"./utils\";\n\n/**\n * Handle a `WWW-Authenticate: Payment …` challenge produced by a\n * draft-lightning-charge-00 server.\n *\n * Flow:\n *  1. Parse the challenge from the header.\n *  2. Decode the `request` auth-param to find the BOLT11 invoice.\n *  3. Pay the invoice via the wallet; receive the HTLC preimage.\n *  4. Build the `Authorization: Payment <credential>` header.\n *  5. Retry the original request with the credential.\n */\nexport const handleMppChargePayment = async (\n  wwwAuthHeader: string,\n  url: string,\n  fetchArgs: RequestInit,\n  headers: Headers,\n  wallet: Wallet,\n): Promise<Response> => {\n  const challenge = parseMppChallenge(wwwAuthHeader);\n  if (!challenge) {\n    throw new Error(\n      \"mpp: invalid or unsupported WWW-Authenticate challenge (expected Payment method=lightning intent=charge)\",\n    );\n  }\n\n  let request: MppChargeRequest;\n  try {\n    request = JSON.parse(decodeBase64url(challenge.request));\n  } catch (_) {\n    throw new Error(\n      \"mpp: invalid request auth-param (not valid base64url-encoded JSON)\",\n    );\n  }\n\n  const invoice = request.methodDetails?.invoice;\n  if (!invoice) {\n    throw new Error(\"mpp: missing invoice in charge request\");\n  }\n\n  const invResp = await wallet.payInvoice({ invoice });\n\n  // Per spec: Authorization: Payment <base64url-token>  (single token, no wrapper)\n  const credential = buildMppCredential(challenge, invResp.preimage);\n  headers.set(\"Authorization\", `Payment ${credential}`);\n\n  return fetch(url, fetchArgs);\n};\n\n/**\n * Fetch a resource protected by the draft-lightning-charge-00 payment\n * authentication protocol.\n *\n * On a `402 Payment Required` response that carries a\n * `WWW-Authenticate: Payment method=\"lightning\" intent=\"charge\" …` header\n * the function pays the embedded BOLT11 invoice and retries with the\n * resulting preimage as the credential.\n *\n * Note: lightning-charge uses consume-once challenge semantics – each\n * challenge embeds a fresh invoice, so paid credentials cannot be reused.\n * The `store` option is accepted for API consistency but is not used.\n */\nexport const fetchWithMpp = async (\n  url: string,\n  fetchArgs: RequestInit,\n  options: { wallet: Wallet },\n): Promise<Response> => {\n  const wallet = options.wallet;\n  if (!wallet) {\n    throw new Error(\"wallet is missing\");\n  }\n  if (!fetchArgs) {\n    fetchArgs = {};\n  }\n  fetchArgs.cache = \"no-store\";\n  fetchArgs.mode = \"cors\";\n  const headers = new Headers(fetchArgs.headers ?? undefined);\n  fetchArgs.headers = headers;\n\n  const initResp = await fetch(url, fetchArgs);\n  const wwwAuthHeader = initResp.headers.get(\"www-authenticate\");\n  if (\n    !wwwAuthHeader ||\n    !wwwAuthHeader.trimStart().toLowerCase().startsWith(\"payment\")\n  ) {\n    return initResp;\n  }\n\n  return handleMppChargePayment(wwwAuthHeader, url, fetchArgs, headers, wallet);\n};\n"],"names":[],"mappings":"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"}