Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

blip-0026: L402 - Lightning HTTP 402 Protocol #26

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

Roasbeef
Copy link
Contributor

@Roasbeef Roasbeef commented Jun 7, 2023

In this commit, we add a bLIP for the L402 (formerly known as LSAT)
protocol. The L402 protocol presents a standardized way of adding LN
micropayments to any existing HTTP-REST or gRPC API. The L402 protocol
repurposes the HTTP 402 Payment Required error code with the necessary
authentication headers required to bind a request's validity to the
payment of an LN invoice. Macaroons are used as flexible authentication
credentials (supports custom caveats, attenuation, etc) which allow an
L402 reverse-proxy to validate an L402 API key without backend LN node
interaction for each request (the macaroon commits to the payment hash).

@Roasbeef
Copy link
Contributor Author

Roasbeef commented Jun 7, 2023

cc @wpaulino @guggero @Kodylow

Copy link
Contributor

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! Thanks for writing this up, this is nifty. The auth protocol described here is wonderfully simple and deserves to be specified, but this document is hugely verbose and adds a wall of text and discussion that makes this protocol look downright complicated and scary. I'm confident this could be cut down by at least half without any less ability to communicate all its detail and make it much less scary to a dev who just wants to use it :)

blip-0026.md Outdated Show resolved Hide resolved
blip-0026.md Outdated
Comment on lines 232 to 297
The following steps describe the diagram further below. It is the flow of calls
that take place for a client software that wants to access a protected resource
that is secured by an authentication server.

As an example, we will look at the `loopd` client that wants to do a loop out
swap with the Lightning Lab's loop server.

**First time looping out**:

1. A loop user wishes to perform a swap with the loop server. They type the
command `loop out <amount>` and hit return.

2. The `loopd` client program contacts the loop server to initiate the swap.

3. The call from the client must always go through the authentication server
reverse proxy, which in this example is `aperture`. The authentication proxy
notices that the client didn't send an L402 and therefore cannot be granted
access to the loop server.

4. `aperture` instructs its own `lnd` instance to create an invoice over a
small amount that is required to acquire a fresh credential.

5. In addition to the invoice, `aperture` also creates a fresh access
credential that is tied to the invoice. The credential is cryptographically
constructed in a way that it is only valid once the invoice has been paid.

6. The credential and the invoice are sent back to the client in the previously
unused HTTP header `402 Payment Required`.

7. The `loopd` understands this returned error code, extracts the invoice from
it and automatically instructs its connected `lnd` instance to pay the
invoice.

8. Paying the invoice results in the `loopd` client now possessing the
cryptographic proof of payment \(the pre-image\). This proof is stored in
the client's local storage, together with the access credential.

9. The combination of the access credential and the pre-image yields a fully
valid L402 that can be cryptographically verified.

10. The client now repeats the original request to the loop server, now
attaching the L402 to the request.

11. The authentication server intercepts the request, extracts the L402 and
validates it. Because the L402 is valid, the request is forwarded to the
actual loop server that then initiates the swap.

12. The answer of the swap server is returned to the client and the swap is now
initiated.

13. The whole process is fully transparent to the user. The only thing they
might notice is a short delay of a few seconds on the first ever loop. Each
successive loop will use the same credential and will not be delayed at
all.

![e2e flow sequence diagram](bip-00026/e2e-flow.png)

**All further loops**:

1. For every new request to the server, the client now automatically attaches
the credential that is stored locally.

2. As long as the credential has not expired, the steps 9-13 above will be
followed. If/when the credential expires, the server will start over at
step 4 and instruct
the client to obtain a fresh credential.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is written as an example, not a "Detailed authentication flow", it seems strange to include it in a normative "Specification" section. ISTM it would be much clearer without all the various names.

blip-0026.md Outdated
However, there are downsides to this approach; for example, if a user switches
WiFi networks, their credential becomes unusable.

### HTTP Specification
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the gRPC section feel like "the specification" - why not replace the "Authentication Flow" section with them?

blip-0026.md Outdated
## L402 HTTP/gRPC Protocol Specification

