@moneydevkit/core 0.11.0-beta.8 → 0.11.0-beta.9
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.
package/dist/mdk402/token.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Parameters for creating an
|
|
2
|
+
* Parameters for creating an L402 credential.
|
|
3
|
+
*
|
|
4
|
+
* The credential is an HMAC-signed opaque token (not a macaroon) that
|
|
5
|
+
* is compatible with the L402 HTTP wire format defined in bLIP-26.
|
|
3
6
|
*/
|
|
4
|
-
export interface
|
|
7
|
+
export interface CreateL402CredentialParams {
|
|
5
8
|
paymentHash: string;
|
|
6
9
|
amountSats: number;
|
|
7
10
|
expiresAt: number;
|
|
@@ -11,9 +14,9 @@ export interface CreateMDK402TokenParams {
|
|
|
11
14
|
currency: string;
|
|
12
15
|
}
|
|
13
16
|
/**
|
|
14
|
-
* Result of verifying an
|
|
17
|
+
* Result of verifying an L402 credential.
|
|
15
18
|
*/
|
|
16
|
-
export type
|
|
19
|
+
export type VerifyL402CredentialResult = {
|
|
17
20
|
valid: true;
|
|
18
21
|
paymentHash: string;
|
|
19
22
|
amountSats: number;
|
|
@@ -30,35 +33,37 @@ export type VerifyMDK402TokenResult = {
|
|
|
30
33
|
*/
|
|
31
34
|
export type ParseAuthResult = {
|
|
32
35
|
valid: true;
|
|
33
|
-
|
|
36
|
+
macaroon: string;
|
|
34
37
|
preimage: string;
|
|
35
38
|
} | {
|
|
36
39
|
valid: false;
|
|
37
40
|
};
|
|
38
41
|
/**
|
|
39
|
-
* Derive a domain-specific HMAC key for
|
|
42
|
+
* Derive a domain-specific HMAC key for L402 credentials.
|
|
40
43
|
* Provides separation from checkout URL signing and webhook verification
|
|
41
44
|
* that also use MDK_ACCESS_TOKEN.
|
|
42
45
|
*/
|
|
43
|
-
export declare function
|
|
46
|
+
export declare function deriveL402Key(accessToken: string): Buffer;
|
|
44
47
|
/**
|
|
45
|
-
* Create a signed
|
|
48
|
+
* Create a signed L402 credential.
|
|
46
49
|
* Returns a base64-encoded JSON string containing the payment hash,
|
|
47
|
-
* amount, expiry, and an HMAC signature.
|
|
50
|
+
* amount, expiry, and an HMAC signature. While not a true macaroon,
|
|
51
|
+
* it is wire-compatible with the L402 protocol as an opaque credential.
|
|
48
52
|
*/
|
|
49
|
-
export declare function
|
|
53
|
+
export declare function createL402Credential(params: CreateL402CredentialParams): string;
|
|
50
54
|
/**
|
|
51
|
-
* Verify the HMAC signature and expiry of an
|
|
55
|
+
* Verify the HMAC signature and expiry of an L402 credential.
|
|
52
56
|
* Does NOT verify the payment preimage — that is a separate step.
|
|
53
57
|
*/
|
|
54
|
-
export declare function
|
|
58
|
+
export declare function verifyL402Credential(credential: string, accessToken: string): VerifyL402CredentialResult;
|
|
55
59
|
/**
|
|
56
60
|
* Verify that a preimage hashes to the expected payment hash.
|
|
57
61
|
* SHA256(preimage_bytes) must equal the payment hash.
|
|
58
62
|
*/
|
|
59
63
|
export declare function verifyPreimage(preimage: string, paymentHash: string): boolean;
|
|
60
64
|
/**
|
|
61
|
-
* Parse an
|
|
62
|
-
*
|
|
65
|
+
* Parse an L402 Authorization header.
|
|
66
|
+
* Accepts both L402 and LSAT schemes per bLIP-26 backwards compatibility.
|
|
67
|
+
* Expected format: "L402 <macaroon>:<preimage>" or "LSAT <macaroon>:<preimage>"
|
|
63
68
|
*/
|
|
64
69
|
export declare function parseAuthorizationHeader(header: string | null): ParseAuthResult;
|
package/dist/mdk402/token.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHmac, timingSafeEqual, createHash } from 'crypto';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
/** Schema for validating decoded
|
|
4
|
-
const
|
|
3
|
+
/** Schema for validating decoded L402 credential payloads. */
|
|
4
|
+
const credentialPayloadSchema = z.object({
|
|
5
5
|
paymentHash: z.string(),
|
|
6
6
|
amountSats: z.number(),
|
|
7
7
|
expiresAt: z.number(),
|
|
@@ -13,23 +13,29 @@ const tokenPayloadSchema = z.object({
|
|
|
13
13
|
/** Version tag for HMAC key derivation. Allows future token format changes. */
|
|
14
14
|
const KEY_DERIVATION_TAG = 'mdk402-token-v1';
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* L402-compatible auth scheme names.
|
|
17
|
+
* Accepts "L402" (current) and "LSAT" (legacy) per bLIP-26 backwards compat.
|
|
18
|
+
*/
|
|
19
|
+
const L402_SCHEMES = ['l402', 'lsat'];
|
|
20
|
+
/**
|
|
21
|
+
* Derive a domain-specific HMAC key for L402 credentials.
|
|
17
22
|
* Provides separation from checkout URL signing and webhook verification
|
|
18
23
|
* that also use MDK_ACCESS_TOKEN.
|
|
19
24
|
*/
|
|
20
|
-
export function
|
|
25
|
+
export function deriveL402Key(accessToken) {
|
|
21
26
|
return createHmac('sha256', accessToken)
|
|
22
27
|
.update(KEY_DERIVATION_TAG)
|
|
23
28
|
.digest();
|
|
24
29
|
}
|
|
25
30
|
/**
|
|
26
|
-
* Create a signed
|
|
31
|
+
* Create a signed L402 credential.
|
|
27
32
|
* Returns a base64-encoded JSON string containing the payment hash,
|
|
28
|
-
* amount, expiry, and an HMAC signature.
|
|
33
|
+
* amount, expiry, and an HMAC signature. While not a true macaroon,
|
|
34
|
+
* it is wire-compatible with the L402 protocol as an opaque credential.
|
|
29
35
|
*/
|
|
30
|
-
export function
|
|
36
|
+
export function createL402Credential(params) {
|
|
31
37
|
const { paymentHash, amountSats, expiresAt, accessToken, resource, amount, currency } = params;
|
|
32
|
-
const key =
|
|
38
|
+
const key = deriveL402Key(accessToken);
|
|
33
39
|
const message = `${paymentHash}\0${amountSats}\0${expiresAt}\0${resource}\0${amount}\0${currency}`;
|
|
34
40
|
const sig = createHmac('sha256', key)
|
|
35
41
|
.update(message)
|
|
@@ -38,13 +44,13 @@ export function createMDK402Token(params) {
|
|
|
38
44
|
return Buffer.from(JSON.stringify(tokenObj)).toString('base64');
|
|
39
45
|
}
|
|
40
46
|
/**
|
|
41
|
-
* Verify the HMAC signature and expiry of an
|
|
47
|
+
* Verify the HMAC signature and expiry of an L402 credential.
|
|
42
48
|
* Does NOT verify the payment preimage — that is a separate step.
|
|
43
49
|
*/
|
|
44
|
-
export function
|
|
50
|
+
export function verifyL402Credential(credential, accessToken) {
|
|
45
51
|
try {
|
|
46
|
-
const decoded = Buffer.from(
|
|
47
|
-
const parsed =
|
|
52
|
+
const decoded = Buffer.from(credential, 'base64').toString('utf8');
|
|
53
|
+
const parsed = credentialPayloadSchema.safeParse(JSON.parse(decoded));
|
|
48
54
|
if (!parsed.success) {
|
|
49
55
|
return { valid: false, reason: 'invalid_format' };
|
|
50
56
|
}
|
|
@@ -59,7 +65,7 @@ export function verifyMDK402Token(token, accessToken) {
|
|
|
59
65
|
return { valid: false, reason: 'invalid_signature' };
|
|
60
66
|
}
|
|
61
67
|
// Verify HMAC with constant-time comparison
|
|
62
|
-
const key =
|
|
68
|
+
const key = deriveL402Key(accessToken);
|
|
63
69
|
const message = `${paymentHash}\0${amountSats}\0${expiresAt}\0${resource}\0${amount}\0${currency}`;
|
|
64
70
|
const expectedSig = createHmac('sha256', key)
|
|
65
71
|
.update(message)
|
|
@@ -91,26 +97,29 @@ export function verifyPreimage(preimage, paymentHash) {
|
|
|
91
97
|
}
|
|
92
98
|
}
|
|
93
99
|
/**
|
|
94
|
-
* Parse an
|
|
95
|
-
*
|
|
100
|
+
* Parse an L402 Authorization header.
|
|
101
|
+
* Accepts both L402 and LSAT schemes per bLIP-26 backwards compatibility.
|
|
102
|
+
* Expected format: "L402 <macaroon>:<preimage>" or "LSAT <macaroon>:<preimage>"
|
|
96
103
|
*/
|
|
97
104
|
export function parseAuthorizationHeader(header) {
|
|
98
105
|
if (!header) {
|
|
99
106
|
return { valid: false };
|
|
100
107
|
}
|
|
101
|
-
|
|
108
|
+
const lower = header.toLowerCase();
|
|
109
|
+
const scheme = L402_SCHEMES.find(s => lower.startsWith(s + ' '));
|
|
110
|
+
if (!scheme) {
|
|
102
111
|
return { valid: false };
|
|
103
112
|
}
|
|
104
|
-
const credentials = header.slice(
|
|
113
|
+
const credentials = header.slice(scheme.length + 1).trim();
|
|
105
114
|
const colonIndex = credentials.indexOf(':');
|
|
106
115
|
if (colonIndex === -1) {
|
|
107
116
|
return { valid: false };
|
|
108
117
|
}
|
|
109
|
-
const
|
|
118
|
+
const macaroon = credentials.slice(0, colonIndex);
|
|
110
119
|
const preimage = credentials.slice(colonIndex + 1);
|
|
111
|
-
if (!
|
|
120
|
+
if (!macaroon || !preimage) {
|
|
112
121
|
return { valid: false };
|
|
113
122
|
}
|
|
114
|
-
return { valid: true,
|
|
123
|
+
return { valid: true, macaroon, preimage };
|
|
115
124
|
}
|
|
116
125
|
//# sourceMappingURL=token.js.map
|
package/dist/mdk402/token.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/mdk402/token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAChE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/mdk402/token.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
import type { Currency } from '@moneydevkit/api-contract';
|
|
2
2
|
/**
|
|
3
3
|
* Configuration for the withPayment wrapper.
|
|
4
|
-
* Defines pricing and token expiry for
|
|
4
|
+
* Defines pricing and token expiry for L402-gated endpoints.
|
|
5
5
|
*/
|
|
6
6
|
export type PaymentConfig = {
|
|
7
7
|
/** Fixed amount or async callback that receives the request and returns the amount. */
|
|
8
8
|
amount: number | ((req: Request) => number | Promise<number>);
|
|
9
9
|
/** Currency for pricing. SAT for direct satoshi amounts, USD for fiat conversion. */
|
|
10
10
|
currency: Currency;
|
|
11
|
-
/** How long the
|
|
11
|
+
/** How long the credential (and invoice) remain valid, in seconds. Default: 900 (15 min). */
|
|
12
12
|
expirySeconds?: number;
|
|
13
13
|
};
|
|
14
14
|
type Handler = (req: Request, context?: any) => Response | Promise<Response>;
|
|
15
15
|
/**
|
|
16
|
-
* Wrap a route handler with
|
|
16
|
+
* Wrap a route handler with L402 payment gating.
|
|
17
17
|
*
|
|
18
18
|
* Unauthenticated requests receive a 402 response with a Lightning invoice.
|
|
19
|
-
* Requests with a valid `Authorization:
|
|
19
|
+
* Requests with a valid `Authorization: L402 <macaroon>:<preimage>` header
|
|
20
20
|
* are forwarded to the inner handler after stateless verification.
|
|
21
|
+
*
|
|
22
|
+
* Also accepts the legacy LSAT scheme per bLIP-26 backwards compatibility.
|
|
21
23
|
*/
|
|
22
24
|
export declare function withPayment(config: PaymentConfig, handler: Handler): Handler;
|
|
23
25
|
export {};
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { createMoneyDevKitClient, createMoneyDevKitNode } from '../mdk';
|
|
2
2
|
import { is_preview_environment } from '../preview';
|
|
3
|
-
import {
|
|
4
|
-
/** Default
|
|
3
|
+
import { createL402Credential, parseAuthorizationHeader, verifyL402Credential, verifyPreimage, } from './token';
|
|
4
|
+
/** Default credential and invoice expiry: 15 minutes. */
|
|
5
5
|
const DEFAULT_EXPIRY_SECONDS = 900;
|
|
6
6
|
/**
|
|
7
|
-
* Wrap a route handler with
|
|
7
|
+
* Wrap a route handler with L402 payment gating.
|
|
8
8
|
*
|
|
9
9
|
* Unauthenticated requests receive a 402 response with a Lightning invoice.
|
|
10
|
-
* Requests with a valid `Authorization:
|
|
10
|
+
* Requests with a valid `Authorization: L402 <macaroon>:<preimage>` header
|
|
11
11
|
* are forwarded to the inner handler after stateless verification.
|
|
12
|
+
*
|
|
13
|
+
* Also accepts the legacy LSAT scheme per bLIP-26 backwards compatibility.
|
|
12
14
|
*/
|
|
13
15
|
export function withPayment(config, handler) {
|
|
14
16
|
return async (req, context) => {
|
|
@@ -20,34 +22,34 @@ export function withPayment(config, handler) {
|
|
|
20
22
|
suggestion: 'Set the MDK_ACCESS_TOKEN environment variable',
|
|
21
23
|
});
|
|
22
24
|
}
|
|
23
|
-
// Check for
|
|
25
|
+
// Check for L402/LSAT authorization header
|
|
24
26
|
const authHeader = req.headers.get('authorization');
|
|
25
27
|
const parsed = parseAuthorizationHeader(authHeader);
|
|
26
28
|
if (!parsed.valid) {
|
|
27
29
|
// No auth or wrong scheme -> issue a new invoice
|
|
28
30
|
return await create402Response(req, config, accessToken);
|
|
29
31
|
}
|
|
30
|
-
// Verify
|
|
31
|
-
const
|
|
32
|
-
if (!
|
|
33
|
-
if (
|
|
34
|
-
// Expired
|
|
32
|
+
// Verify credential integrity and expiry
|
|
33
|
+
const credentialResult = verifyL402Credential(parsed.macaroon, accessToken);
|
|
34
|
+
if (!credentialResult.valid) {
|
|
35
|
+
if (credentialResult.reason === 'expired') {
|
|
36
|
+
// Expired credential -> issue a fresh invoice
|
|
35
37
|
return await create402Response(req, config, accessToken);
|
|
36
38
|
}
|
|
37
39
|
return errorResponse(401, {
|
|
38
|
-
code: '
|
|
39
|
-
message: 'Invalid or malformed
|
|
40
|
+
code: 'invalid_credential',
|
|
41
|
+
message: 'Invalid or malformed L402 credential',
|
|
40
42
|
});
|
|
41
43
|
}
|
|
42
|
-
// Verify
|
|
44
|
+
// Verify credential was issued for this specific endpoint
|
|
43
45
|
const expectedResource = `${req.method}:${new URL(req.url).pathname}`;
|
|
44
|
-
if (
|
|
46
|
+
if (credentialResult.resource !== expectedResource) {
|
|
45
47
|
return errorResponse(403, {
|
|
46
48
|
code: 'resource_mismatch',
|
|
47
|
-
message: '
|
|
49
|
+
message: 'Credential was not issued for this resource',
|
|
48
50
|
});
|
|
49
51
|
}
|
|
50
|
-
// Verify
|
|
52
|
+
// Verify credential was issued for the current price
|
|
51
53
|
let currentAmount;
|
|
52
54
|
try {
|
|
53
55
|
currentAmount = typeof config.amount === 'function'
|
|
@@ -61,16 +63,16 @@ export function withPayment(config, handler) {
|
|
|
61
63
|
details: err instanceof Error ? err.message : String(err),
|
|
62
64
|
});
|
|
63
65
|
}
|
|
64
|
-
if (
|
|
66
|
+
if (credentialResult.amount !== currentAmount || credentialResult.currency !== config.currency) {
|
|
65
67
|
return errorResponse(403, {
|
|
66
68
|
code: 'amount_mismatch',
|
|
67
|
-
message: '
|
|
69
|
+
message: 'Credential was not issued for this price',
|
|
68
70
|
});
|
|
69
71
|
}
|
|
70
72
|
// Verify payment proof (skip in preview/sandbox mode)
|
|
71
73
|
const isPreview = is_preview_environment();
|
|
72
74
|
if (!isPreview) {
|
|
73
|
-
if (!verifyPreimage(parsed.preimage,
|
|
75
|
+
if (!verifyPreimage(parsed.preimage, credentialResult.paymentHash)) {
|
|
74
76
|
return errorResponse(401, {
|
|
75
77
|
code: 'invalid_payment_proof',
|
|
76
78
|
message: 'Invalid payment preimage',
|
|
@@ -84,7 +86,10 @@ export function withPayment(config, handler) {
|
|
|
84
86
|
/**
|
|
85
87
|
* Build the 402 Payment Required response with a Lightning invoice.
|
|
86
88
|
* Creates a checkout on the MDK backend for dashboard visibility,
|
|
87
|
-
* generates an invoice via the local Lightning node, and signs an
|
|
89
|
+
* generates an invoice via the local Lightning node, and signs an L402 credential.
|
|
90
|
+
*
|
|
91
|
+
* The WWW-Authenticate header follows bLIP-26 format:
|
|
92
|
+
* L402 macaroon="<credential>", invoice="<bolt11>"
|
|
88
93
|
*/
|
|
89
94
|
async function create402Response(req, config, accessToken) {
|
|
90
95
|
// Resolve dynamic pricing
|
|
@@ -137,11 +142,11 @@ async function create402Response(req, config, accessToken) {
|
|
|
137
142
|
scid: invoiceResult.scid,
|
|
138
143
|
});
|
|
139
144
|
node.destroy(); // Clean up node connection
|
|
140
|
-
// 4. Create signed
|
|
145
|
+
// 4. Create signed L402 credential bound to this endpoint
|
|
141
146
|
const expiresAt = Math.floor(invoiceResult.expiresAt.getTime() / 1000);
|
|
142
147
|
const amountSats = pendingCheckout.invoiceAmountSats ?? checkout.invoiceAmountSats ?? 0;
|
|
143
148
|
const resource = `${req.method}:${new URL(req.url).pathname}`;
|
|
144
|
-
const
|
|
149
|
+
const macaroon = createL402Credential({
|
|
145
150
|
paymentHash: invoiceResult.paymentHash,
|
|
146
151
|
amountSats,
|
|
147
152
|
expiresAt,
|
|
@@ -150,14 +155,14 @@ async function create402Response(req, config, accessToken) {
|
|
|
150
155
|
amount,
|
|
151
156
|
currency: config.currency,
|
|
152
157
|
});
|
|
153
|
-
// 5. Build 402 response
|
|
154
|
-
const wwwAuthenticate = `
|
|
158
|
+
// 5. Build 402 response with L402-compatible headers (bLIP-26)
|
|
159
|
+
const wwwAuthenticate = `L402 macaroon="${macaroon}", invoice="${invoiceResult.invoice}"`;
|
|
155
160
|
return new Response(JSON.stringify({
|
|
156
161
|
error: {
|
|
157
162
|
code: 'payment_required',
|
|
158
163
|
message: 'Payment required',
|
|
159
164
|
},
|
|
160
|
-
|
|
165
|
+
macaroon,
|
|
161
166
|
invoice: invoiceResult.invoice,
|
|
162
167
|
paymentHash: invoiceResult.paymentHash,
|
|
163
168
|
amountSats,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"with-payment.js","sourceRoot":"","sources":["../../src/mdk402/with-payment.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAA;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AACnD,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"with-payment.js","sourceRoot":"","sources":["../../src/mdk402/with-payment.ts"],"names":[],"mappings":"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"}
|