next-l402
Version:
Lightning Network HTTP 402 Payment Required protocol implementation for Next.js applications
103 lines • 4.57 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.markPaymentComplete = exports.createChallengeHandler = exports.createChallengeResponse = void 0;
const server_1 = require("next/server");
const token_utils_1 = require("./token-utils");
const cache_1 = require("./cache");
const macaroons_js_1 = require("macaroons.js");
const uuid_1 = require("uuid");
// Default configuration values
const DEFAULT_PRICE_SATS = 100;
const DEFAULT_TOKEN_VALIDITY_SECONDS = 24 * 60 * 60; // 24 hours
/**
* Creates a challenge response for L402 authentication
*/
const createChallengeResponse = async (options) => {
// Create an invoice for the payment
const invoice = await options.lightning.createInvoice(options.priceSats || DEFAULT_PRICE_SATS);
// Generate a secret key if not provided
const secretKey = options.secretKey || Buffer.from((0, uuid_1.v4)().replace(/-/g, ''), 'hex');
// Create the macaroon identifier with payment hash
const identifier = (0, token_utils_1.createMacaroonIdentifier)(invoice.paymentHash);
// Create the macaroon
const location = options.location || 'https://localhost:3000';
// Create macaroon with builder pattern
const paymentHashBase64 = Buffer.from(invoice.paymentHash, 'hex').toString('base64');
const expirationTime = Date.now() + DEFAULT_TOKEN_VALIDITY_SECONDS * 1000;
let macaroonBuilder = new macaroons_js_1.MacaroonsBuilder(location, secretKey.toString('hex'), identifier.toString('base64'));
// Add required caveats
macaroonBuilder.add_first_party_caveat(`payment_hash = ${paymentHashBase64}`);
macaroonBuilder.add_first_party_caveat(`expiration = ${expirationTime}`);
// Add any custom caveats
if (options.caveats) {
options.caveats.forEach((caveat) => {
macaroonBuilder.add_first_party_caveat(`${caveat.type} = ${caveat.value}`);
});
}
// Get the macaroon and serialize it
const macaroon = macaroonBuilder.getMacaroon();
const serializedMacaroon = macaroon.serialize();
// Store the session in cache for later validation
const session = {
macaroon: serializedMacaroon,
invoice: {
paymentHash: invoice.paymentHash,
paymentRequest: invoice.paymentRequest,
amountSats: invoice.amountSats,
},
secretKey,
createdAt: Date.now(),
};
cache_1.l402Cache.setSession(invoice.paymentHash, session);
// Create the WWW-Authenticate header with the macaroon
const wwwAuthenticate = (0, token_utils_1.createWwwAuthenticateHeader)(serializedMacaroon, invoice.paymentRequest);
return {
invoice,
wwwAuthenticate,
macaroon: serializedMacaroon,
};
};
exports.createChallengeResponse = createChallengeResponse;
/**
* Creates an L402 challenge endpoint that handles Lightning invoice generation
*/
const createChallengeHandler = (options) => {
return async (req) => {
try {
const challenge = await (0, exports.createChallengeResponse)(options);
// Cache the challenge for the target route
const targetRoute = req.nextUrl.searchParams.get('route') || '/api/protected/default';
const cachedChallenge = {
wwwAuthenticate: challenge.wwwAuthenticate,
paymentHash: challenge.invoice.paymentHash,
createdAt: Date.now(),
};
cache_1.l402Cache.setCachedChallenge(targetRoute, cachedChallenge);
return new server_1.NextResponse('Payment Required', {
status: 402,
headers: {
'WWW-Authenticate': challenge.wwwAuthenticate,
'Content-Type': 'text/plain',
},
});
}
catch {
// Error handling for challenge creation failures
return new server_1.NextResponse('Internal Server Error', {
status: 500,
headers: { 'Content-Type': 'text/plain' },
});
}
};
};
exports.createChallengeHandler = createChallengeHandler;
/**
* Payment completion handler - call this after Lightning payment is verified
*/
const markPaymentComplete = (_paymentHash) => {
// This would typically update a cache or database to mark the payment as completed
// For now, the cached session creation in createChallengeResponse handles this
// Future: implement payment completion logic here
};
exports.markPaymentComplete = markPaymentComplete;
//# sourceMappingURL=challenge.js.map
;