This section defines the "L402" authentication scheme, which transmits
credentials as `<macaroon(s)>:<preimage>` pairs, where the preimage is encoded
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused by the use of "macaroon" here - the common term for this appears to be "token", whereas macaroon has a more niche and specific definition which I'm not sure quite fits.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cryptographic properties of macaroons are desired, so the distinction is intentional.
Resources expounding on the application utility of these cryptographic properties are here: https://docs.lightning.engineering/the-lightning-network/l402/macaroons

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my note at #26 (comment) - certainly a server implementing this protocol would use macaroons or something like them, but there's no need to be specific about it here, and the thing that the server returns may be a macaroon, but from the perspective of this protocol, its just an opaque token.

Copy link
Contributor Author

@Roasbeef Roasbeef Jun 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and the thing that the server returns may be a macaroon, but from the perspective of this protocol, its just an opaque token.

The requirement here is that the API key itself commits to the payment hash within the invoice. This attribute is central to the protocol. Committing to the payment hash in the api key is what allows the reverse proxy implementing this protocol to be more or less stateless: given the pre-image and the macaroon (symmetric key hmac chain that includes a commitment to the payment hash in the chain), it can validate if an API key is complete or not. The API key here is a two-tuple of (macaroon, preimage).

This structure means that the reverse proxy doesn't need to maintain all the invoice state of the LN node it's connected to, and also doesn't need to hit an API to see if an invoice has been paid or not for each request. At high loads, such a requirement would prohibit many use cases.

It's feasible that one could use JWT's or w/e (inheriting all the baggage) other structured API keys here, but then we'd also have to specify how for each of those, the committed payment hash is to be parsed and recognized. Macaroons are more or less less standardized in their structure (tons of implementations in various languages that can all parse the same macaroons), and in use elsewhere in other independent ecosystems.

Devs are totally free to spin a similar protocol using PASETO or w/e another JWT alternatives of course. The goal of this document is to specify the L402 protocol as already defined and deployed in the wild. It's also worth noting that these headers are just for the L402 portion, the server being proxied to gets these headers and can interpret them if they wish, but can also layer w/e other authentication mechanism (oauth, etc, etc) as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The requirement here is that the API key itself commits to the payment hash within the invoice. This attribute is central to the protocol. Committing to the payment hash in the api key is what allows the reverse proxy implementing this protocol to be more or less stateless: given the pre-image and the macaroon (symmetric key hmac chain that includes a commitment to the payment hash in the chain), it can validate if an API key is complete or not. The API key here is a two-tuple of (macaroon, preimage).

Exactly, the sensible way to implement this is the stateless approach, but it also doesn't need to be any specific format, and I'm also not convinced the spec has to write out a specific format - certainly a server could implement this in a stateful way (or commit to some other id which is used to derive the payment hash, index into a later-generated map/DB of payments, etc), it would just be less performant.

It's feasible that one could use JWT's or w/e (inheriting all the baggage) other structured API keys here, but then we'd also have to specify how for each of those, the committed payment hash is to be parsed and recognized.

Right that was my point below, I'm not convinced it makes sense to do that specification here - it seems orthogonal to the actual specification of an authentication flow, and removing it entirely doesn't take away from the ability of someone to read this document and fully implement it in an interoperable way.

If would also make sense as an appendix, but focusing on it throughout the document only makes the document more confusing as there's more crammed in what is otherwise a nicely simple authentication scheme.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kinda feel like there's a disagreement here in terms of what's in scope of the L402 nomenclature.

I'd classify it as the "data pieces and their format in the HTTP headers (or grpc calls)". Feels like there's a strong opinion herein to also include the authentication token format and specification, but it's pretty obvious to me that these are separable/unimportant from a client's perspective.

For any client that wants to know how to parse and pay/return a call from an L402 response, the auth token format is a distraction. In that case, the "data pieces and format" are all that really matter.

If there's other data or formatting that we should consider, that's probably worth documenting here. But the server side security/auth and/or how stateful/stateless it needs to be seems like an implementation detail.

This structure means that the reverse proxy doesn't need to maintain all the invoice state of the LN node it's connected to, and also doesn't need to hit an API to see if an invoice has been paid or not for each request.

This is a nice property that you can implement in any way/with a variety of tokens, but I'd argue that it's a "nice to have" of what you can do with the L402 tokens+preimages. You can also dumbly store state and do lookups (which you kinda have to do anyway for time-based access tokens / and or there different approaches to this).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but it's pretty obvious to me that these are separable/unimportant from a client's perspective.

Agreed, note that this information is only really relevant to the L402 proxy. The web service may still require the client to attach normal authentication information such as cookies or w/e other custom information. In most cases, the L402 proxy can be oblivious about the actual request information it's proxying, it only needs to ensure all the headers are copied over as normal. A client also doesn't have to introspect into the credential at all, it just encodes it with base64, then puts it where the server expects it.

