From ebddc4db48589094ad3ae8bd15b1f299934ca99e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=E2=80=99Aquino?= Date: Mon, 30 Sep 2024 13:16:22 -0700 Subject: [PATCH 1/4] Add checkout OTP endpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add OTP authentication endpoints for the lightning checkout process, to allow users without Damus iOS to buy Purple Changelog-Added: Add OTP authentication support to the Purple LN checkout process Signed-off-by: Daniel D’Aquino --- src/router_config.js | 82 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/src/router_config.js b/src/router_config.js index bcb7f89..22c531c 100644 --- a/src/router_config.js +++ b/src/router_config.js @@ -305,7 +305,87 @@ function config_router(app) { } }) - // MARK: OTP routes + // MARK: OTP lightning checkout routes + + /** + * This route is used to request an OTP code for a specific checkout. + * + * @param {string} req.params.checkout_id - The ID of the checkout object. + * @param {string} req.params.pubkey - The public key of the user verifying the OTP. + * + * @returns {Object} - A JSON response indicating whether the OTP code was sent successfully + */ + router.post('/ln-checkout/:checkout_id/request-otp/:pubkey', async (req, res) => { + const pubkey = req.params.pubkey + if (!pubkey) { + invalid_request(res, 'Could not parse account pubkey') + return + } + + const checkout_id = req.params.checkout_id + if (!checkout_id) { + error_response(res, 'Could not parse checkout_id') + return + } + + const otp_code = await app.web_auth_manager.generate_otp(pubkey) + await app.web_auth_manager.send_otp(pubkey, otp_code) + json_response(res, { success: true }) + }); + + /** + * This route is used to verify an OTP code for a specific checkout. + * + * @param {string} req.params.checkout_id - The ID of the checkout object. + * @param {string} req.params.pubkey - The public key of the user verifying the OTP. + * @param {string} req.body.otp_code - The OTP code to verify. + * + * @returns {Object} - A JSON response containing the checkout object if the OTP is valid, or an error message if invalid. + */ + router.put('/ln-checkout/:checkout_id/verify-otp/:pubkey', async (req, res) => { + const pubkey = req.params.pubkey + if (!pubkey) { + invalid_request(res, 'Could not parse account pubkey') + return + } + + const checkout_id = req.params.checkout_id + if (!checkout_id) { + error_response(res, 'Could not parse checkout_id') + return + } + + const otp_code = req.body.otp_code + if (!otp_code) { + invalid_request(res, 'Missing otp_code') + return + } + + const is_valid = await app.web_auth_manager.validate_otp(pubkey, otp_code) + if(!is_valid) { + unauthorized_response(res, { valid: false }) + return + } + + try { + const response = await app.invoice_manager.verify_checkout_object(checkout_id, pubkey) + if (response.request_error) { + invalid_request(res, response.request_error) + } + if (response.checkout_object) { + const session_token = await app.web_auth_manager.create_session(pubkey) + json_response(res, { + checkout_object: response.checkout_object, + otp: { valid: true, session_token: session_token } + }) + } + } catch (e) { + error("%s", e.toString()) + invalid_request(res, e.toString()) + } + }) + + // MARK: OTP login routes router.post('/accounts/:pubkey/request-otp', async (req, res) => { const pubkey = req.params.pubkey From c50d18cb6d5c2d08000c42e685939a093f8c3451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=E2=80=99Aquino?= Date: Wed, 2 Oct 2024 10:34:58 -0700 Subject: [PATCH 2/4] Add CLI debugging option to npm scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel D’Aquino --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 07dd73e..d528c3f 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "start": "node src/index.js", "dev": "TRANSLATION_PROVIDER=mock ENABLE_HTTP_AUTH=\"true\" node src/index.js", "dev-debug": "TRANSLATION_PROVIDER=mock ENABLE_HTTP_AUTH=\"true\" node --inspect src/index.js", + "dev-debug-cli": "TRANSLATION_PROVIDER=mock ENABLE_HTTP_AUTH=\"true\" node inspect src/index.js", "type-check": "tsc --checkJs --allowJs src/*.js --noEmit --skipLibCheck", "type-check-path": "tsc --checkJs --allowJs --noEmit --skipLibCheck" }, From c5be7d3f9845435b788b431060bec9e329330e5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=E2=80=99Aquino?= Date: Sat, 5 Oct 2024 14:19:34 -0700 Subject: [PATCH 3/4] Add more descriptive errors to web auth middleware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel D’Aquino --- src/web_auth.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/web_auth.js b/src/web_auth.js index 8ab6661..552c554 100644 --- a/src/web_auth.js +++ b/src/web_auth.js @@ -137,31 +137,31 @@ class WebAuthManager { async require_web_auth(req, res, next) { const auth_header = req.header('Authorization'); if (!auth_header) { - unauthorized_response(res, 'Unauthorized'); + unauthorized_response(res, 'Unauthorized, no auth header'); return; } const [auth_type, token] = auth_header.split(' '); if (auth_type !== 'Bearer') { - unauthorized_response(res, 'Unauthorized'); + unauthorized_response(res, 'Unauthorized, invalid auth type'); return; } if (!token) { - unauthorized_response(res, 'Unauthorized'); + unauthorized_response(res, 'Unauthorized, no token'); return; } const session_data = await this.dbs.sessions.get(token); if (!session_data) { - unauthorized_response(res, 'Unauthorized'); + unauthorized_response(res, 'Unauthorized, invalid token'); return; } // Check if the session has expired if (current_time() - session_data.created_at > this.session_expiry) { await this.dbs.sessions.del(token); - unauthorized_response(res, 'Unauthorized'); + unauthorized_response(res, 'Unauthorized, session expired'); return; } From 8bdbd179536da01c3a74b1c8cb5cd7427437c672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=E2=80=99Aquino?= Date: Tue, 8 Oct 2024 13:36:06 -0700 Subject: [PATCH 4/4] Add troubleshooting tip regarding the use of Node.js v22.x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel D’Aquino --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 84b1eb2..6a7e430 100644 --- a/README.md +++ b/README.md @@ -74,4 +74,8 @@ curl -X PUT http:///admin/users//account-uuid -d '{"admin_password": "", "account_uuid": ""}' ``` +### Failures around Lightning Network payments +Ensure you are running Node.js v18.x. Preferably, use the provided nix-shell environment to ensure you are using the correct version of Node.js. + +There is a known issue with Node.js v22.x where the `ln.connect_and_init` call does not work with the way things are setup.