@getalby/lightning-tools 7.0.2 → 8.1.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 (44) hide show
  1. package/README.md +108 -28
  2. package/dist/cjs/402/l402.cjs +157 -0
  3. package/dist/cjs/402/l402.cjs.map +1 -0
  4. package/dist/cjs/402/mpp.cjs +179 -0
  5. package/dist/cjs/402/mpp.cjs.map +1 -0
  6. package/dist/cjs/402/x402.cjs +1320 -0
  7. package/dist/cjs/402/x402.cjs.map +1 -0
  8. package/dist/cjs/402.cjs +1694 -0
  9. package/dist/cjs/402.cjs.map +1 -0
  10. package/dist/cjs/bolt11.cjs +534 -518
  11. package/dist/cjs/bolt11.cjs.map +1 -1
  12. package/dist/cjs/index.cjs +811 -453
  13. package/dist/cjs/index.cjs.map +1 -1
  14. package/dist/cjs/lnurl.cjs +22 -7
  15. package/dist/cjs/lnurl.cjs.map +1 -1
  16. package/dist/esm/402/l402.js +150 -0
  17. package/dist/esm/402/l402.js.map +1 -0
  18. package/dist/esm/402/mpp.js +177 -0
  19. package/dist/esm/402/mpp.js.map +1 -0
  20. package/dist/esm/402/x402.js +1318 -0
  21. package/dist/esm/402/x402.js.map +1 -0
  22. package/dist/esm/402.js +1683 -0
  23. package/dist/esm/402.js.map +1 -0
  24. package/dist/esm/bolt11.js +534 -519
  25. package/dist/esm/bolt11.js.map +1 -1
  26. package/dist/esm/index.js +803 -451
  27. package/dist/esm/index.js.map +1 -1
  28. package/dist/esm/lnurl.js +22 -7
  29. package/dist/esm/lnurl.js.map +1 -1
  30. package/dist/lightning-tools.umd.js +3 -3
  31. package/dist/lightning-tools.umd.js.map +1 -1
  32. package/dist/types/402/l402.d.ts +51 -0
  33. package/dist/types/402/mpp.d.ts +26 -0
  34. package/dist/types/402/x402.d.ts +13 -0
  35. package/dist/types/402.d.ts +78 -0
  36. package/dist/types/bolt11.d.ts +6 -1
  37. package/dist/types/index.d.ts +76 -28
  38. package/dist/types/lnurl.d.ts +2 -0
  39. package/package.json +20 -5
  40. package/dist/cjs/l402.cjs +0 -93
  41. package/dist/cjs/l402.cjs.map +0 -1
  42. package/dist/esm/l402.js +0 -87
  43. package/dist/esm/l402.js.map +0 -1
  44. package/dist/types/l402.d.ts +0 -35
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Client: parse "www-authenticate" header from server response
3
+ * @param input
4
+ * @returns details from the header value (token or macaroon, invoice)
5
+ */
6
+ const parseL402 = (input) => {
7
+ // Remove the L402 and LSAT identifiers
8
+ const string = input.replace("L402", "").replace("LSAT", "").trim();
9
+ // Initialize an object to store the key-value pairs
10
+ const keyValuePairs = {};
11
+ // Regular expression to match key and (quoted or unquoted) value
12
+ const regex = /(\w+)=("([^"]*)"|'([^']*)'|([^,]*))/g;
13
+ let match;
14
+ // Use regex to find all key-value pairs
15
+ while ((match = regex.exec(string)) !== null) {
16
+ // Key is always match[1]
17
+ // Value is either match[3] (double-quoted), match[4] (single-quoted), or match[5] (unquoted)
18
+ keyValuePairs[match[1]] = match[3] || match[4] || match[5];
19
+ }
20
+ if (!keyValuePairs["token"] && keyValuePairs["macaroon"]) {
21
+ // fallback to old naming
22
+ keyValuePairs["token"] = keyValuePairs["macaroon"];
23
+ delete keyValuePairs["macaroon"];
24
+ }
25
+ if (!("token" in keyValuePairs) ||
26
+ typeof keyValuePairs["token"] !== "string") {
27
+ throw new Error("No macaroon or token found in www-authenticate header");
28
+ }
29
+ if (!("invoice" in keyValuePairs) ||
30
+ typeof keyValuePairs["invoice"] !== "string") {
31
+ throw new Error("No invoice found in www-authenticate header");
32
+ }
33
+ return keyValuePairs;
34
+ };
35
+
36
+ const handleL402Payment = async (l402Header, url, fetchArgs, headers, wallet) => {
37
+ const details = parseL402(l402Header);
38
+ const token = details.token || details.macaroon;
39
+ const invoice = details.invoice;
40
+ if (!token) {
41
+ throw new Error("L402: missing token/macaroon in WWW-Authenticate header");
42
+ }
43
+ if (!invoice) {
44
+ throw new Error("L402: missing invoice in WWW-Authenticate header");
45
+ }
46
+ const invResp = await wallet.payInvoice({ invoice });
47
+ headers.set("Authorization", `L402 ${token}:${invResp.preimage}`);
48
+ return fetch(url, fetchArgs);
49
+ };
50
+ const fetchWithL402 = async (url, fetchArgs, options) => {
51
+ const wallet = options.wallet;
52
+ if (!wallet) {
53
+ throw new Error("wallet is missing");
54
+ }
55
+ if (!fetchArgs) {
56
+ fetchArgs = {};
57
+ }
58
+ fetchArgs.cache = "no-store";
59
+ fetchArgs.mode = "cors";
60
+ const headers = new Headers(fetchArgs.headers ?? undefined);
61
+ fetchArgs.headers = headers;
62
+ const initResp = await fetch(url, fetchArgs);
63
+ const header = initResp.headers.get("www-authenticate");
64
+ if (!header) {
65
+ return initResp;
66
+ }
67
+ return handleL402Payment(header, url, fetchArgs, headers, wallet);
68
+ };
69
+
70
+ async function issueL402Macaroon(secret, paymentHash, params) {
71
+ if (params !== undefined &&
72
+ Object.prototype.hasOwnProperty.call(params, "paymentHash")) {
73
+ throw new Error("paymentHash is reserved");
74
+ }
75
+ const payload = { ...params, paymentHash };
76
+ const encoded = Buffer.from(JSON.stringify(payload)).toString("base64url");
77
+ const mac = await sign(secret, encoded);
78
+ return `${encoded}.${mac}`;
79
+ }
80
+ async function verifyL402Macaroon(secret, token) {
81
+ const { timingSafeEqual } = await import('crypto');
82
+ const dotIndex = token.lastIndexOf(".");
83
+ if (dotIndex === -1)
84
+ throw new Error("Invalid macaroon token");
85
+ const encoded = token.slice(0, dotIndex);
86
+ const mac = token.slice(dotIndex + 1);
87
+ // Constant-time comparison to prevent timing attacks
88
+ const expectedMac = await sign(secret, encoded);
89
+ try {
90
+ if (!timingSafeEqual(Buffer.from(mac, "hex"), Buffer.from(expectedMac, "hex"))) {
91
+ throw new Error("Invalid macaroon token");
92
+ }
93
+ }
94
+ catch (e) {
95
+ throw new Error("Invalid macaroon token");
96
+ }
97
+ try {
98
+ const parsed = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
99
+ if (parsed === null ||
100
+ typeof parsed !== "object" ||
101
+ Array.isArray(parsed) ||
102
+ typeof parsed.paymentHash !== "string") {
103
+ throw new Error("Invalid macaroon payload");
104
+ }
105
+ return parsed;
106
+ }
107
+ catch {
108
+ throw new Error("Invalid macaroon token");
109
+ }
110
+ }
111
+ async function sign(secret, payload) {
112
+ const { createHmac } = await import('crypto');
113
+ return createHmac("sha256", secret).update(payload).digest("hex");
114
+ }
115
+
116
+ /**
117
+ * Server: create a WWW-Authenticate header for a given macaroon and invoice
118
+ * @param args the macaroon/token and invoice generated for the client's request
119
+ * @returns the header value
120
+ */
121
+ const makeL402AuthenticateHeader = (args) => {
122
+ if (!args.token) {
123
+ throw new Error("token must be provided");
124
+ }
125
+ return `L402 version="0" token="${args.token}", invoice="${args.invoice}"`;
126
+ };
127
+ /**
128
+ * Server: parse "authorization" header sent from client
129
+ * @param input value from authorization header
130
+ * @returns the macaroon and preimage
131
+ */
132
+ function parseL402Authorization(input) {
133
+ // Backwards compat: LSAT was the former name of L402
134
+ const normalized = input.replace(/^LSAT /, "L402 ");
135
+ const prefix = "L402 ";
136
+ if (!normalized.startsWith(prefix))
137
+ return null;
138
+ const credentials = normalized.slice(prefix.length);
139
+ const colonIndex = credentials.indexOf(":");
140
+ if (colonIndex === -1) {
141
+ throw new Error("Invalid authorization header value");
142
+ }
143
+ return {
144
+ token: credentials.slice(0, colonIndex),
145
+ preimage: credentials.slice(colonIndex + 1),
146
+ };
147
+ }
148
+
149
+ export { fetchWithL402, issueL402Macaroon, makeL402AuthenticateHeader, parseL402, parseL402Authorization, verifyL402Macaroon };
150
+ //# sourceMappingURL=l402.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"l402.js","sources":["../../../src/402/l402/utils.ts","../../../src/402/l402/l402.ts","../../../src/402/l402/server/l402.ts","../../../src/402/l402/server/utils.ts"],"sourcesContent":["interface WwwAuthenticatePayload {\n token: string;\n invoice: string;\n [key: string]: string; // Allows any other string properties\n}\n\n/**\n * Client: parse \"www-authenticate\" header from server response\n * @param input\n * @returns details from the header value (token or macaroon, invoice)\n */\nexport const parseL402 = (input: string): WwwAuthenticatePayload => {\n // Remove the L402 and LSAT identifiers\n const string = input.replace(\"L402\", \"\").replace(\"LSAT\", \"\").trim();\n\n // Initialize an object to store the key-value pairs\n const keyValuePairs: Record<string, string> = {};\n\n // Regular expression to match key and (quoted or unquoted) value\n const regex = /(\\w+)=(\"([^\"]*)\"|'([^']*)'|([^,]*))/g;\n let match;\n\n // Use regex to find all key-value pairs\n while ((match = regex.exec(string)) !== null) {\n // Key is always match[1]\n // Value is either match[3] (double-quoted), match[4] (single-quoted), or match[5] (unquoted)\n keyValuePairs[match[1]] = match[3] || match[4] || match[5];\n }\n\n if (!keyValuePairs[\"token\"] && keyValuePairs[\"macaroon\"]) {\n // fallback to old naming\n keyValuePairs[\"token\"] = keyValuePairs[\"macaroon\"];\n delete keyValuePairs[\"macaroon\"];\n }\n\n if (\n !(\"token\" in keyValuePairs) ||\n typeof keyValuePairs[\"token\"] !== \"string\"\n ) {\n throw new Error(\"No macaroon or token found in www-authenticate header\");\n }\n if (\n !(\"invoice\" in keyValuePairs) ||\n typeof keyValuePairs[\"invoice\"] !== \"string\"\n ) {\n throw new Error(\"No invoice found in www-authenticate header\");\n }\n\n return keyValuePairs as WwwAuthenticatePayload;\n};\n","import { Wallet } from \"../utils\";\nimport { parseL402 } from \"./utils\";\n\nexport const handleL402Payment = async (\n l402Header: string,\n url: string,\n fetchArgs: RequestInit,\n headers: Headers,\n wallet: Wallet,\n): Promise<Response> => {\n const details = parseL402(l402Header);\n const token = details.token || details.macaroon;\n const invoice = details.invoice;\n\n if (!token) {\n throw new Error(\"L402: missing token/macaroon in WWW-Authenticate header\");\n }\n if (!invoice) {\n throw new Error(\"L402: missing invoice in WWW-Authenticate header\");\n }\n\n const invResp = await wallet.payInvoice({ invoice });\n headers.set(\"Authorization\", `L402 ${token}:${invResp.preimage}`);\n return fetch(url, fetchArgs);\n};\n\nexport const fetchWithL402 = async (\n url: string,\n fetchArgs: RequestInit,\n options: {\n wallet: Wallet;\n },\n) => {\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 header = initResp.headers.get(\"www-authenticate\");\n if (!header) {\n return initResp;\n }\n\n return handleL402Payment(header, url, fetchArgs, headers, wallet);\n};\n","export type MacaroonPayload<T> = T & {\n paymentHash: string; // hex — SHA256 of the preimage\n};\n\nexport async function issueL402Macaroon<T extends Record<string, unknown>>(\n secret: string,\n paymentHash: string,\n params?: T,\n): Promise<string> {\n if (\n params !== undefined &&\n Object.prototype.hasOwnProperty.call(params, \"paymentHash\")\n ) {\n throw new Error(\"paymentHash is reserved\");\n }\n const payload = { ...params, paymentHash } as MacaroonPayload<T>;\n const encoded = Buffer.from(JSON.stringify(payload)).toString(\"base64url\");\n const mac = await sign(secret, encoded);\n return `${encoded}.${mac}`;\n}\n\nexport async function verifyL402Macaroon<T = unknown>(\n secret: string,\n token: string,\n): Promise<MacaroonPayload<T>> {\n const { timingSafeEqual } = await import(\"crypto\");\n const dotIndex = token.lastIndexOf(\".\");\n if (dotIndex === -1) throw new Error(\"Invalid macaroon token\");\n\n const encoded = token.slice(0, dotIndex);\n const mac = token.slice(dotIndex + 1);\n\n // Constant-time comparison to prevent timing attacks\n const expectedMac = await sign(secret, encoded);\n try {\n if (\n !timingSafeEqual(Buffer.from(mac, \"hex\"), Buffer.from(expectedMac, \"hex\"))\n ) {\n throw new Error(\"Invalid macaroon token\");\n }\n } catch (e) {\n throw new Error(\"Invalid macaroon token\");\n }\n\n try {\n const parsed: unknown = JSON.parse(\n Buffer.from(encoded, \"base64url\").toString(\"utf8\"),\n );\n if (\n parsed === null ||\n typeof parsed !== \"object\" ||\n Array.isArray(parsed) ||\n typeof (parsed as Record<string, unknown>).paymentHash !== \"string\"\n ) {\n throw new Error(\"Invalid macaroon payload\");\n }\n return parsed as MacaroonPayload<T>;\n } catch {\n throw new Error(\"Invalid macaroon token\");\n }\n}\n\nasync function sign(secret: string, payload: string): Promise<string> {\n const { createHmac } = await import(\"crypto\");\n return createHmac(\"sha256\", secret).update(payload).digest(\"hex\");\n}\n","/**\n * Server: create a WWW-Authenticate header for a given macaroon and invoice\n * @param args the macaroon/token and invoice generated for the client's request\n * @returns the header value\n */\nexport const makeL402AuthenticateHeader = (args: {\n token?: string;\n invoice: string;\n}) => {\n if (!args.token) {\n throw new Error(\"token must be provided\");\n }\n\n return `L402 version=\"0\" token=\"${args.token}\", invoice=\"${args.invoice}\"`;\n};\n\n/**\n * Server: parse \"authorization\" header sent from client\n * @param input value from authorization header\n * @returns the macaroon and preimage\n */\nexport function parseL402Authorization(\n input: string,\n): { token: string; preimage: string } | null {\n // Backwards compat: LSAT was the former name of L402\n const normalized = input.replace(/^LSAT /, \"L402 \");\n const prefix = \"L402 \";\n if (!normalized.startsWith(prefix)) return null;\n const credentials = normalized.slice(prefix.length);\n const colonIndex = credentials.indexOf(\":\");\n if (colonIndex === -1) {\n throw new Error(\"Invalid authorization header value\");\n }\n return {\n token: credentials.slice(0, colonIndex),\n preimage: credentials.slice(colonIndex + 1),\n };\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Parse a `WWW-Authenticate: Payment …` header produced by a
3
+ * draft-lightning-charge-00 server. Expected format:
4
+ *
5
+ * Payment id="<id>", realm="<realm>", method="lightning",
6
+ * intent="charge", request="<base64url>" [, expires="<rfc3339>"]
7
+ *
8
+ * Returns null when the header is not a Payment lightning/charge challenge.
9
+ */
10
+ const parseMppChallenge = (header) => {
11
+ if (!header.trimStart().toLowerCase().startsWith("payment")) {
12
+ return null;
13
+ }
14
+ const rest = header
15
+ .slice(header.toLowerCase().indexOf("payment") + "payment".length)
16
+ .trim();
17
+ const result = {};
18
+ const regex = /(\w+)=("([^"]*)"|'([^']*)'|([^,\s]*))/g;
19
+ let match;
20
+ while ((match = regex.exec(rest)) !== null) {
21
+ result[match[1]] = match[3] ?? match[4] ?? match[5] ?? "";
22
+ }
23
+ if (result.method !== "lightning" ||
24
+ result.intent !== "charge" ||
25
+ !result.id ||
26
+ !result.realm ||
27
+ !result.request) {
28
+ return null;
29
+ }
30
+ return {
31
+ id: result.id,
32
+ realm: result.realm,
33
+ method: result.method,
34
+ intent: result.intent,
35
+ request: result.request,
36
+ ...(result.expires ? { expires: result.expires } : {}),
37
+ };
38
+ };
39
+ /** Decode a base64url string (no padding required) to a UTF-8 string. */
40
+ const decodeBase64url = (input) => {
41
+ const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
42
+ const binary = atob(base64);
43
+ const bytes = new Uint8Array(binary.length);
44
+ for (let i = 0; i < binary.length; i++) {
45
+ bytes[i] = binary.charCodeAt(i);
46
+ }
47
+ return new TextDecoder("utf-8").decode(bytes);
48
+ };
49
+ /** Encode a UTF-8 string to base64url without padding. */
50
+ const encodeBase64url = (input) => {
51
+ const bytes = new TextEncoder().encode(input);
52
+ let binary = "";
53
+ for (let i = 0; i < bytes.length; i++) {
54
+ binary += String.fromCharCode(bytes[i]);
55
+ }
56
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
57
+ };
58
+ /**
59
+ * JSON Canonicalization Scheme (RFC 8785).
60
+ * Produces compact JSON with object keys sorted lexicographically.
61
+ */
62
+ const jcs = (value) => {
63
+ if (value === null || typeof value !== "object") {
64
+ return JSON.stringify(value);
65
+ }
66
+ if (Array.isArray(value)) {
67
+ return "[" + value.map(jcs).join(",") + "]";
68
+ }
69
+ const keys = Object.keys(value).sort();
70
+ return ("{" +
71
+ keys
72
+ .map((k) => JSON.stringify(k) + ":" + jcs(value[k]))
73
+ .join(",") +
74
+ "}");
75
+ };
76
+ /**
77
+ * Build the base64url-encoded credential token for the `Authorization` header.
78
+ *
79
+ * Per the spec the credential is a JCS-serialised JSON object that echoes all
80
+ * challenge auth-params (id, realm, method, intent, request, expires) and
81
+ * carries the HTLC preimage that proves payment:
82
+ *
83
+ * {
84
+ * "challenge": { "id": "…", "intent": "charge",
85
+ * "method": "lightning", "realm": "…", "request": "…" },
86
+ * "payload": { "preimage": "<64-char lowercase hex>" }
87
+ * }
88
+ *
89
+ * Keys are sorted lexicographically at every level per JCS.
90
+ */
91
+ const buildMppCredential = (challenge, preimage, source) => {
92
+ const challengeEcho = {
93
+ id: challenge.id,
94
+ intent: challenge.intent,
95
+ method: challenge.method,
96
+ realm: challenge.realm,
97
+ request: challenge.request,
98
+ };
99
+ if (challenge.expires) {
100
+ challengeEcho.expires = challenge.expires;
101
+ }
102
+ const credential = {
103
+ challenge: challengeEcho,
104
+ payload: { preimage },
105
+ };
106
+ return encodeBase64url(jcs(credential));
107
+ };
108
+
109
+ /**
110
+ * Handle a `WWW-Authenticate: Payment …` challenge produced by a
111
+ * draft-lightning-charge-00 server.
112
+ *
113
+ * Flow:
114
+ * 1. Parse the challenge from the header.
115
+ * 2. Decode the `request` auth-param to find the BOLT11 invoice.
116
+ * 3. Pay the invoice via the wallet; receive the HTLC preimage.
117
+ * 4. Build the `Authorization: Payment <credential>` header.
118
+ * 5. Retry the original request with the credential.
119
+ */
120
+ const handleMppChargePayment = async (wwwAuthHeader, url, fetchArgs, headers, wallet) => {
121
+ const challenge = parseMppChallenge(wwwAuthHeader);
122
+ if (!challenge) {
123
+ throw new Error("mpp: invalid or unsupported WWW-Authenticate challenge (expected Payment method=lightning intent=charge)");
124
+ }
125
+ let request;
126
+ try {
127
+ request = JSON.parse(decodeBase64url(challenge.request));
128
+ }
129
+ catch (_) {
130
+ throw new Error("mpp: invalid request auth-param (not valid base64url-encoded JSON)");
131
+ }
132
+ const invoice = request.methodDetails?.invoice;
133
+ if (!invoice) {
134
+ throw new Error("mpp: missing invoice in charge request");
135
+ }
136
+ const invResp = await wallet.payInvoice({ invoice });
137
+ // Per spec: Authorization: Payment <base64url-token> (single token, no wrapper)
138
+ const credential = buildMppCredential(challenge, invResp.preimage);
139
+ headers.set("Authorization", `Payment ${credential}`);
140
+ return fetch(url, fetchArgs);
141
+ };
142
+ /**
143
+ * Fetch a resource protected by the draft-lightning-charge-00 payment
144
+ * authentication protocol.
145
+ *
146
+ * On a `402 Payment Required` response that carries a
147
+ * `WWW-Authenticate: Payment method="lightning" intent="charge" …` header
148
+ * the function pays the embedded BOLT11 invoice and retries with the
149
+ * resulting preimage as the credential.
150
+ *
151
+ * Note: lightning-charge uses consume-once challenge semantics – each
152
+ * challenge embeds a fresh invoice, so paid credentials cannot be reused.
153
+ * The `store` option is accepted for API consistency but is not used.
154
+ */
155
+ const fetchWithMpp = async (url, fetchArgs, options) => {
156
+ const wallet = options.wallet;
157
+ if (!wallet) {
158
+ throw new Error("wallet is missing");
159
+ }
160
+ if (!fetchArgs) {
161
+ fetchArgs = {};
162
+ }
163
+ fetchArgs.cache = "no-store";
164
+ fetchArgs.mode = "cors";
165
+ const headers = new Headers(fetchArgs.headers ?? undefined);
166
+ fetchArgs.headers = headers;
167
+ const initResp = await fetch(url, fetchArgs);
168
+ const wwwAuthHeader = initResp.headers.get("www-authenticate");
169
+ if (!wwwAuthHeader ||
170
+ !wwwAuthHeader.trimStart().toLowerCase().startsWith("payment")) {
171
+ return initResp;
172
+ }
173
+ return handleMppChargePayment(wwwAuthHeader, url, fetchArgs, headers, wallet);
174
+ };
175
+
176
+ export { fetchWithMpp };
177
+ //# sourceMappingURL=mpp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mpp.js","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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"}