The expectation isn't that all active web services switch over to macaroons, it's that only the proxy needs to care about it, and otherwise the API backend can go unchanged.

I've updated these sections to mention the optimization (committing to the hash in the credential), and weakened the language around verification.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

proxy // in-app HTTP handler middleware is baaaaaasically the same thing only difference is which machine it's running on, no?

I don't understand how where the L402 is being handled has a material impact on the specification of the "data pieces and their format" which is kiinda all we need from a spec?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I can see why there might be an argument to keeping the specification here agnostic on the encoding of the token itself, but as someone who has implemented this as a middleware already, I do think there are benefits to having this specified as well. By standardizing the full encoding you make the L402 token itself much more portable such that clients that don't have to have a relationship with the server itself in order to know how to decode and validate the macaroon. If the encoding is standardized, then any consumer of the L402 will know how to deserialize the macaroon portion, read the payment hash and validate against the preimage.

There's also ecosystem benefits to having a system like this be cross-compatible. As an example, using the LSAT Playground (I guess I need to rebrand this now...) you can validate an L402 generated from boltwall or aperture, you can even take an L402 generated from aperture, add a caveat in the playgrond, and then have it validate against a server running boltwall middleware. You get additional benefits imo when you want to start running in environments like Nostr (if we could get L402 auth standardized for nostr) where you might have different clients and servers needing to interoperate, or something like https://sphinx.chat.

This is particularly useful when caveats come in to play so that clients can introspect their token to see how much time might be left before their authorization expires for example. So all of this plays a pretty important role in the nature of these being stateless: even though it could be encoded in other ways, having it be standardized makes it far more portable.

I don't understand how where the L402 is being handled has a material impact on the specification of the "data pieces and their format" which is kiinda all we need from a spec?

Aren't the "data pieces and their format" covered by specifying the token being a base64 encoded macaroon?

blip-0026.md Outdated Show resolved Hide resolved
@Roasbeef Roasbeef marked this pull request as draft June 7, 2023 06:08
@niftynei
Copy link

niftynei commented Jul 7, 2023

hey yall, i've been making a L402 lib for the CLN ecosystem. Here's a bit of feedback.

  • This doc contains way more than necessary to implement the L402 protocol. Sometimes more verbiage + info can be useful, but in this case it is actually a disservice towards helping people build or re-implement the spec for themselves. I say this having had to parse it to write a conforming Flask interceptor.
  • the usage of the term macaroons in the spec is unnecessarily restrictive. you can use any token/hmac system that you'd like. I've been using token in my implementation.

Cool protocol, really nice that this finally is getting some proper documentation that we can do cross implementation on

In this commit, we add a bLIP for the L402 (formerly known as LSAT)
protocol. The L402 protocol presents a standardized way of adding LN
micropayments to any existing HTTP-REST or gRPC API. The L402 protocol
repurposes the HTTP 402 Payment Required error code with the necessary
authentication headers required to bind a request's validity to the
payment of an LN invoice. Macaroons are used as flexible authentication
credentials (supports custom caveats, attenuation, etc) which allow an
L402 reverse-proxy to validate an L402 API key without backend LN node
interaction for each request (the macaroon commits to the payment hash).
@Roasbeef
Copy link
Contributor Author

Roasbeef commented Jul 8, 2023

Thanks for the feedback @niftynei!

This doc contains way more than necessary to implement the L402 protocol

The latest push significantly cuts down on the size of the document. Further edits are likely possible as well near the start and end of the specification.

the usage of the term macaroons in the spec is unnecessarily restrictive

The latest iteration weakens the language around macaroons, and moves the section on a suggested structure to the appendix. You're correct that anything can be used in practice, IMO given the macaroons are already specified and standardized, I think it makes sense to nudge proxy implementations in that direction. Note that clients don't need to introspect into them at all, and just serialize them where expected. It also isn't expected the API backends rely soley on L402 for in-depth authentication, normal auth headers or cookies or w/e can still be used. The proxies are meant to be loosely coupled to the backends they support, such that any API can be put in front of a proxy and Things Just Work^TM.

@Roasbeef Roasbeef marked this pull request as ready for review July 8, 2023 01:55
Copy link

@niftynei niftynei left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added suggested comments to update (at least the HTTP section) to conform to the nomenclautre i'm using in chargesats (macroon -> token).

