LSAT Playground
Learn how Lightning Service Authentication Tokens (LSATs) work using the lsat-js library.

Signing Key
Signing key for generating and verifying LSATs. This will be used where required for the rest of the playground.

Generate LSAT from Invoice
Enter a valid lightning invoice in the textbox below to generate a new LSAT.

Since macaroon generation depends on the actual application, we will generate our own base-macaroon to use to generate the LSAT. This requires a signing key, so make sure one has been set above first.

Enter BOLT11 Invoice

Code Snippet

import {Identifier, Lsat} from 'lsat-js'
// can use any macaroon utility as long it follows lib-macaroon standard
import * as Macaroon from 'macaroon'

const identifier = new Identifier({
  paymentHash: Buffer.from(paymentHash, 'hex'),
})

const macaroon = Macaroon.newMacaroon({
  version: 1,
  rootKey: signingKey,
  identifier: identifier.toString(),
  location: window.location.origin,
})

const lsat = Lsat.fromMacaroon(getRawMacaroon(macaroon), payreq)
lsat.toJSON()

Generate LSAT from Challenge
A challenge is returned in a 402 Response's WWWW-Authenticate header. It includes the macaroon and invoice to be paid, encoded with base64

Enter WWW-Authenticate LSAT Challenge

Code Snippet

import {Lsat} from 'lsat-js'

// challenge must be a base64 encodedstring
const lsat = Lsat.fromChallenge(challenge)

// Lsat.fromHeader(header) accomplishes the same thing
// for a challenge with the "LSAT" prefix, which describes
// the _type_ of challenge WWW-Authenticate challenge
lsat.toJSON()

Parse LSAT Token
Simple utility to get a human readable version of an LSAT parsed from an encoded token

Enter Token

Code Snippet

import {Lsat} from 'lsat-js'

// challenge must be a base64 encodedstring
const lsat = Lsat.fromToken(token)

lsat.toJSON()

Satisfy LSAT
In order for an LSAT to be satisfied, it must have a preimage that corresponds to its paymentHash serving as its proof of payment.

Enter LSAT Token & Preimage

Code Snippet

import {Lsat} from 'lsat-js'

const lsat = Lsat.fromToken(token)
// throws if the preimage is malformed or does not match
lsat.setPreimage(secret)
lsat.toJSON()

Add Expiration
This will demonstrate how to add new first-party caveats onto an LSAT's macaroon.

Enter LSAT and Expiration to Set

Code Snippet

import {Lsat, Caveat} from 'lsat-js'

const lsat = Lsat.fromToken(token)

Validate LSAT
Run various checks against a given LSAT to determine its validity.

This tool will use the signing key set at the top of the page unless another is set below. The key used to generate the original LSAT is required for full validation. For LSAT's provided by an outside source's WWW-Authenticate header, this will not be possible since this is only known by the macaroon "baker". You can, however, still check the preimage and any expiration caveats.

Enter LSAT & Secret

Code Snippet

import {
  Lsat, 
  expirationSatisfier, 
  verifyMacaroonCaveats
} from 'lsat-js'

const lsat = Lsat.fromToken(token)

// check if is expired based on (optional) expiration caveat
lsat.isExpired() 

// checks LSAT preimage
lsat.isSatisfied()

// checks caveats are satisfied and macaroon signature is valid
verifyMacaroonCaveats(
  lsat.baseMacaroon,
  signingKey,
  // must pass all satisfiers necessary for validation
  expirationSatisfier
)

lsat.toJSON()

Live Demo
Try out LSATs in a live demo by hitting an endpoint protected by Boltwall.
Use the Lightning Joule extension (or any other WebLN enabled browser wallet) for the best LSAT payment experience.

The LSATs generated here have an expiration caveat attached, giving you timed access (with some buffer) from the time of LSAT generation (not payment). Use the data derived from the response and displayed in the right column in the playground to check validity.

⚡️⚡️ This demo is built using a TESTNET node. Please interact with it accordingly. ⚡️⚡️
seconds

LSAT Challenge
This is in the www-authenticate header that was returned by the server with a 402 response

LSAT Token
The token to be sent back in the Authorization request header. This will be sent automatically when making the request but is shown here for reference.