From 7b1fb5d9a6ce9998c9eed0c4a7f37249cd3d844c Mon Sep 17 00:00:00 2001 From: KernelDeimos Date: Wed, 6 Aug 2025 14:58:33 -0400 Subject: [PATCH] fix: confirm email with code flow It wasn't working: - erroneously expected `email` parameter in request - sqlite compatibility issue - incorrect branching, fixed with guard clause --- src/backend/src/routers/confirm-email.js | 66 ++++++++++--------- src/backend/src/routers/save_account.js | 2 +- src/backend/src/routers/send-confirm-email.js | 2 +- src/backend/src/routers/signup.js | 2 +- 4 files changed, 38 insertions(+), 34 deletions(-) diff --git a/src/backend/src/routers/confirm-email.js b/src/backend/src/routers/confirm-email.js index 465dcb0c2..360ff3a02 100644 --- a/src/backend/src/routers/confirm-email.js +++ b/src/backend/src/routers/confirm-email.js @@ -43,15 +43,22 @@ router.post('/confirm-email', auth, express.json(), async (req, res, next)=>{ const db = req.services.get('database').get(DB_WRITE, 'auth'); // Increment & check rate limit - if(kv.incr(`confirm-email|${req.ip}|${req.body.email ?? req.body.username}`) > 10) + if(kv.incr(`confirm-email|${req.ip}|${req.user.email ?? req.user.username}`) > 10) return res.status(429).send({error: 'Too many requests.'}); // Set expiry for rate limit - kv.expire(`confirm-email|${req.ip}|${req.body.email ?? req.body.username}`, 60 * 10, 'NX') + kv.expire(`confirm-email|${req.ip}|${req.user.email ?? req.user.username}`, 60 * 10, 'NX') + + console.log('need to check these', typeof req.body.code, typeof req.user.email_confirm_code, req.body.code, req.user.email_confirm_code); + + if(req.body.code !== req.user.email_confirm_code) { + res.send({ email_confirmed: false }); + return; + } // Scenario: email was confirmed on another account already { const svc_cleanEmail = req.services.get('clean-email'); - const clean_email = svc_cleanEmail.clean(req.body.email); + const clean_email = svc_cleanEmail.clean(req.user.email); if ( ! await svc_cleanEmail.validate(clean_email) ) { APIError.create('field_invalid', null, { @@ -63,7 +70,7 @@ router.post('/confirm-email', auth, express.json(), async (req, res, next)=>{ const rows = await db.read( `SELECT EXISTS( SELECT 1 FROM user WHERE (email=? OR clean_email=?) AND email_confirmed=1 AND password IS NOT NULL - ) AS email_exists`, [req.body.email, clean_email]); + ) AS email_exists`, [req.user.email, clean_email]); if ( rows[0].email_exists ) { APIError.create('email_already_in_use').write(res); return; @@ -76,37 +83,34 @@ router.post('/confirm-email', auth, express.json(), async (req, res, next)=>{ [req.user.email], ); - if(req.body.code === req.user.email_confirm_code) { - await db.write( - "UPDATE `user` SET `email_confirmed` = 1, `requires_email_confirmation` = 0 WHERE id = ? LIMIT 1", - [req.user.id], - ); - const svc_getUser = req.services.get('get-user'); - await svc_getUser.get_user({ id: req.user.id, force: true }); + // Update user record to say email is confirmed + await db.write( + "UPDATE `user` SET `email_confirmed` = 1, `requires_email_confirmation` = 0 WHERE id = ? LIMIT 1", + [req.user.id], + ); + + // Invalidate user cache + const svc_getUser = req.services.get('get-user'); + await svc_getUser.get_user({ id: req.user.id, force: true }); - const svc_event = req.services.get('event'); - svc_event.emit('user.email-confirmed', { - user_uid: req.user.uuid, - email: req.user.email, - }); - } + // Emit internal event + const svc_event = req.services.get('event'); + svc_event.emit('user.email-confirmed', { + user_uid: req.user.uuid, + email: req.user.email, + }); - // Build response object - const res_obj = { - email_confirmed: (req.body.code === req.user.email_confirm_code), - original_client_socket_id: req.body.original_client_socket_id, - } - - // Send realtime success msg to client - if(req.body.code === req.user.email_confirm_code){ - const svc_socketio = req.services.get('socketio'); - svc_socketio.send({ room: req.user.id }, 'user.email_confirmed', { - original_client_socket_id: req.body.original_client_socket_id - }); - } + // Emit websocket event (TODO: should come from internal event above) + const svc_socketio = req.services.get('socketio'); + svc_socketio.send({ room: req.user.id }, 'user.email_confirmed', { + original_client_socket_id: req.body.original_client_socket_id + }); // return results - return res.send(res_obj) + return res.send({ + email_confirmed: true, + original_client_socket_id: req.body.original_client_socket_id, + }); }) module.exports = router diff --git a/src/backend/src/routers/save_account.js b/src/backend/src/routers/save_account.js index b54af3991..f7f438a50 100644 --- a/src/backend/src/routers/save_account.js +++ b/src/backend/src/routers/save_account.js @@ -147,7 +147,7 @@ router.post('/save_account', auth, express.json(), async (req, res, next)=>{ // password await bcrypt.hash(req.body.password, 8), // email_confirm_code - email_confirm_code, + '' + email_confirm_code, //email_confirm_token email_confirm_token, // referred_by diff --git a/src/backend/src/routers/send-confirm-email.js b/src/backend/src/routers/send-confirm-email.js index 5b5e26a0f..76ccbfaf2 100644 --- a/src/backend/src/routers/send-confirm-email.js +++ b/src/backend/src/routers/send-confirm-email.js @@ -46,7 +46,7 @@ router.post('/send-confirm-email', auth, express.json(), async (req, res, next)= `UPDATE user SET email_confirm_code = ? WHERE id = ?`, [ // email_confirm_code - email_confirm_code, + '' + email_confirm_code, // id req.user.id, ]); diff --git a/src/backend/src/routers/signup.js b/src/backend/src/routers/signup.js index ce972cd80..63fdbc2ca 100644 --- a/src/backend/src/routers/signup.js +++ b/src/backend/src/routers/signup.js @@ -271,7 +271,7 @@ module.exports = eggspress(['/signup'], { // referrer req.body.referrer ?? null, // email_confirm_code - email_confirm_code, + '' + email_confirm_code, // email_confirm_token email_confirm_token, // free_storage