diff --git a/api/root.js b/api/root.js index 73c3bb5..3abe11e 100644 --- a/api/root.js +++ b/api/root.js @@ -24,6 +24,7 @@ router.get('/', (_, res) => { router.get('/login', hasQueryParams('user'), async (req, res, next) => { try { const { user, redirect, zone, product = PRODUCT_ATOM, nolink } = req.query + const normalizedEmail = user.trim().toLowerCase() const { STAGE = 'dev' } = process.env let origin = `${req.protocol}://${req.get('host')}` if (STAGE) { @@ -31,7 +32,7 @@ router.get('/login', hasQueryParams('user'), async (req, res, next) => { } // login user and send OTP email const deliveryInfo = await loginUser({ - user, + user: normalizedEmail, redirect: decodeURIComponent(redirect || `${origin}/verify`), zone: decodeURIComponent(zone || 'utc'), product: product.toLowerCase(), @@ -49,8 +50,8 @@ router.get('/login', hasQueryParams('user'), async (req, res, next) => { }) } return res.json({ - message: `Login passcode sent to ${user} through email`, - user, + message: `Login passcode sent to ${normalizedEmail} through email`, + user: normalizedEmail, }) } catch (err) { if (err instanceof APIError) { @@ -64,8 +65,9 @@ router.get('/login', hasQueryParams('user'), async (req, res, next) => { router.get('/verify', hasQueryParams('user', 'otp'), async (req, res, next) => { try { const { user: email, otp, reset_uuid, product = PRODUCT_ATOM, timeout, future_access } = req.query + const normalizedEmail = email.trim().toLowerCase() const { token, api_access, prefix, product: jwtProduct } = await verifyOTP({ - email, + email: normalizedEmail, otp, reset_uuid: ['1', 'true'].includes(reset_uuid), product: product.toLowerCase(), @@ -73,9 +75,9 @@ router.get('/verify', hasQueryParams('user', 'otp'), async (req, res, next) => { future_access: ['1', 'true'].includes(future_access), }) return res.json({ - message: `User ${email} verified, please store and use the token responsibly`, - email, - user: email, // for consistency with other routes + message: `User ${normalizedEmail} verified, please store and use the token responsibly`, + email: normalizedEmail, + user: normalizedEmail, // for consistency with other routes token, product: jwtProduct, access: { diff --git a/modules/db.js b/modules/db.js index 3f192a3..8b0f165 100644 --- a/modules/db.js +++ b/modules/db.js @@ -17,7 +17,10 @@ const _checkEmpty = ({ ...params }) => { } } +const _normalizeEmail = (email) => email.trim().toLowerCase() + const selectUser = async ({ email, selects, conditions = [] }) => { + email = _normalizeEmail(email) // returns user data, or undefined if user not found _checkEmpty({ email }) const { rows = [] } = await rPool.query(` @@ -40,6 +43,7 @@ const listUsers = async ({ selects, conditions }) => { } const insertUser = async ({ email, ...props }) => { + email = _normalizeEmail(email) _checkEmpty({ email }) const entries = Object.entries({ email, ...props }) try { @@ -60,6 +64,7 @@ const insertUser = async ({ email, ...props }) => { // resolves to 1 if update successful const updateUser = async ({ email, ...updates }) => { + email = _normalizeEmail(email) _checkEmpty({ email }) const entries = Object.entries(updates) return await wPool.transaction(async (query) => { @@ -82,21 +87,27 @@ const updateUser = async ({ email, ...updates }) => { } -const deleteUser = (email) => wPool.query(` - DELETE FROM equsers - WHERE email = $1; -`, [email]) +const deleteUser = (email) => { + email = _normalizeEmail(email) + return wPool.query(` + DELETE FROM equsers + WHERE email = $1; + `, [email]) +} -const getUserWL = (email) => rPool.query(` - SELECT - wl.logo, - wl.sender, - wl.company - FROM whitelabel AS wl - INNER JOIN equsers AS u - ON u.client->'wl'->(0) = wl.whitelabelid::text::jsonb - WHERE u.email = $1; -`, [email]) +const getUserWL = (email) => { + email = _normalizeEmail(email) + return rPool.query(` + SELECT + wl.logo, + wl.sender, + wl.company + FROM whitelabel AS wl + INNER JOIN equsers AS u + ON u.client->'wl'->(0) = wl.whitelabelid::text::jsonb + WHERE u.email = $1; + `, [email]) +} module.exports = { selectUser,