🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@powforge/l402-verify

Package Overview
Dependencies
Maintainers
1
Versions
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@powforge/l402-verify

Standalone L402 (Lightning HTTP 402) payment verifier. Zero runtime dependencies. Parses Authorization: L402 <macaroon>: headers, verifies the HMAC-signed macaroon, checks sha256(preimage) === payment_hash, and confirms invoice settlement via LN

latest
Source
npmnpm
Version
0.1.0
Version published
Weekly downloads
5
-50%
Maintainers
1
Weekly downloads
 
Created
Source

@powforge/l402-verify

Standalone L402 (Lightning HTTP 402) payment verifier. Zero runtime dependencies.

The verification half of @powforge/mcp-l402-gate, extracted as a small, citable reference for the x402 / L402 ecosystem. Use it when you want to accept L402 payments without pulling in the full MCP gate + identity-score stack.

What it does

  • parseL402Header(header) — parse Authorization: L402 <macaroon>:<preimage>
  • verifyMacaroon(macaroon, opts) — recompute the HMAC-SHA256 signature, check expiry, scope, optional replay
  • preimageMatchesHash(preimage, paymentHash) — constant-time sha256(preimage) === payment_hash
  • checkInvoicePaid(paymentHash, opts) — single LNBits GET to see if the invoice is settled
  • verifyL402(authHeader, opts) — compose all four into one async call

What it does NOT do

  • Mint invoices, mint macaroons, build 402 challenges (issuer-side; use @powforge/mcp-l402-gate)
  • Hold any Lightning admin keys
  • Lookup identity scores (separate package: @powforge/identity)
  • Persist replay-state (callers own the usedSet — in-memory Set, Redis, SQLite, whatever you like)

Install

npm install @powforge/l402-verify

Requires Node 18+ (for globalThis.fetch).

Use

One-call verification

const { verifyL402 } = require('@powforge/l402-verify');

const result = await verifyL402(req.headers.authorization, {
  secret: process.env.L402_SECRET,
  expectedScope: 'mcp-l402-gate:call',
  lnbitsUrl: process.env.LNBITS_URL,
  lnbitsApiKey: process.env.LNBITS_INVOICE_KEY,
  usedSet: globalReplaySet,  // optional: Set<string> of redeemed preimages
});

if (!result.valid) {
  return res.status(401).json({ error: result.reason });
}
// result = { valid: true, paymentHash, scope, caveats, expiresAt, preimage }

Bring-your-own paid-check (LND, Phoenix, hosted)

const result = await verifyL402(req.headers.authorization, {
  secret: process.env.L402_SECRET,
  expectedScope: 'pay-per-call',
  checkPaidFn: async (paymentHash) => {
    // Ask your own Lightning node / wallet
    const inv = await lnd.lookupInvoice({ r_hash_str: paymentHash });
    return inv.state === 'SETTLED';
  },
});

Step-by-step (advanced)

const { parseL402Header, verifyMacaroon, preimageMatchesHash, checkInvoicePaid } = require('@powforge/l402-verify');

const parsed = parseL402Header(req.headers.authorization);
if (!parsed) return res.status(400).json({ error: 'bad L402 header' });

const v = verifyMacaroon(parsed.macaroon, {
  secret: process.env.L402_SECRET,
  expectedScope: 'mcp-l402-gate:call',
});
if (!v.ok) return res.status(401).json({ error: v.reason });

if (!preimageMatchesHash(parsed.preimage, v.paymentHash)) {
  return res.status(401).json({ error: 'preimage mismatch' });
}

const paid = await checkInvoicePaid(v.paymentHash, {
  lnbitsUrl: process.env.LNBITS_URL,
  lnbitsApiKey: process.env.LNBITS_INVOICE_KEY,
});
if (!paid) return res.status(402).json({ error: 'not yet paid' });

// caller is paid + macaroon is valid — proceed

Wire format

Macaroon = base64url(JSON.stringify({
  v: 1,
  ph: <hex payment hash>,
  sc: "<scope>",
  exp: <unix seconds>,
  cav: { ... },
  sig: <hex hmac-sha256 over canonical JSON of body sans sig>
}))

Authorization: L402 <macaroon>:<preimage_hex>

This is the same wire format produced by @powforge/mcp-l402-gate@0.3+. If you mint with one and verify with the other, they will line up.

L402 background

L402 is the Lightning Labs spec for using HTTP 402 + Lightning Network as the payment rail. The server returns WWW-Authenticate: L402 macaroon="...", invoice="...", the client pays the invoice, takes the preimage, and re-requests with Authorization: L402 <macaroon>:<preimage>. The server then verifies (1) the macaroon HMAC, (2) that sha256(preimage) == payment_hash, and (3) that the invoice actually settled on the Lightning Network.

This package implements step 3 against LNBits by default; pass your own checkPaidFn for any other Lightning backend (LND, Phoenix, Voltage, hosted).

Spec: https://docs.lightning.engineering/the-lightning-network/l402

Replay protection

The package is stateless by default. If you want replay protection, pass a usedSet (any object with .has(string) and .add(string) — a JS Set works). On a successful verifyL402 call, the preimage is added to the set. On any subsequent call with the same preimage, verification fails with reason: "preimage already redeemed".

For multi-process deployments, back the set with Redis or a database; the interface is just { has, add }.

License

MIT. PowForge.

Keywords

l402

FAQs

Package last updated on 12 May 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts