Skip to content

multi: update L402 headers for bLIP-0026 token= spec revision#208

Open
Roasbeef wants to merge 8 commits into
lightninglabs:masterfrom
Roasbeef:blip-updates
Open

multi: update L402 headers for bLIP-0026 token= spec revision#208
Roasbeef wants to merge 8 commits into
lightninglabs:masterfrom
Roasbeef:blip-updates

Conversation

@Roasbeef
Copy link
Copy Markdown
Member

In this PR, we update aperture's L402 header handling to align with the
bLIP-0026 spec revision, which generalizes the protocol from "macaroon"
to "token" terminology. The core change is that the WWW-Authenticate
challenge header now uses token= as the key name (with a version=
parameter for forward compatibility), while the legacy macaroon=
format is retained for backwards compatibility with existing clients.

This is the implementation counterpart to
bLIP-0026 PR #26,
which accumulated feedback from reviewers pointing out that the tight
coupling to macaroon terminology was causing potential adopters to
believe macaroons were a hard requirement. The spec now makes clear
that L402 is token-format agnostic (any token that can commit to a
payment hash works), with macaroons as the RECOMMENDED format.

See each commit message for a detailed description w.r.t the
incremental changes.

Server-Side Challenge Header

FreshChallengeHeader now emits distinct formats per scheme. The L402
header uses version="0", token="<base64>", invoice="<bolt11>" while
the legacy LSAT header retains the old macaroon="<base64>", invoice="<bolt11>" format. The LSAT header is still sent first
(via header.Set) because old loop software reads only the first
WWW-Authenticate value.

Server-Side Header Parsing

FromHeader now checks six header variants in priority order:
Authorization (LSAT/L402), Grpc-Metadata-Token, Token,
Grpc-Metadata-Macaroon, Macaroon. Current-spec header names are
checked before legacy ones. The new HeaderTokenMD and HeaderToken
constants are added alongside the existing legacy constants (which are
retained and marked as backwards compat).

Client-Side Credential

MacaroonCredential.GetRequestMetadata now sends the serialized token
under both the current-spec "token" key and the legacy "macaroon"
key. This ensures the client works with both new servers (that check
for "token") and old servers (that only check for "macaroon").

Client-Side Challenge Parsing

The authHeaderRegex in the client interceptor now accepts both
token= and macaroon= key names, with an optional version=
parameter prefix. This handles all four format combinations a server
might send.

CORS

addCorsHeaders now includes Grpc-Metadata-Token, Token, and
Macaroon in the allowed headers list. Without this, browser-based
clients using spec-compliant header names were CORS-blocked.

Bug Fix

We also fix a pre-existing bug in FreshChallengeHeader where a
MarshalBinary failure was logged but not returned, allowing
execution to continue with nil macBytes.

Forwards/Backwards Compatibility Tests

A new l402/header_test.go covers the full matrix of header formats
across all layers: FromHeader parsing all six variants with correct
preimage extraction, header priority ordering, SetHeader dual-scheme
output, the client interceptor regex matching all format combinations
(including future version values), and MacaroonCredential sending
both gRPC metadata keys. The existing auth/authenticator_test.go and
l402/client_interceptor_test.go are updated to cover the new header
names and dual-key metadata behavior.

In this commit, we add two new header constants (`HeaderTokenMD` and
`HeaderToken`) per the bLIP-0026 spec revision, which renames the
gRPC metadata key from "macaroon" to "token". The `FromHeader`
function now checks these current-spec header names first, falling
back to the legacy `Grpc-Metadata-Macaroon` and `Macaroon` headers
for backwards compatibility.

The existing `HeaderMacaroonMD` and `HeaderMacaroon` constants are
retained and marked as legacy. Error messages and comments throughout
`FromHeader` are updated from "macaroon" to "token" to reflect the
spec terminology. The priority order is: Authorization (LSAT/L402),
then current-spec metadata headers, then legacy metadata headers.
In this commit, we update `MacaroonCredential.GetRequestMetadata` to
send the serialized token under both the current-spec "token" key and
the legacy "macaroon" key. Previously only "macaroon" was sent, which
meant a strict bLIP-0026 server that only checks for the "token" key
would reject gRPC requests from this client. Sending both keys ensures
forwards compatibility with new servers while maintaining backwards
compatibility with servers that haven't upgraded yet.
In this commit, we update `authHeaderRegex` to accept both the
current-spec `token=` key name and the legacy `macaroon=` key name in
`WWW-Authenticate` challenge headers. We also handle the optional
`version=` parameter prefix introduced in bLIP-0026. The regex now
matches all four combinations: `LSAT macaroon=` (oldest), `L402
macaroon=` (transitional), `L402 token=` (current without version),
and `L402 version="0", token=` (current with version).
In this commit, we update `FreshChallengeHeader` to emit distinct
header formats for the legacy LSAT and current L402 schemes. The L402
header now uses the bLIP-0026 format with `version="0", token=...`,
while the legacy LSAT header retains the old `macaroon=...` format.
Previously both schemes emitted the same `macaroon=` format string.

We also fix a pre-existing bug where a `MarshalBinary` failure was
logged but not returned, allowing execution to continue with nil
macBytes and produce an invalid challenge header with an empty token
field.
In this commit, we update the `MockAuthenticator` to match the real
authenticator's behavior after the bLIP-0026 spec revision. The
`Accept` method now checks current-spec header names
(`Grpc-Metadata-Token`, `Token`) before legacy ones, mirroring the
priority order in `l402.FromHeader`. The `FreshChallengeHeader` method
now emits distinct formats per scheme: `version="0", token=...` for
L402 and `macaroon=...` for legacy LSAT.
In this commit, we add `Grpc-Metadata-Token`, `Token`, and `Macaroon`
to the CORS `Access-Control-Allow-Headers` list alongside the existing
`Grpc-Metadata-macaroon`. Without this, browser-based L402 clients
using the current-spec header names would be blocked by CORS preflight
checks.
In this commit, we add comprehensive tests covering the full matrix of
L402 header formats to ensure forwards and backwards compatibility
with the bLIP-0026 spec revision.

The new `l402/header_test.go` covers:
 - `FromHeader` parsing all six header variants (Authorization with
   LSAT/L402 schemes, Grpc-Metadata-Token, Token, Grpc-Metadata-Macaroon,
   Macaroon) with correct preimage extraction in each case.
 - Header priority ordering (Authorization > current-spec metadata >
   legacy metadata).
 - `SetHeader` emitting both LSAT and L402 Authorization values that
   are each independently parseable by `FromHeader`.
 - The client interceptor regex matching all format combinations:
   `LSAT macaroon=` (oldest), `L402 macaroon=` (transitional), `L402
   token=` (current without version), `L402 version="0", token=`
   (current with version), and future version values. Invalid schemes
   and missing invoice fields are rejected.
 - `MacaroonCredential.GetRequestMetadata` sending both "token" and
   "macaroon" gRPC keys with identical hex-encoded values.

The `auth/authenticator_test.go` gets three new cases testing that
`L402Authenticator.Accept` handles the current-spec `HeaderTokenMD`
and `HeaderToken` header names (valid and invalid).

The `l402/client_interceptor_test.go` assertions are updated to
expect two metadata keys ("token" + "macaroon") per credential, with
both carrying the same value.
In this commit, we fix tokenFromContext to also map the "token" and
"macaroon" gRPC metadata keys into the HTTP header struct passed to
FromHeader. Previously only the Authorization header was forwarded,
which meant the standard MacaroonCredential flow (which sends data
under the "token" and "macaroon" metadata keys) was never picked up
by the server interceptor. The gRPC transport strips the
"Grpc-Metadata-" prefix, so we map "token" to HeaderTokenMD and
"macaroon" to HeaderMacaroonMD for FromHeader to find them.
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.

1 participant