Moving the Specification up within the first 200 lines is a massive improvement from befrore. 👌


2. The server decides that payment is required to access the endpoint. They
return a HTTP status code of `402` along with a `WWW-Authenticate` header
containing a macaroon and invoice. The macaroon commits to the payment hash
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
containing a macaroon and invoice. The macaroon commits to the payment hash
containing a token and invoice. Ideally, the token commits to the payment hash

payment pre-image `r` to the payment hash `H`, `H = sha256(r)`

4. The client re-issues the request with a `Authorization` header that includes
the payment preimage `r`, and the macaroon sent by the server.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
the payment preimage `r`, and the macaroon sent by the server.
the payment preimage `r`, and the token sent by the server.

the payment preimage `r`, and the macaroon sent by the server.

5. The server verifies the cryptographically verifies the authenticity and
integrity of the macaroon, then ensures the payment hash committed to in the
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
integrity of the macaroon, then ensures the payment hash committed to in the
integrity of the token, then ensures the payment hash committed to in the


5. The server verifies the cryptographically verifies the authenticity and
integrity of the macaroon, then ensures the payment hash committed to in the
macaroon `H`, satisfies the relation the `H = sha256(r)`.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
macaroon `H`, satisfies the relation the `H = sha256(r)`.
token `H`, satisfies the relation the `H = sha256(r)`.


6. The server returns a 200 OK error code along with the specified resource.

If the service supports persistent L402 credential re-use, then a client SHOULD
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
If the service supports persistent L402 credential re-use, then a client SHOULD
If the service supports persistent L402 token re-use, then a client SHOULD

?? not sure about this one. Do you mean something other than the auth/hmac token here?

In other words, to receive authorization, the client:

* Pays the invoice from the server, thus revealing the invoice’s preimage
* Constructs the L402 by concatenating the base64-encoded macaroon\(s\), a
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Constructs the L402 by concatenating the base64-encoded macaroon\(s\), a
* Constructs the L402 by concatenating the encoded auth token\(s\), a

* Constructs the L402 by concatenating the base64-encoded macaroon\(s\), a
single colon \(":"\), and the hex-encoded preimage.

Since the macaroon and the preimage are both binary data encoded in an ASCII
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Since the macaroon and the preimage are both binary data encoded in an ASCII
Since the auth token and the preimage are both binary data encoded in an ASCII

