Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion auth/authenticator.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ 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)
mac, preimage, discharges, err := l402.FromHeader(header)
if err != nil {
log.Debugf("Deny: %v", err)
return false
Expand All @@ -51,6 +51,7 @@ func (l *L402Authenticator) Accept(header *http.Header, serviceName string) bool
Macaroon: mac,
Preimage: preimage,
TargetService: serviceName,
Discharges: discharges,
}
err = l.minter.VerifyL402(context.Background(), verificationParams)
if err != nil {
Expand Down
101 changes: 71 additions & 30 deletions l402/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,13 @@ 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) {
//
// The returned discharge macaroons will be non-nil when the binary-encoded
// macaroon data contains more than one macaroon (as per the macaroon.Slice
// convention, the first is the root macaroon and the rest are discharges for
// its third-party caveats).
func FromHeader(header *http.Header) (*macaroon.Macaroon, lntypes.Preimage,
[]*macaroon.Macaroon, error) {
var authHeader string

switch {
Expand All @@ -63,32 +69,50 @@ func FromHeader(header *http.Header) (*macaroon.Macaroon, lntypes.Preimage, erro
}

if len(matches) != 4 {
return nil, lntypes.Preimage{}, fmt.Errorf("invalid "+
"auth header format: %s", authHeader)
return nil, lntypes.Preimage{}, nil,
Comment on lines 71 to +72

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Slightly out of scope, but I realize there's a bug here: https://github.com/djkazic/aperture/blob/4e3194e169b577cc63b887331415a533043bab39/l402/header.go#L58-L69

That should be a break.

fmt.Errorf("invalid auth header "+
"format: %s", authHeader)
}

// Decode the content of the two parts of the header value.
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)
return nil, lntypes.Preimage{}, nil,
fmt.Errorf("base64 decode of macaroon "+
"failed: %v", err)
}
mac := &macaroon.Macaroon{}
err = mac.UnmarshalBinary(macBytes)
if err != nil {
return nil, lntypes.Preimage{}, fmt.Errorf("unable to "+
"unmarshal macaroon: %v", err)

// Use Slice to unmarshal so we can extract discharge
// macaroons if present. By convention the first macaroon

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Interesting, so now there'll be multiple macaroons, space delimited as part of the header? Do we need to update the spec to account for this?

// is the root and the rest are discharges.
var slice macaroon.Slice
if err := slice.UnmarshalBinary(macBytes); err != nil {
return nil, lntypes.Preimage{}, nil,
fmt.Errorf("unable to unmarshal "+
"macaroon: %v", err)
}
if len(slice) == 0 {
return nil, lntypes.Preimage{}, nil,
fmt.Errorf("no macaroon found in " +
"auth header")
}
mac := slice[0]
var discharges []*macaroon.Macaroon
if len(slice) > 1 {
discharges = slice[1:]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Re spec again, right now depending on an implicit ordering here.

}

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{}, nil,
fmt.Errorf("hex decode of preimage "+
"failed: %v", err)
}

// All done, we don't need to extract anything from the
// macaroon since the preimage was presented separately.
return mac, preimage, nil
return mac, preimage, discharges, nil

// Header field 2: Contains only the macaroon.
case header.Get(HeaderMacaroonMD) != "":
Expand All @@ -99,43 +123,60 @@ func FromHeader(header *http.Header) (*macaroon.Macaroon, lntypes.Preimage, erro
authHeader = header.Get(HeaderMacaroon)

default:
return nil, lntypes.Preimage{}, fmt.Errorf("no auth header " +
"provided")
return nil, lntypes.Preimage{}, nil, fmt.Errorf(
"no auth header provided",
)
}

// For case 2 and 3, we need to actually unmarshal the macaroon to
// extract the preimage.
// extract the preimage. Use Slice to support discharge macaroons.
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{}, nil, fmt.Errorf("hex decode "+
"of macaroon failed: %v", err)
}
mac := &macaroon.Macaroon{}
err = mac.UnmarshalBinary(macBytes)
if err != nil {
return nil, lntypes.Preimage{}, fmt.Errorf("unable to "+
var slice macaroon.Slice
if err := slice.UnmarshalBinary(macBytes); err != nil {
return nil, lntypes.Preimage{}, nil, fmt.Errorf("unable to "+
"unmarshal macaroon: %v", err)
}
if len(slice) == 0 {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should add a test case for this.

return nil, lntypes.Preimage{}, nil, fmt.Errorf(
"no macaroon found in header",
)
}
mac := slice[0]
var discharges []*macaroon.Macaroon
if len(slice) > 1 {
discharges = slice[1:]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same logic in a few areas, can extract into a helper functio.

}

preimageHex, ok := HasCaveat(mac, PreimageKey)
if !ok {
return nil, lntypes.Preimage{}, errors.New("preimage caveat " +
"not found")
return nil, lntypes.Preimage{}, nil, 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{}, nil, fmt.Errorf("hex decode "+
"of preimage failed: %v", err)
}

return mac, preimage, nil
return mac, preimage, discharges, nil
}

// SetHeader sets the provided authentication elements as the default/standard
// HTTP header for the L402 protocol.
// HTTP header for the L402 protocol. If discharges are provided, they are
// serialized alongside the root macaroon using the macaroon.Slice convention.
func SetHeader(header *http.Header, mac *macaroon.Macaroon,
preimage fmt.Stringer) error {
preimage fmt.Stringer, discharges []*macaroon.Macaroon) error {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's make the discharges ...*macaroon.Macaroon`, then we won't break the API.


macBytes, err := mac.MarshalBinary()
// Build a Slice with the root macaroon first, followed by any
// discharge macaroons, then serialize.
slice := macaroon.Slice{mac}
slice = append(slice, discharges...)
macBytes, err := slice.MarshalBinary()
if err != nil {
return err
}
Expand Down
Loading
Loading