From 923653232200b1b4472f021e829f1c3d0038d203 Mon Sep 17 00:00:00 2001 From: gofeuer <11652547+gofeuer@users.noreply.github.com> Date: Sat, 10 Aug 2024 21:46:37 -0300 Subject: [PATCH] Accept comma separated macaroons --- auth/authenticator.go | 5 +++- l402/header.go | 48 +++++++++++++++++++++++++------------- l402/server_interceptor.go | 5 +++- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/auth/authenticator.go b/auth/authenticator.go index 80cfc23..7250904 100644 --- a/auth/authenticator.go +++ b/auth/authenticator.go @@ -41,12 +41,15 @@ func (l *L402Authenticator) Accept(header *http.Header, serviceName string) bool // Try reading the macaroon and preimage from the HTTP header. This can // be in different header fields depending on the implementation and/or // protocol. - mac, preimage, err := l402.FromHeader(header) + macs, preimage, err := l402.FromHeader(header) if err != nil { log.Debugf("Deny: %v", err) return false } + // TODO: Be able to accept multiple macaroons + mac := macs[0] + verificationParams := &mint.VerificationParams{ Macaroon: mac, Preimage: preimage, diff --git a/l402/header.go b/l402/header.go index bca5419..19864d6 100644 --- a/l402/header.go +++ b/l402/header.go @@ -7,6 +7,7 @@ import ( "fmt" "net/http" "regexp" + "strings" "github.com/lightningnetwork/lnd/lntypes" "gopkg.in/macaroon.v2" @@ -42,7 +43,7 @@ var ( // // If only the macaroon is sent in header 2 or three then it is expected to have // a caveat with the preimage attached to it. -func FromHeader(header *http.Header) (*macaroon.Macaroon, lntypes.Preimage, error) { +func FromHeader(header *http.Header) (macaroon.Slice, lntypes.Preimage, error) { var authHeader string switch { @@ -71,14 +72,22 @@ func FromHeader(header *http.Header) (*macaroon.Macaroon, lntypes.Preimage, erro macBase64, preimageHex := matches[2], matches[3] macBytes, err := base64.StdEncoding.DecodeString(macBase64) if err != nil { - return nil, lntypes.Preimage{}, fmt.Errorf("base64 "+ - "decode of macaroon failed: %v", err) + // macBase64 might be multiple macaroons separated by commas, + // so we strip them and try again + macBase64 = strings.ReplaceAll(macBase64, ",", "") + macBytes, err = base64.StdEncoding.DecodeString(macBase64) + if err != nil { + return nil, lntypes.Preimage{}, + fmt.Errorf("base64 decode of macaroon failed: %v", err) + } } - mac := &macaroon.Macaroon{} + // macBytes can contain multiple macaroons, + // (* macaroon.Slice) knows how to decode them without separators + mac := make(macaroon.Slice, 0, 1) err = mac.UnmarshalBinary(macBytes) if err != nil { - return nil, lntypes.Preimage{}, fmt.Errorf("unable to "+ - "unmarshal macaroon: %v", err) + return nil, lntypes.Preimage{}, + fmt.Errorf("unable to unmarshal macaroon: %v", err) } preimage, err := lntypes.MakePreimageFromStr(preimageHex) if err != nil { @@ -107,24 +116,31 @@ func FromHeader(header *http.Header) (*macaroon.Macaroon, lntypes.Preimage, erro // extract the preimage. macBytes, err := hex.DecodeString(authHeader) if err != nil { - return nil, lntypes.Preimage{}, fmt.Errorf("hex decode of "+ - "macaroon failed: %v", err) + return nil, lntypes.Preimage{}, + fmt.Errorf("hex decode of macaroon failed: %v", err) } - mac := &macaroon.Macaroon{} + mac := make(macaroon.Slice, 0, 1) err = mac.UnmarshalBinary(macBytes) if err != nil { return nil, lntypes.Preimage{}, fmt.Errorf("unable to "+ "unmarshal macaroon: %v", err) } - preimageHex, ok := HasCaveat(mac, PreimageKey) - if !ok { - return nil, lntypes.Preimage{}, errors.New("preimage caveat " + - "not found") + var preimageHex string + var ok bool + for _, m := range mac { + if preimageHex, ok = HasCaveat(m, PreimageKey); ok { + break + } + } + if preimageHex == "" || !ok { + return nil, lntypes.Preimage{}, + errors.New("preimage caveat not found") } + preimage, err := lntypes.MakePreimageFromStr(preimageHex) if err != nil { - return nil, lntypes.Preimage{}, fmt.Errorf("hex decode of "+ - "preimage failed: %v", err) + return nil, lntypes.Preimage{}, + fmt.Errorf("hex decode of preimage failed: %v", err) } return mac, preimage, nil @@ -132,7 +148,7 @@ func FromHeader(header *http.Header) (*macaroon.Macaroon, lntypes.Preimage, erro // SetHeader sets the provided authentication elements as the default/standard // HTTP header for the L402 protocol. -func SetHeader(header *http.Header, mac *macaroon.Macaroon, +func SetHeader(header *http.Header, mac macaroon.Slice, preimage fmt.Stringer) error { macBytes, err := mac.MarshalBinary() diff --git a/l402/server_interceptor.go b/l402/server_interceptor.go index f2af76b..ca79803 100644 --- a/l402/server_interceptor.go +++ b/l402/server_interceptor.go @@ -92,11 +92,14 @@ func tokenFromContext(ctx context.Context) (*TokenID, error) { } log.Debugf("Auth header present in request: %s", md.Get(HeaderAuthorization)) - macaroon, _, err := FromHeader(header) + macaroons, _, err := FromHeader(header) if err != nil { return nil, fmt.Errorf("auth header extraction failed: %v", err) } + // TODO: Be able to accept multiple macaroons + macaroon := macaroons[0] + // If there is an L402, decode and add it to the context. identifier, err := DecodeIdentifier(bytes.NewBuffer(macaroon.Id())) if err != nil {