diff --git a/23.md b/23.md new file mode 100644 index 0000000..fcf9f60 --- /dev/null +++ b/23.md @@ -0,0 +1,187 @@ +LUD-23: `addressRequest` base spec. +======================================= + +`author: Christian Moss aka Mandelduck` + +--- + +## Lightning Address Sharing + +A service can request a user's Lightning address without requiring manual text input. This is particularly useful for games, apps, and services that need to pay users (e.g., game rewards, referral bonuses, content creator payouts) where typing an address would create friction. + +The service displays a QR code or triggers a URL scheme, the user's wallet prompts for permission, and upon approval sends the Lightning address to the service's callback endpoint. + +### User story: + +**Conference Gaming Example**: You're at a Bitcoin conference and want to play a kart racing game that streams sats to your wallet as you collect coins on the track. + +**Current friction**: You must use a keyboard to manually enter your Lightning address into a UI the game developer built. This is slow, error-prone, and breaks immersion. + +**With LUD-23**: You simply scan a QR code displayed on the game screen with your phone, approve the request in your wallet, and start playing immediately. The game streams sats directly to your Lightning address as you collect coins - no typing required. + +### Use cases: +- Arcade/kiosk games at conferences that pay players in real-time +- Point-of-sale systems collecting customer Lightning addresses for refunds/rewards +- Interactive installations and experiences that reward participants +- Game developers rewarding players without requiring account creation +- Apps distributing payments to users +- Services onboarding users for payouts +- Any application needing a user's Lightning address without manual input + +### Server-side generation of addressRequest URL: + +When creating an `addressRequest` handler, `LN SERVICE` must include a `k1` query parameter consisting of randomly generated 32 bytes of data. An example is `https://yourapp.com/share-address?tag=addressRequest&k1=hex(32 bytes of random data)`. + +The `k1` serves two purposes: +1. **Request matching**: Allows the service to match the callback response to the specific user/session that initiated the request +2. **Spam prevention**: Prevents random requests to the callback endpoint + +`LN SERVICE` must maintain a cache of unused `k1` values and only accept callbacks with valid `k1` values from that cache. Used `k1` values should be removed after successful address submission to prevent replay attacks. + +### Server-side choice of subdomain: + +While not as critical as with authentication (LUD-04), `LN SERVICE` should still use clear, meaningful domain names since wallets will display the requesting domain to users for approval. For example, `rewards.yourgame.com` is clearer than `api-v2.yourgame.com`. + +### Wallet to service interaction flow: + +1. `LN WALLET` scans a QR code or receives a URL scheme intent and decodes the URL which contains: + - `tag` query parameter with value `addressRequest` + - `k1` (hex encoded 32 bytes) for request matching + - optional `callback` URL (if different from the base URL) + +2. If `callback` is not present in the initial URL, `LN WALLET` makes a GET request to the URL to retrieve details: + + ```JSON + { + "tag": "addressRequest", + "callback": "https://yourapp.com/receive-address", + "k1": "hex(32 bytes of random data)", + "description": "Share your Lightning address with AwesomeGame to receive rewards" + } + ``` + + The `description` field should be a human-readable explanation of why the address is being requested. + +3. `LN WALLET` displays a confirmation dialog which must include: + - The domain name extracted from the callback URL + - The `description` text explaining the request + - A clear indication that the Lightning address will be shared + - Approve/Deny options + +4. Once approved by user, `LN WALLET` makes a GET request to the `callback` URL with the following query parameters: + + ``` + GET ?k1=&address= + ``` + +5. `LN SERVICE` responds with JSON: + + ```JSON + {"status": "OK"} + ``` + + or + + ```JSON + {"status": "ERROR", "reason": "error details..."} + ``` + +### URL scheme format (same device): + +For same-device interactions, the URL can be formatted as: + +``` +lightning:addressRequest?callback=https://yourapp.com/receive&k1=hex(32 bytes)&description=Share%20address%20with%20AwesomeGame +``` + +Or services can implement deep linking to their own app: + +``` +yourapp://receive-address?address=user@wallet.com +``` + +### Encoding for QR codes: + +For QR code display (cross-device), the URL should be bech32-encoded following LUD-01: + +``` +lnurl1dp68gurn8ghj7um9wfmxjcm99e5k7... (encodes https://yourapp.com/share?tag=addressRequest&k1=...) +``` + +When used in QR codes, `LNURL` should be uppercase. + +### Security considerations: + +1. **Public information**: Lightning addresses are public identifiers meant to be shared. No sensitive authentication material is transmitted. + +2. **User consent**: Wallets MUST require explicit user approval before sharing the address. The dialog should clearly show which service is requesting the address. + +3. **k1 validation**: Services must validate that the `k1` in the callback matches an unused `k1` from their cache to prevent spam and replay attacks. + +4. **HTTPS required**: All callback URLs must use HTTPS (or http:// for Tor v2/v3 onion addresses). + +5. **Rate limiting**: Services should implement rate limiting on the callback endpoint to prevent abuse. + +6. **No authentication**: This protocol does NOT authenticate the user - it only collects their Lightning address. If authentication is needed, use LUD-04 instead. + +### Example implementation (JavaScript): + +```javascript +// Server-side: Generate addressRequest +const crypto = require('crypto'); + +function generateAddressRequest(sessionId) { + const k1 = crypto.randomBytes(32).toString('hex'); + + // Store k1 with session mapping + sessionCache.set(k1, { sessionId, timestamp: Date.now() }); + + return { + tag: "addressRequest", + callback: `https://yourgame.com/api/receive-address`, + k1: k1, + description: "Share your Lightning address with AwesomeGame to receive in-game rewards" + }; +} + +// Server-side: Handle callback +app.get('/api/receive-address', (req, res) => { + const { k1, address } = req.query; + + // Validate k1 + const session = sessionCache.get(k1); + if (!session) { + return res.json({ status: "ERROR", reason: "Invalid or expired request" }); + } + + // Validate address format per LUD-16 (lowercase, [a-z0-9\-_.+] local part) + const [localPart, domain] = (address || '').split('@'); + if (!localPart || !domain || !/^[a-z0-9\-_.+]+$/.test(localPart)) { + return res.json({ status: "ERROR", reason: "Invalid Lightning address format" }); + } + + // Store address with user session + userSessions.updateAddress(session.sessionId, address); + + // Remove used k1 + sessionCache.delete(k1); + + res.json({ status: "OK" }); +}); +``` + +### Dependencies: + +- **LUD-01**: Base LNURL encoding/decoding for QR code support +- **LUD-16**: Understanding Lightning address format (`user@domain.com`) + +### Differences from LUD-04 (auth): + +Unlike authentication, this protocol: +- Does NOT require signature generation/verification +- Does NOT derive domain-specific keys +- Does NOT authenticate the user's identity +- Only collects a public Lightning address +- Is much simpler to implement + +If you need authentication, use LUD-04. If you just need a payment destination, use LUD-23. diff --git a/README.md b/README.md index 5d4929f..8008582 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ These are all the individual documents describing each small piece of protocol t | [19][19] | Pay link discoverable from withdraw link. | [Blixt][blixt], [CoinCorner][coincorner], [OBW][obw], [LifPay][lifpay], [LNbits][lnbits] | | [20][20] | Long payment description for pay protocol. | [Alby][alby], [BitBanana][bitbanana], [Blixt][blixt], [Clams][clams], [cliché][cliche], [Mash][mash], [OneKey][onekey], [Phoenix][phoenix], [Piggy][piggy] | | [21][21] | `verify` LNURL-pay payments | [Alby][alby], [LifPay][lifpay], [Mutiny][mutiny], [Stacker.News][stacker.news], [Zaprite][zaprite] | +| [22][22] | Request user's Lightning address | _implementation pending_ | [alby]: https://github.com/getAlby/lightning-browser-extension [bipa]: https://bipa.app @@ -227,6 +228,7 @@ Tools for developers [19]: 19.md [20]: 20.md [21]: 21.md +[22]: 22.md Dependency Tree ---------------