Skip to content
Merged
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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,8 @@ curl -X PUT http://<HOST_AND_PORT>/admin/users/<PUBKEY_HEX_FORMAT>/account-uuid
-d '{"admin_password": "<ADMIN_PASSWORD_SET_ON_THE_RESPECTIVE_ENV_VARIABLE>", "account_uuid": "<UUID_FOUND_ON_IAP_TRANSACTION>"}'
```

### 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.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
82 changes: 81 additions & 1 deletion src/router_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions src/web_auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
Loading