L402: The Missing Piece in the Internet’s Payment Infrastructure

L402 is an open protocol that implements internet-native paywalls by building upon the HTTP 402 Payment Required status code and the Lightning Network.

Live demo

New Monetization Models

Unlocks new revenue streams by enabling micropayments, pay-per-use and granular access control models.

Open and Extensible

L402's open protocol encourages innovation and wide adoption across industries, fostering a thriving ecosystem of applications and services.

Language agnostic

L402's design is universally compatible, ensuring seamless integration across various programming environments and platforms.

Native Digital Payments

Integrates with the Lightning Network for instant, low-cost transactions, perfect for API monetization and digital services.

Granular Access Control

Leveraging macaroons, L402 enables fine-grained access control and secure token management for enhanced security and flexibility.

AI-Friendly Protocol

Built on HTTP, the Lightning Network, and Macaroons, L402 provides a machine-friendly scheme perfect for AI applications and automated systems.

L402 Protocol Flow

sequenceDiagram participant Client participant Server Client->>Server: Request access to HTTP resource Server-->Server: Check client credentials (invalid) Server-->Server: Generate challenge (macaroon + invoice) Server-->>Client: Return HTTP 402 status code & challenge Client->>Client: Complete challenge & obtain preimage Client->>Server: Request access L402 Authentication header (macaroon + preimage) Server->>Server: Verify credentials and proof of completion for the linked challenge Server-->>Client: Serve the requested resource

Server: Challenge Details

Before processing a request to a protected endpoint, the server issues an L402 challenge by returning a 402 Payment Required HTTP response with the challenge details encoded in a response header.

The challenge includes a macaroon (a token granting specific permissions) and a Lightning Network invoice.

              
macaroon = "MDAzNmxvY2F0aW9uIGh0dHBzOi8vbHNhdC1wbGF5Z3Jv..."
invoice = "lnbc12340n1pny96vwpp5hc4l9wl8ze9jajratkzuxa5..."

challenge = "L402 macaroon="{macaroon}"  invoice="{invoice}"

# The challenge is returned in the response headers 
response.headers['WWW-Authenticate'] = challenge

Client: Authentication Header

Upon receiving an HTTP 402 Payment Required status, the client extracts the macaroon and Lightning invoice from the challenge.

The client then pays the Lightning invoice and obtains the preimage. Using the macaroon and preimage, the client constructs an authenticated request to the server, which will now be processed since the client has completed the required payment and authentication steps.

              
# Client prepares the authentication header with the 
# macaroon and preimage
challenge = response.headers['WWW-Authenticate']
macaroon, invoice = parse_challenge(challenge)

# Pay the lightning invoice to receive the preimage
preimage = pay_invoice(invoice)
auth_header = f"L402 {macaroon}:{preimage}"

# Client sends a request with the authentication header
headers = {'Authorization': f"L402 {macaroon}:{preimage}"}
requests.get(url, headers=headers)

Macaroons

Macaroons are a crucial component of the L402 protocol, providing a secure and flexible way to manage client permissions.

They are bearer tokens that contain caveats, which specify the constraints and limitations of the token. In the context of L402, macaroons are used to grant access to specific resources or services based on the client’s payment.

The server generates a macaroon with the appropriate caveats, such as an expiration time or scope limitations, and includes it in the challenge header.

              
# URL where the credentials can be used
location = "fewsats.com"
version = 0
payment_hash = "35cf3da4dfdefa01a3859659d447eb2eeb070c9c6610f4..."
token_id = "7133548b39c094b83120052b10685d7cc5..."

# The identifier needs to be encoded as bytes
identifier = encode_id(version, payment_hash, token_id)

# The caveats can be used to add constraints to the macaroon
caveats = [
  "expires_at=2024-06-14T01:38:46Z"
]

macaroon = create_macaroon(identifier, location, caveats)

print(macaroon.encode())
# AgELZmV3c2F0cy5jb20CQgAANc89pN/e+gG...

print(macaroon.json())
# {
#   "ID": "000035cf3da4dfdefa01a385965...",
#   "version": 0,
#   "payment_hash": "35cf3da4dfdefa01a385965...",
#   "token_id": "7133548b39c094b83120052b106...",
#   "location": "fewsats.com",
#   "caveats": [
#     "expires_at=2024-06-14T01:38:46Z"
#   ]
# }