From d1dfa58674585faf7a8f88cfe89d4bd4da16eaf1 Mon Sep 17 00:00:00 2001 From: Adam Jones Date: Wed, 16 Oct 2024 14:13:19 +0100 Subject: [PATCH] login-account-proxy: Better handle duplicate email change requests --- .../src/pages/api/public/submit.ts | 47 ++++++++++++++----- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/apps/login-account-proxy/src/pages/api/public/submit.ts b/apps/login-account-proxy/src/pages/api/public/submit.ts index 3c0317c..206fd7e 100644 --- a/apps/login-account-proxy/src/pages/api/public/submit.ts +++ b/apps/login-account-proxy/src/pages/api/public/submit.ts @@ -1,10 +1,12 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import axios from 'axios'; +import createHttpError from 'http-errors'; import { apiRoute } from '../../../lib/api/apiRoute'; import env from '../../../lib/api/env'; +import { slackAlert } from '../../../lib/api/slackAlert'; export type SubmitRequest = { - oldEmail: string, + oldEmail?: string, newEmail: string, password: string, secret: string, @@ -43,6 +45,8 @@ export default apiRoute(async ( return; } + const isChangingEmail = data.oldEmail && data.oldEmail !== data.newEmail; + const clientCredentialsGrantParams = new URLSearchParams(); clientCredentialsGrantParams.append('grant_type', 'client_credentials'); clientCredentialsGrantParams.append('client_id', 'login-account-proxy'); @@ -56,24 +60,25 @@ export default apiRoute(async ( const potentialUsers = (await axios.get<{ id: string, email: string }[]>(`https://login.bluedot.org/admin/realms/customers/users?email=${encodeURIComponent(data.oldEmail || data.newEmail)}&exact=true`, { headers })).data; if (potentialUsers.length > 1) { - throw new Error(`Found more than one user for email: ${data.newEmail}`); + throw new Error(`Found more than one user for email: ${data.oldEmail || data.newEmail}`); } - if (potentialUsers.length === 1) { - if (data.oldEmail && data.oldEmail !== data.newEmail) { - await axios.put(`https://login.bluedot.org/admin/realms/customers/users/${potentialUsers[0]!.id}`, { - ...potentialUsers[0], - email: data.newEmail, - username: data.newEmail, - }, { headers }); + // No account exists, and we expect one to + if (isChangingEmail && potentialUsers.length === 0) { + const potentialNewUsers = (await axios.get<{ id: string, email: string }[]>(`https://login.bluedot.org/admin/realms/customers/users?email=${encodeURIComponent(data.newEmail)}&exact=true`, { headers })).data; + + if (potentialNewUsers.length === 0) { + throw new createHttpError.BadRequest(`Tried to change email, but cannot find existing accounts in Keycloak for email ${data.oldEmail} or ${data.newEmail}`); } - await axios.put(`https://login.bluedot.org/admin/realms/customers/users/${potentialUsers[0]!.id}/reset-password`, { - type: 'password', - value: data.password, - }, { headers }); + if (potentialNewUsers.length >= 1) { + slackAlert([`User tried to change email from ${data.oldEmail} to ${data.newEmail}, but only found account for new email. Ignoring this request, because this usually indicates it previously succeeded and they clicked the button in Bubble twice in quick succession.`]); + res.status(200).json({ type: 'success' }); + return; + } } + // No account exists, and we don't expect one to if (potentialUsers.length === 0) { await axios.post('https://login.bluedot.org/admin/realms/customers/users', { enabled: true, @@ -85,5 +90,21 @@ export default apiRoute(async ( }, { headers }); } + // An account exists + if (potentialUsers.length === 1) { + if (isChangingEmail) { + await axios.put(`https://login.bluedot.org/admin/realms/customers/users/${potentialUsers[0]!.id}`, { + ...potentialUsers[0], + email: data.newEmail, + username: data.newEmail, + }, { headers }); + } + + await axios.put(`https://login.bluedot.org/admin/realms/customers/users/${potentialUsers[0]!.id}/reset-password`, { + type: 'password', + value: data.password, + }, { headers }); + } + res.status(200).json({ type: 'success' }); }, 'insecure_no_auth');