based format, there should be no problem with either containing control
characters or colons \(see "CTL" in [_Appendix B.1 of
\[RFC5234\]_](https://tools.ietf.org/html/rfc5234#appendix-B.1)\). If a user
provides a macaroon or preimage containing any of these characters, this is to
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
provides a macaroon or preimage containing any of these characters, this is to
provides an auth token or preimage containing any of these characters, this is to

be considered an invalid L402 and SHOULD result in a 402 and authentication
information as specified above.

If a client wishes to send the macaroon `"AGIAJEemVQUTEyNCR0exk7ek90Cg=="`
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
If a client wishes to send the macaroon `"AGIAJEemVQUTEyNCR0exk7ek90Cg=="`
If a client wishes to send the auth token `"AGIAJEemVQUTEyNCR0exk7ek90Cg=="`

must be skipped by the authorizer as the macaroon holder can further attenuate
the macaroon for other applications.

### Macaroon Revocation
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
### Macaroon Revocation
### Auth Token Revocation

Copy link

@bucko13 bucko13 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woot! Exciting to see this being formalized and to hear other people are working on it 🎉

up the ladder. Typically, a user must _manually_ navigate a web-page to request
an upgrade to a higher tier, or downgrade to a lower tier. With the L402
standard, tier upgrades can easily be automated: the user hits a new endpoint
to obtain an _upgraded_ L402 which _encodes_ additional functionality or
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just noting this part here wrt to the conversations about if macaroons are implementation detail or part of the spec. Using macaroons for the token is part of what makes this possible since attenuation like this is part of the design goal of macaroons.

lacks credentials or contains an L402 that is invalid or insufficient in some
way:

1. Ths server SHOULD derive a economical price for the endpoint expressed in
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
1. Ths server SHOULD derive a economical price for the endpoint expressed in
1. Ths server SHOULD derive an economical price for the endpoint expressed in

Comment on lines +452 to +455
The `L402` protocol was formerly known as `LSAT`. In order to preserve
backwards compatibility with clients, anywhere `L402` is used within HTTP
headers or requests, a valid protocol flow with the string `LSAT` should also
be accepted.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

macaroons are created, attenuated, and verified as part of L402. This chapter
will require an understanding of how macaroons work and how they are useful in
the context of authentication. It may be useful to skim the [introductory
research paper on macaroons](https://research.google/pubs/pub41892/).
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be good to reference some implementations as well. Not all of them are interoperable unfortunately, so highlighting ones that are standard compliant would be useful as well as for further reference on how they work which can be helpful to understanding.


To prevent abusers of a macaroon-based authenticated service, a macaroon should
be able to be revoked. This can be achieved by having the minter remove the
macaroon’s corresponding root key. By doing so, the minter will never be able
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately this makes the system somewhat less stateless :-/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's purely optional, otherwise you can implement time based revocation just via a caveat expiry. I think it also depends on the service, some may be able to get away with a single global service key, while others may already require per user/account keys for the macaroons. In theory, you can have the best of both worlds if a deterministic scheme is used to derivation of the symmetric keys.

Copy link
Contributor

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for making this more readable. I think still a good chunk of text could be cut down.

Comment on lines +82 to +110
# Motivation

The early creators of the Web created a cut out for a future Internet-native
payment system in the form of the HTTP 402 Payment Required status code. This
status code was intended to be returned if payment was required in order to be
able to fetch/interact with the resources at a given HTTP endpoint. The 402
status code thus presented a way to enable HTTP-native payment metered APIs on
the Web. The rise of the Transport Layer Security protocol, enables payments on
the Web, albeit via client-side interfaces that rely on opaque payment networks
such as credit cards. With the rise of Bitcoin, and the Lightning Network, the
Internet now has a native digital currency and a low-cost, low-latency, high
volume payment system to enable highly scalable payments over open payment
infrastructure.

This document specifies the L402 protocol which utilizes the 402 status code,
macaroons, the Lightning Network, and special authorization headers to create a
native payment-metering system for the Web. An L402 credential can serve both
as authentication, as well as a payment mechanism \(one can view it as a
ticket\) for paid APIs. By leveraging L402, a service or business is able to
offer a new tier of paid APIs that sit between free, and subscription: pay as
you go.

One can view L402 credential (a macaroon) as a fancy authentication credential
or cookie. They differ from regular cookies in that they're a cryptographically
verifiable bearer credential. An L402 credential _encodes_ all its capabilities
within a macaroon which can only be created by the end service provider. The
L402 specification uses a combination of `HTTP` as well as the Lightning Network
to create a seamless end-to-end payment+authentication flow for the
next-generation of paid APIs built on top of the Lightning Network.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the abstract repeat almost entirely the same things, one or the other should be removed.


# Abstract

L402 is a protocol standard based on the HTTP 402 Payment Required error code
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now a bit confusing - the abstract and motivation sections repeatedly refer to the auth token as a token or macaroon interchangeably. I'd suggest sticking with token as its the more general/accepted term, but either way just stick with one.


This section defines the client-side L402 authentication headers, which
transmits credentials as `<macaroon(s)>:<preimage>` pairs, where the preimage
is encoded as hex and the macaroon is encoded as base64. Multiple macaroons are
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean the client has to re-encode what it got as base64, or that we're requiring the server to encode the auth tokens as base64? If its the server, it should be in the preceding section, not in the section about the client behavior.


## L402 HTTP/gRPC Protocol Specification

### Server HTTP Authentication Headers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the auth flow sections below repeat this info, can we just elide the two "authentication headers" sections entirely?


A valid response header MUST take the form of:
```
WWW-Authenticate: L402 macaroon="M", invoice="P"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this define the invoice as bolt11 (not necessarily the parameter name, but just written out in the requirements, rather than simply "lightning invoice", which is now ambiguous).

the L402 authentication scheme and the macaroon needed for the client to
form a complete L402.

A valid response header MUST take the form of:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For forwards-compat, should we specify that unknown key=V parameters are ignored? eg that would allow use of bolt12 offers or some other invoicing scheme in the future.

@slavakurilyak
Copy link

Any updates on this since last year?

/cc @Roasbeef

@ProofOfKeags
Copy link

Any updates on this since last year?

What updates are you looking for specifically?

@TheBlueMatt
Copy link
Contributor

Presumably either feedback being address or the author saying they don't want to address feedback so we can merge this! :)

@Roasbeef
Copy link
Contributor Author

@slavakurilyak Hi, I've been gathering some significant feedback from a dozen or so teams/companies actively working on integrating the protocol into their offerings. We've been meeting once a month or so to discuss improvements to the protocol and also generally how things are fairing in the wild. I plan to make future PRs to start to incorporate some of this feedback (eg: ways to handle more stateful APIs, structured information outside the macaroon caveats, etc, etc).

With all that said, I'll move to merge this in a few days (addressing the obvious typos/wording improvements suggested above), as the current text has been adequate for those seeking to actively build upon the protocol, and some felt that the lack of a merged PR meant the protocol was in an ambiguous state.

Copy link

@positiveblue positiveblue left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the push @Roasbeef, I'm thrilled about this! 🚀

Regarding L402, I would suggest focusing on macaroons and having the spec define how caveats are structured (as we do here). Later, we can even include n appendix on "standard" caveats (e.g., expires vs expires_at). But I would do that once we observe their usage in the wild.

I'd like to propose two additions:

Versioning in the WWW-Authenticate header: Adding a version field apart from "macaroon" and "invoice" would allow for different "encoding" in the future, enabling clients/servers to understand and respond appropriately. If we call the current version V0

 L402 version="V0" macaroon="X", invoice="Y"

Returning multiple macaroons/invoices: While the exact implementation in the WWW-Authenticate header is uncertain, it would be beneficial to return an array of "Offers" with specific macaroons/invoices for each one, along with data explaining their purpose (like "supported plans" and what each plan includes). If versioning is part of the protocol (🤞 ), we can fix this in the future and even return them in the response body instead of the header in future versions of the protocol.


1. The server MUST verify the cryptographic authenticity and integrity of the
credential.
1.1. If a credential is invalid, then the server MUST return a 401

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The markdown needs to be fixed. The subsections are not rendered properly. (1.1, 3.1 and 3.2)


1. Should verify that the contained BOLT 11 invoice doesn't request an
excessive amount of Bitcoin.
1.1. If so, then the client SHOULD abandon the HTTP request.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

format: same here

are essential to the verification of macaroons, so they must be stored securely
and reliably.

#### Macaroon Identifier

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we may be over specifying a bit here.

Why wouldn't user_id/macaroon_version be caveats?

An L402 credentials are linked to a specific challenge (invoice) and what identifies that invoice is the payment_hash so I would go with that in my implementation and leave everything else to caveats.

Not sure why this needs to be specified though? Unlike biscuits, macaroons can only be validated by the issuer, so the id should be whatever the issuer wants without encoding extra information/encoding whatever the issuer wants.


For the L402 protocol the challenge takes the following form:
```
L402 macaroon="X", invoice="Y"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the " needed?

Aperture, Fewsats and lsat-js do not add them but Sulu does. I think it is because of how it is specified here.

If I could choose, I would say let's go without them.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you are referring to two different things here @positiveblue . From what I see Aperture does add the " double-quotes when generating the "challenge" header, as can be seen here. Sulu replicates this functionality, as does lsat-js.

It is the challenge response with the L402 token that does not use " , as can be seen in the code from Aperture. Sulu does the same.

I think the use of " in the www-authenticate header, while to my knowledge not strictly necessary, its actually quite typical. See for example:

See also here.

In my opinion we should keep with standard practices here, so I see no problem maintaining the " for the www-authenticate header.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, I was following this diagram and checked the wrong header:

sequenceDiagram
     participant L as Lightning Node
     participant C as Client
     participant S as Server
     C->>S: GET HTTP/1.1 Request
     S->>C: HTTP/1.1 402 Payment Required
     Note over S: WWW-Authenticate: L402 macaroon=X invoice=Y
     C->>L: Pay invoice
     L->>C: Returns pre-image
     C->>S: GET HTTP/1.1 Request
     Note over C: Authorization: L402 macaroon:preimage
     S->>S: Verify L402 authorization
     S->>C: HTTP/1.1 200 OK

I see that even aperture does it that way too

Case closed

participant S as Server
C->>S: GET HTTP/1.1 Request
S->>C: HTTP/1.1 402 Payment Required
Note over S: WWW-Authenticate: L402 macaroon=X invoice=Y

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Note over S: WWW-Authenticate: L402 macaroon=X invoice=Y
Note over S: WWW-Authenticate: L402 macaroon="X" invoice="Y"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants