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

Fix/rename lsat to l402 #28

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# LSAT-Middleware

A middleware library for [Gin](https://github.com/gin-gonic/gin) and [Echo](https://echo.labstack.com/) framework that uses [LSAT](https://lsat.tech/) (a protocol standard for authentication and paid APIs) and provides handler functions to accept microtransactions before serving ad-free content or any paid APIs.
A middleware library for [Gin](https://github.com/gin-gonic/gin) and [Echo](https://echo.labstack.com/) framework that uses [L402, formerly known as LSAT](https://docs.lightning.engineering/the-lightning-network/l402) (a protocol standard for authentication and paid APIs) and provides handler functions to accept microtransactions before serving ad-free content or any paid APIs.

The middleware:-

1. Checks the preference of the user whether they need paid content or free content.
2. Verify the LSAT before serving paid content.
3. Send macaroon and invoice if the user prefers paid content and fails to present a valid LSAT.
2. Verify the L402 before serving paid content.
3. Send macaroon and invoice if the user prefers paid content and fails to present a valid L402.

<img src="https://user-images.githubusercontent.com/44242169/186736015-f956dfe1-cba0-4dc3-9755-9d22cb1c7e77.jpg" width="700">

Expand Down
2 changes: 1 addition & 1 deletion caveat/caveat.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func EncodeCaveat(caveat Caveat) string {
func DecodeCaveat(caveatString string) (Caveat, error) {
splitted := strings.Split(caveatString, "=")
if len(splitted) != 2 {
return Caveat{}, fmt.Errorf("LSAT does not have the right format: %s", caveatString)
return Caveat{}, fmt.Errorf("L402 does not have the right format: %s", caveatString)
}
return Caveat{Condition: splitted[0], Value: splitted[1]}, nil
}
Expand Down
16 changes: 8 additions & 8 deletions echolsat/echolsat.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func (lsatmiddleware *EchoLsat) Handler(next echo.HandlerFunc) echo.HandlerFunc
return nil
}
// Set LSAT type Free if client does not support LSAT
c.Set("LSAT", &lsat.LsatInfo{
c.Set(lsat.LSAT_HEADER, &lsat.LsatInfo{
Type: lsat.LSAT_TYPE_FREE,
})
return next(c)
Expand All @@ -49,7 +49,7 @@ func (lsatmiddleware *EchoLsat) Handler(next echo.HandlerFunc) echo.HandlerFunc
err = lsat.VerifyLSAT(mac, caveats, lsatmiddleware.Middleware.RootKey, preimage)
if err != nil {
//not a valid LSAT
c.Set("LSAT", &lsat.LsatInfo{
c.Set(lsat.LSAT_HEADER, &lsat.LsatInfo{
Type: lsat.LSAT_TYPE_ERROR,
Error: err,
})
Expand All @@ -58,13 +58,13 @@ func (lsatmiddleware *EchoLsat) Handler(next echo.HandlerFunc) echo.HandlerFunc
//LSAT verification ok, mark client as having paid
macaroonId, err := macaroonutils.GetMacIdFromMacaroon(mac)
if err != nil {
c.Set("LSAT", &lsat.LsatInfo{
c.Set(lsat.LSAT_HEADER, &lsat.LsatInfo{
Type: lsat.LSAT_TYPE_ERROR,
Error: err,
})
return next(c)
}
c.Set("LSAT", &lsat.LsatInfo{
c.Set(lsat.LSAT_HEADER, &lsat.LsatInfo{
Type: lsat.LSAT_TYPE_PAID,
Preimage: preimage,
PaymentHash: macaroonId.PaymentHash,
Expand All @@ -78,28 +78,28 @@ func (lsatmiddleware *EchoLsat) SetLSATHeader(c echo.Context, caveats []caveat.C
ctx := context.Background()
lnInvoice := lnrpc.Invoice{
Value: lsatmiddleware.Middleware.AmountFunc(c.Echo().AcquireContext().Request()),
Memo: "LSAT",
Memo: lsat.LSAT_HEADER,
}
LNClientConn := &ln.LNClientConn{
LNClient: lsatmiddleware.Middleware.LNClient,
}
invoice, paymentHash, err := LNClientConn.GenerateInvoice(ctx, lnInvoice, c.Echo().AcquireContext().Request())
if err != nil {
c.Set("LSAT", &lsat.LsatInfo{
c.Set(lsat.LSAT_HEADER, &lsat.LsatInfo{
Type: lsat.LSAT_TYPE_ERROR,
Error: err,
})
return
}
macaroonString, err := macaroonutils.GetMacaroonAsString(paymentHash, caveats, lsatmiddleware.Middleware.RootKey)
if err != nil {
c.Set("LSAT", &lsat.LsatInfo{
c.Set(lsat.LSAT_HEADER, &lsat.LsatInfo{
Type: lsat.LSAT_TYPE_ERROR,
Error: err,
})
return
}
c.Response().Header().Set("WWW-Authenticate", fmt.Sprintf("LSAT macaroon=%s, invoice=%s", macaroonString, invoice))
c.Response().Header().Set("WWW-Authenticate", fmt.Sprintf("%s macaroon=%s, invoice=%s", lsat.LSAT_HEADER, macaroonString, invoice))
c.JSON(http.StatusPaymentRequired, map[string]interface{}{
"code": http.StatusPaymentRequired,
"message": lsat.PAYMENT_REQUIRED_MESSAGE,
Expand Down
18 changes: 9 additions & 9 deletions echolsat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func echoLsatHandler(lsatmiddleware *echolsat.EchoLsat) *echo.Echo {
router.Use(lsatmiddleware.Handler)

router.GET("/protected", func(c echo.Context) error {
lsatInfo := c.Get("LSAT").(*lsat.LsatInfo)
lsatInfo := c.Get("L402").(*lsat.LsatInfo)
if lsatInfo.Type == lsat.LSAT_TYPE_FREE {
return c.JSON(http.StatusAccepted, map[string]interface{}{
"code": http.StatusAccepted,
Expand Down Expand Up @@ -116,13 +116,13 @@ func TestEchoLsatWithLNURLConfig(t *testing.T) {
assert.Equal(t, lsat.PAYMENT_REQUIRED_MESSAGE, message)
assert.Equal(t, http.StatusPaymentRequired, res.Code)

assert.True(t, strings.HasPrefix(res.HeaderMap.Get("Www-Authenticate"), "LSAT macaroon="))
assert.True(t, strings.HasPrefix(res.HeaderMap.Get("Www-Authenticate"), "L402 macaroon="))
assert.True(t, strings.Contains(res.HeaderMap.Get("Www-Authenticate"), "invoice="))
})

router.GET("/protected").
SetHeader(gofight.H{
"Authorization": fmt.Sprintf("LSAT %s:%s", TEST_MACAROON_VALID, TEST_PREIMAGE_VALID),
"Authorization": fmt.Sprintf("L402 %s:%s", TEST_MACAROON_VALID, TEST_PREIMAGE_VALID),
}).
Run(handler, func(res gofight.HTTPResponse, req gofight.HTTPRequest) {
message := fmt.Sprint(gjson.Get(res.Body.String(), "message"))
Expand All @@ -133,7 +133,7 @@ func TestEchoLsatWithLNURLConfig(t *testing.T) {

router.GET("/protected").
SetHeader(gofight.H{
"Authorization": fmt.Sprintf("LSAT %s:%s", TEST_MACAROON_VALID, TEST_PREIMAGE_INVALID),
"Authorization": fmt.Sprintf("L402 %s:%s", TEST_MACAROON_VALID, TEST_PREIMAGE_INVALID),
}).
Run(handler, func(res gofight.HTTPResponse, req gofight.HTTPRequest) {
message := fmt.Sprint(gjson.Get(res.Body.String(), "message"))
Expand All @@ -148,7 +148,7 @@ func TestEchoLsatWithLNURLConfig(t *testing.T) {

router.GET("/protected").
SetHeader(gofight.H{
"Authorization": fmt.Sprintf("LSAT %s:%s", TEST_MACAROON_WITHOUT_CAVEATS, TEST_MACAROON_WITHOUT_CAVEATS_PREIMAGE),
"Authorization": fmt.Sprintf("L402 %s:%s", TEST_MACAROON_WITHOUT_CAVEATS, TEST_MACAROON_WITHOUT_CAVEATS_PREIMAGE),
}).
Run(handler, func(res gofight.HTTPResponse, req gofight.HTTPRequest) {
message := fmt.Sprint(gjson.Get(res.Body.String(), "message"))
Expand Down Expand Up @@ -215,13 +215,13 @@ func TestEchoLsatWithLNDConfig(t *testing.T) {
assert.Equal(t, lsat.PAYMENT_REQUIRED_MESSAGE, message)
assert.Equal(t, http.StatusPaymentRequired, res.Code)

assert.True(t, strings.HasPrefix(res.HeaderMap.Get("Www-Authenticate"), "LSAT macaroon="))
assert.True(t, strings.HasPrefix(res.HeaderMap.Get("Www-Authenticate"), "L402 macaroon="))
assert.True(t, strings.Contains(res.HeaderMap.Get("Www-Authenticate"), "invoice="))
})

router.GET("/protected").
SetHeader(gofight.H{
"Authorization": fmt.Sprintf("LSAT %s:%s", TEST_MACAROON_VALID, TEST_PREIMAGE_VALID),
"Authorization": fmt.Sprintf("L402 %s:%s", TEST_MACAROON_VALID, TEST_PREIMAGE_VALID),
}).
Run(handler, func(res gofight.HTTPResponse, req gofight.HTTPRequest) {
message := fmt.Sprint(gjson.Get(res.Body.String(), "message"))
Expand All @@ -232,7 +232,7 @@ func TestEchoLsatWithLNDConfig(t *testing.T) {

router.GET("/protected").
SetHeader(gofight.H{
"Authorization": fmt.Sprintf("LSAT %s:%s", TEST_MACAROON_VALID, TEST_PREIMAGE_INVALID),
"Authorization": fmt.Sprintf("L402 %s:%s", TEST_MACAROON_VALID, TEST_PREIMAGE_INVALID),
}).
Run(handler, func(res gofight.HTTPResponse, req gofight.HTTPRequest) {
message := fmt.Sprint(gjson.Get(res.Body.String(), "message"))
Expand All @@ -247,7 +247,7 @@ func TestEchoLsatWithLNDConfig(t *testing.T) {

router.GET("/protected").
SetHeader(gofight.H{
"Authorization": fmt.Sprintf("LSAT %s:%s", TEST_MACAROON_WITHOUT_CAVEATS, TEST_MACAROON_WITHOUT_CAVEATS_PREIMAGE),
"Authorization": fmt.Sprintf("L402 %s:%s", TEST_MACAROON_WITHOUT_CAVEATS, TEST_MACAROON_WITHOUT_CAVEATS_PREIMAGE),
}).
Run(handler, func(res gofight.HTTPResponse, req gofight.HTTPRequest) {
message := fmt.Sprint(gjson.Get(res.Body.String(), "message"))
Expand Down
37 changes: 26 additions & 11 deletions examples/echolsat/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,32 @@ func main() {
if err != nil {
log.Fatal("Failed to load .env file")
}
lnClientConfig := &ln.LNClientConfig{
LNClientType: os.Getenv("LN_CLIENT_TYPE"),
LNDConfig: ln.LNDoptions{
Address: os.Getenv("LND_ADDRESS"),
MacaroonHex: os.Getenv("MACAROON_HEX"),
},
LNURLConfig: ln.LNURLoptions{
Address: os.Getenv("LNURL_ADDRESS"),
},
RootKey: []byte(os.Getenv("ROOT_KEY")),

var lnClientConfig *ln.LNClientConfig
clientType := os.Getenv("LN_CLIENT_TYPE")

switch clientType {
case "LND":
lnClientConfig = &ln.LNClientConfig{
LNClientType: clientType,
LNDConfig: ln.LNDoptions{
Address: os.Getenv("LND_ADDRESS"),
MacaroonHex: os.Getenv("MACAROON_HEX"),
},
RootKey: []byte(os.Getenv("ROOT_KEY")),
}
case "LNURL":
lnClientConfig = &ln.LNClientConfig{
LNClientType: clientType,
LNURLConfig: ln.LNURLoptions{
Address: os.Getenv("LNURL_ADDRESS"),
},
RootKey: []byte(os.Getenv("ROOT_KEY")),
}
default:
log.Fatalf("Invalid LN_CLIENT_TYPE: %s. Must be either 'LND' or 'LNURL'.", clientType)
}

fr := &FiatRateConfig{
Currency: "USD",
Amount: 0.01,
Expand All @@ -88,7 +103,7 @@ func main() {
router.Use(echolsatmiddleware.Handler)

router.GET("/protected", func(c echo.Context) error {
lsatInfo := c.Get("LSAT").(*lsat.LsatInfo)
lsatInfo := c.Get(lsat.LSAT_HEADER).(*lsat.LsatInfo)
if lsatInfo.Type == lsat.LSAT_TYPE_FREE {
return c.JSON(http.StatusAccepted, map[string]interface{}{
"code": http.StatusAccepted,
Expand Down
39 changes: 27 additions & 12 deletions examples/ginlsat/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,32 @@ func main() {
if err != nil {
log.Fatal("Failed to load .env file")
}
lnClientConfig := &ln.LNClientConfig{
LNClientType: os.Getenv("LN_CLIENT_TYPE"),
LNDConfig: ln.LNDoptions{
Address: os.Getenv("LND_ADDRESS"),
MacaroonHex: os.Getenv("MACAROON_HEX"),
},
LNURLConfig: ln.LNURLoptions{
Address: os.Getenv("LNURL_ADDRESS"),
},
RootKey: []byte(os.Getenv("ROOT_KEY")),

var lnClientConfig *ln.LNClientConfig
clientType := os.Getenv("LN_CLIENT_TYPE")

switch clientType {
case "LND":
lnClientConfig = &ln.LNClientConfig{
LNClientType: clientType,
LNDConfig: ln.LNDoptions{
Address: os.Getenv("LND_ADDRESS"),
MacaroonHex: os.Getenv("MACAROON_HEX"),
},
RootKey: []byte(os.Getenv("ROOT_KEY")),
}
case "LNURL":
lnClientConfig = &ln.LNClientConfig{
LNClientType: clientType,
LNURLConfig: ln.LNURLoptions{
Address: os.Getenv("LNURL_ADDRESS"),
},
RootKey: []byte(os.Getenv("ROOT_KEY")),
}
default:
log.Fatalf("Invalid LN_CLIENT_TYPE: %s. Must be either 'LND' or 'LNURL'.", clientType)
}

fr := &FiatRateConfig{
Currency: "USD",
Amount: 0.01,
Expand All @@ -89,7 +104,7 @@ func main() {
router.Use(ginlsatmiddleware.Handler)

router.GET("/protected", func(c *gin.Context) {
lsatInfo := c.Value("LSAT").(*lsat.LsatInfo)
lsatInfo := c.Value(lsat.LSAT_HEADER).(*lsat.LsatInfo)
if lsatInfo.Type == lsat.LSAT_TYPE_FREE {
c.JSON(http.StatusAccepted, gin.H{
"code": http.StatusAccepted,
Expand All @@ -108,7 +123,7 @@ func main() {
}
})
router.GET("/protected/2", func(c *gin.Context) {
lsatInfo := c.Value("LSAT").(*lsat.LsatInfo)
lsatInfo := c.Value(lsat.LSAT_HEADER).(*lsat.LsatInfo)
if lsatInfo.Type == lsat.LSAT_TYPE_FREE {
c.JSON(http.StatusAccepted, gin.H{
"code": http.StatusAccepted,
Expand Down
16 changes: 8 additions & 8 deletions ginlsat/ginlsat.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (lsatmiddleware *GinLsat) Handler(c *gin.Context) {
return
}
// Set LSAT type Free if client does not support LSAT
c.Set("LSAT", &lsat.LsatInfo{
c.Set(lsat.LSAT_HEADER, &lsat.LsatInfo{
Type: lsat.LSAT_TYPE_FREE,
})
return
Expand All @@ -46,7 +46,7 @@ func (lsatmiddleware *GinLsat) Handler(c *gin.Context) {
err = lsat.VerifyLSAT(mac, caveats, lsatmiddleware.Middleware.RootKey, preimage)
if err != nil {
//not a valid LSAT
c.Set("LSAT", &lsat.LsatInfo{
c.Set(lsat.LSAT_HEADER, &lsat.LsatInfo{
Type: lsat.LSAT_TYPE_ERROR,
Error: err,
})
Expand All @@ -55,13 +55,13 @@ func (lsatmiddleware *GinLsat) Handler(c *gin.Context) {
//LSAT verification ok, mark client as having paid
macaroonId, err := macaroonutils.GetMacIdFromMacaroon(mac)
if err != nil {
c.Set("LSAT", &lsat.LsatInfo{
c.Set(lsat.LSAT_HEADER, &lsat.LsatInfo{
Type: lsat.LSAT_TYPE_ERROR,
Error: err,
})
return
}
c.Set("LSAT", &lsat.LsatInfo{
c.Set(lsat.LSAT_HEADER, &lsat.LsatInfo{
Type: lsat.LSAT_TYPE_PAID,
Preimage: preimage,
PaymentHash: macaroonId.PaymentHash,
Expand All @@ -74,28 +74,28 @@ func (lsatmiddleware *GinLsat) SetLSATHeader(c *gin.Context, caveats []caveat.Ca
ctx := context.Background()
lnInvoice := lnrpc.Invoice{
Value: lsatmiddleware.Middleware.AmountFunc(c.Request),
Memo: "LSAT",
Memo: lsat.LSAT_HEADER,
}
LNClientConn := &ln.LNClientConn{
LNClient: lsatmiddleware.Middleware.LNClient,
}
invoice, paymentHash, err := LNClientConn.GenerateInvoice(ctx, lnInvoice, c.Request)
if err != nil {
c.Set("LSAT", &lsat.LsatInfo{
c.Set(lsat.LSAT_HEADER, &lsat.LsatInfo{
Type: lsat.LSAT_TYPE_ERROR,
Error: err,
})
return
}
macaroonString, err := macaroonutils.GetMacaroonAsString(paymentHash, caveats, lsatmiddleware.Middleware.RootKey)
if err != nil {
c.Set("LSAT", &lsat.LsatInfo{
c.Set(lsat.LSAT_HEADER, &lsat.LsatInfo{
Type: lsat.LSAT_TYPE_ERROR,
Error: err,
})
return
}
c.Writer.Header().Set("WWW-Authenticate", fmt.Sprintf("LSAT macaroon=%s, invoice=%s", macaroonString, invoice))
c.Writer.Header().Set("WWW-Authenticate", fmt.Sprintf("%s macaroon=%s, invoice=%s", lsat.LSAT_HEADER, macaroonString, invoice))
c.AbortWithStatusJSON(http.StatusPaymentRequired, gin.H{
"code": http.StatusPaymentRequired,
"message": lsat.PAYMENT_REQUIRED_MESSAGE,
Expand Down
Loading
Loading