UNPKG

next-l402

Version:

Lightning Network HTTP 402 Payment Required protocol implementation for Next.js applications

103 lines 4.57 kB
"use strict"; 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