mirror of
https://github.com/lightninglabs/aperture.git
synced 2025-12-17 09:04:19 +01:00
auth: create invoice request with closure, add challenger
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
@@ -9,8 +8,6 @@ import (
|
||||
"regexp"
|
||||
|
||||
"github.com/lightninglabs/kirin/macaroons"
|
||||
"github.com/lightninglabs/loop/lndclient"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"gopkg.in/macaroon-bakery.v2/bakery"
|
||||
"gopkg.in/macaroon-bakery.v2/bakery/checkers"
|
||||
)
|
||||
@@ -20,31 +17,27 @@ var (
|
||||
opWildcard = "*"
|
||||
)
|
||||
|
||||
type LndAuthenticator struct {
|
||||
client lnrpc.LightningClient
|
||||
// LsatAuthenticator is an authenticator that uses the LSAT protocol to
|
||||
// authenticate requests.
|
||||
type LsatAuthenticator struct {
|
||||
challenger Challenger
|
||||
macService *macaroons.Service
|
||||
}
|
||||
|
||||
// A compile time flag to ensure the LndAuthenticator satisfies the
|
||||
// A compile time flag to ensure the LsatAuthenticator satisfies the
|
||||
// Authenticator interface.
|
||||
var _ Authenticator = (*LndAuthenticator)(nil)
|
||||
var _ Authenticator = (*LsatAuthenticator)(nil)
|
||||
|
||||
// NewLndAuthenticator creates a new authenticator that is connected to an lnd
|
||||
// backend and can create new invoices if required.
|
||||
func NewLndAuthenticator(cfg *Config) (*LndAuthenticator, error) {
|
||||
client, err := lndclient.NewBasicClient(
|
||||
cfg.LndHost, cfg.TlsPath, cfg.MacDir, cfg.Network,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// NewLsatAuthenticator creates a new authenticator that authenticates requests
|
||||
// based on LSAT tokens.
|
||||
func NewLsatAuthenticator(challenger Challenger) (*LsatAuthenticator, error) {
|
||||
macService, err := macaroons.NewService()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &LndAuthenticator{
|
||||
client: client,
|
||||
return &LsatAuthenticator{
|
||||
challenger: challenger,
|
||||
macService: macService,
|
||||
}, nil
|
||||
}
|
||||
@@ -53,7 +46,7 @@ func NewLndAuthenticator(cfg *Config) (*LndAuthenticator, error) {
|
||||
// to a given backend service.
|
||||
//
|
||||
// NOTE: This is part of the Authenticator interface.
|
||||
func (l *LndAuthenticator) Accept(header *http.Header) bool {
|
||||
func (l *LsatAuthenticator) Accept(header *http.Header) bool {
|
||||
authHeader := header.Get("Authorization")
|
||||
log.Debugf("Trying to authorize with header value [%s].", authHeader)
|
||||
if authHeader == "" {
|
||||
@@ -103,29 +96,23 @@ func (l *LndAuthenticator) Accept(header *http.Header) bool {
|
||||
// complete.
|
||||
//
|
||||
// NOTE: This is part of the Authenticator interface.
|
||||
func (l *LndAuthenticator) FreshChallengeHeader(r *http.Request) (
|
||||
func (l *LsatAuthenticator) FreshChallengeHeader(r *http.Request) (
|
||||
http.Header, error) {
|
||||
|
||||
// Obtain a new invoice from lnd first. We need to know the payment hash
|
||||
// so we can add it as a caveat to the macaroon.
|
||||
ctx := context.Background()
|
||||
invoice := &lnrpc.Invoice{
|
||||
Memo: "LSAT",
|
||||
Value: 1,
|
||||
}
|
||||
response, err := l.client.AddInvoice(ctx, invoice)
|
||||
paymentRequest, paymentHash, err := l.challenger.NewChallenge()
|
||||
if err != nil {
|
||||
log.Errorf("Error adding invoice: %v", err)
|
||||
log.Errorf("Error creating new challenge: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
paymentHashHex := hex.EncodeToString(response.RHash)
|
||||
|
||||
// Create a new macaroon and add the payment hash as a caveat.
|
||||
// The bakery requires at least one operation so we add an "allow all"
|
||||
// permission set for now.
|
||||
mac, err := l.macService.NewMacaroon(
|
||||
[]bakery.Op{{Entity: opWildcard, Action: opWildcard}}, []string{
|
||||
checkers.Condition(macaroons.CondRHash, paymentHashHex),
|
||||
checkers.Condition(
|
||||
macaroons.CondRHash, paymentHash.String(),
|
||||
),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@@ -135,8 +122,7 @@ func (l *LndAuthenticator) FreshChallengeHeader(r *http.Request) (
|
||||
|
||||
str := "LSAT macaroon='%s' invoice='%s'"
|
||||
str = fmt.Sprintf(
|
||||
str, base64.StdEncoding.EncodeToString(mac),
|
||||
response.GetPaymentRequest(),
|
||||
str, base64.StdEncoding.EncodeToString(mac), paymentRequest,
|
||||
)
|
||||
header := r.Header
|
||||
header.Set("WWW-Authenticate", str)
|
||||
@@ -8,17 +8,6 @@ import (
|
||||
"github.com/lightninglabs/kirin/freebie"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
// LndHost is the hostname of the LND instance to connect to.
|
||||
LndHost string `long:"lndhost" description:"Hostname of the LND instance to connect to"`
|
||||
|
||||
TlsPath string `long:"tlspath"`
|
||||
|
||||
MacDir string `long:"macdir"`
|
||||
|
||||
Network string `long:"network"`
|
||||
}
|
||||
|
||||
type Level string
|
||||
|
||||
func (l Level) lower() string {
|
||||
|
||||
@@ -1,15 +1,26 @@
|
||||
package auth
|
||||
|
||||
import "net/http"
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
)
|
||||
|
||||
// Authenticator is the generic interface for validating client headers and
|
||||
// returning new challenge headers.
|
||||
type Authenticator interface {
|
||||
// Accept returns whether or not the header successfully authenticates the user
|
||||
// to a given backend service.
|
||||
// Accept returns whether or not the header successfully authenticates
|
||||
// the user to a given backend service.
|
||||
Accept(*http.Header) bool
|
||||
|
||||
// FreshChallengeHeader returns a header containing a challenge for the user to
|
||||
// complete.
|
||||
// FreshChallengeHeader returns a header containing a challenge for the
|
||||
// user to complete.
|
||||
FreshChallengeHeader(r *http.Request) (http.Header, error)
|
||||
}
|
||||
|
||||
// Challenger is an interface for generating new payment challenges.
|
||||
type Challenger interface {
|
||||
// NewChallenge creates a new LSAT payment challenge, returning a
|
||||
// payment request (invoice) and the corresponding payment hash.
|
||||
NewChallenge() (string, lntypes.Hash, error)
|
||||
}
|
||||
|
||||
74
challenger.go
Normal file
74
challenger.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package kirin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/lightninglabs/kirin/auth"
|
||||
"github.com/lightninglabs/loop/lndclient"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
)
|
||||
|
||||
// InvoiceRequestGenerator is a function type that returns a new request for the
|
||||
// lnrpc.AddInvoice call.
|
||||
type InvoiceRequestGenerator func() (*lnrpc.Invoice, error)
|
||||
|
||||
// LndChallenger is a challenger that uses an lnd backend to create new LSAT
|
||||
// payment challenges.
|
||||
type LndChallenger struct {
|
||||
client lnrpc.LightningClient
|
||||
genInvoiceReq InvoiceRequestGenerator
|
||||
}
|
||||
|
||||
// A compile time flag to ensure the LndChallenger satisfies the
|
||||
// Challenger interface.
|
||||
var _ auth.Challenger = (*LndChallenger)(nil)
|
||||
|
||||
// NewLndChallenger creates a new challenger that uses the given connection
|
||||
// details to connect to an lnd backend to create payment challenges.
|
||||
func NewLndChallenger(cfg *authConfig, genInvoiceReq InvoiceRequestGenerator) (
|
||||
auth.Challenger, error) {
|
||||
|
||||
if genInvoiceReq == nil {
|
||||
return nil, fmt.Errorf("genInvoiceReq cannot be nil")
|
||||
}
|
||||
|
||||
client, err := lndclient.NewBasicClient(
|
||||
cfg.LndHost, cfg.TlsPath, cfg.MacDir, cfg.Network,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &LndChallenger{
|
||||
client: client,
|
||||
genInvoiceReq: genInvoiceReq,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewChallenge creates a new LSAT payment challenge, returning a payment
|
||||
// request (invoice) and the corresponding payment hash.
|
||||
//
|
||||
// NOTE: This is part of the Challenger interface.
|
||||
func (l *LndChallenger) NewChallenge() (string, lntypes.Hash, error) {
|
||||
// Obtain a new invoice from lnd first. We need to know the payment hash
|
||||
// so we can add it as a caveat to the macaroon.
|
||||
invoice, err := l.genInvoiceReq()
|
||||
if err != nil {
|
||||
log.Errorf("Error generating invoice request: %v", err)
|
||||
return "", lntypes.ZeroHash, err
|
||||
}
|
||||
ctx := context.Background()
|
||||
response, err := l.client.AddInvoice(ctx, invoice)
|
||||
if err != nil {
|
||||
log.Errorf("Error adding invoice: %v", err)
|
||||
return "", lntypes.ZeroHash, err
|
||||
}
|
||||
paymentHash, err := lntypes.MakeHash(response.RHash)
|
||||
if err != nil {
|
||||
log.Errorf("Error parsing payment hash: %v", err)
|
||||
return "", lntypes.ZeroHash, err
|
||||
}
|
||||
|
||||
return response.PaymentRequest, paymentHash, nil
|
||||
}
|
||||
14
config.go
14
config.go
@@ -2,7 +2,6 @@ package kirin
|
||||
|
||||
import (
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightninglabs/kirin/auth"
|
||||
"github.com/lightninglabs/kirin/proxy"
|
||||
)
|
||||
|
||||
@@ -17,6 +16,17 @@ var (
|
||||
defaultMaxLogFileSize = 10
|
||||
)
|
||||
|
||||
type authConfig struct {
|
||||
// LndHost is the hostname of the LND instance to connect to.
|
||||
LndHost string `long:"lndhost" description:"Hostname of the LND instance to connect to"`
|
||||
|
||||
TlsPath string `long:"tlspath"`
|
||||
|
||||
MacDir string `long:"macdir"`
|
||||
|
||||
Network string `long:"network"`
|
||||
}
|
||||
|
||||
type config struct {
|
||||
// ListenAddr is the listening address that we should use to allow Kirin
|
||||
// to listen for requests.
|
||||
@@ -26,7 +36,7 @@ type config struct {
|
||||
// is located.
|
||||
StaticRoot string `long:"staticroot" description:"The folder where the static content is located."`
|
||||
|
||||
Authenticator *auth.Config `long:"authenticator" description:"Configuration for the authenticator."`
|
||||
Authenticator *authConfig `long:"authenticator" description:"Configuration for the authenticator."`
|
||||
|
||||
// Services is a list of JSON objects in string format, which specify
|
||||
// each backend service to Kirin.
|
||||
|
||||
35
kirin.go
35
kirin.go
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/lightninglabs/kirin/auth"
|
||||
"github.com/lightninglabs/kirin/proxy"
|
||||
"github.com/lightningnetwork/lnd/build"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
@@ -37,17 +38,14 @@ func start() error {
|
||||
return fmt.Errorf("unable to set up logging: %v", err)
|
||||
}
|
||||
|
||||
// Create the auxiliary services the proxy needs to work.
|
||||
authenticator, err := auth.NewLndAuthenticator(cfg.Authenticator)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
servicesProxy, err := proxy.New(
|
||||
authenticator, cfg.Services, cfg.StaticRoot,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
// Create the proxy and connect it to lnd.
|
||||
genInvoiceReq := func() (*lnrpc.Invoice, error) {
|
||||
return &lnrpc.Invoice{
|
||||
Memo: "LSAT",
|
||||
Value: 1,
|
||||
}, nil
|
||||
}
|
||||
servicesProxy, err := createProxy(cfg, genInvoiceReq)
|
||||
server := &http.Server{
|
||||
Addr: cfg.ListenAddr,
|
||||
Handler: http.HandlerFunc(servicesProxy.ServeHTTP),
|
||||
@@ -103,6 +101,23 @@ func setupLogging(cfg *config) error {
|
||||
return build.ParseAndSetDebugLevels(cfg.DebugLevel, logWriter)
|
||||
}
|
||||
|
||||
// createProxy creates the proxy with all the services it needs.
|
||||
func createProxy(cfg *config, genInvoiceReq InvoiceRequestGenerator) (
|
||||
*proxy.Proxy, error) {
|
||||
|
||||
challenger, err := NewLndChallenger(
|
||||
cfg.Authenticator, genInvoiceReq,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authenticator, err := auth.NewLsatAuthenticator(challenger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return proxy.New(authenticator, cfg.Services, cfg.StaticRoot)
|
||||
}
|
||||
|
||||
// cleanup closes the given server and shuts down the log rotator.
|
||||
func cleanup(server *http.Server) {
|
||||
err := server.Close()
|
||||
|
||||
Reference in New Issue
Block a user