From bbbdf67b2881f5f65d8dcffdc9b7850592bfb821 Mon Sep 17 00:00:00 2001 From: Denis Bykhov Date: Fri, 18 Apr 2025 23:17:31 +0500 Subject: [PATCH] Fix otp admin login (#8639) * Fix otp admin login Signed-off-by: Denis Bykhov * Fix Signed-off-by: Denis Bykhov --------- Signed-off-by: Denis Bykhov --- server/account/src/__tests__/utils.test.ts | 1 + server/account/src/operations.ts | 72 ++++++----- server/account/src/utils.ts | 139 +++++++++++---------- 3 files changed, 116 insertions(+), 96 deletions(-) diff --git a/server/account/src/__tests__/utils.test.ts b/server/account/src/__tests__/utils.test.ts index a1fb371c0b..52c65c8926 100644 --- a/server/account/src/__tests__/utils.test.ts +++ b/server/account/src/__tests__/utils.test.ts @@ -1715,6 +1715,7 @@ describe('account utils', () => { describe('loginOrSignUpWithProvider', () => { const mockCtx = { + info: jest.fn(), error: jest.fn() } as unknown as MeasureContext diff --git a/server/account/src/operations.ts b/server/account/src/operations.ts index d4e92ffb02..4192d8a477 100644 --- a/server/account/src/operations.ts +++ b/server/account/src/operations.ts @@ -150,7 +150,7 @@ export async function login ( const isConfirmed = emailSocialId.verifiedOn != null - const extraToken: Record = isAdminEmail(email) ? { admin: 'true' } : {} + const extraToken: Record = isAdminEmail(normalizedEmail) ? { admin: 'true' } : {} ctx.info('Login succeeded', { email, normalizedEmail, isConfirmed, emailSocialId, ...extraToken }) return { @@ -292,48 +292,56 @@ export async function validateOtp ( // Note: can support OTP based on any other social logins later const normalizedEmail = cleanEmail(email) - const emailSocialId = await getEmailSocialId(db, normalizedEmail) + try { + const emailSocialId = await getEmailSocialId(db, normalizedEmail) - if (emailSocialId == null) { - throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, { account: email })) - } + if (emailSocialId == null) { + throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, { account: email })) + } - const isValid = await isOtpValid(db, emailSocialId._id, code) + const isValid = await isOtpValid(db, emailSocialId._id, code) - if (!isValid) { - throw new PlatformError(new Status(Severity.ERROR, platform.status.InvalidOtp, {})) - } + if (!isValid) { + throw new PlatformError(new Status(Severity.ERROR, platform.status.InvalidOtp, {})) + } - await db.otp.deleteMany({ socialId: emailSocialId._id }) + await db.otp.deleteMany({ socialId: emailSocialId._id }) - if (emailSocialId.verifiedOn == null) { - await db.socialId.updateOne({ _id: emailSocialId._id }, { verifiedOn: Date.now() }) - } + if (emailSocialId.verifiedOn == null) { + await db.socialId.updateOne({ _id: emailSocialId._id }, { verifiedOn: Date.now() }) + } - // This method handles both login and signup - const account = await db.account.findOne({ uuid: emailSocialId.personUuid as AccountUuid }) + // This method handles both login and signup + const account = await db.account.findOne({ uuid: emailSocialId.personUuid as AccountUuid }) - if (account == null) { - // This is a signup - await createAccount(db, emailSocialId.personUuid, true) + if (account == null) { + // This is a signup + await createAccount(db, emailSocialId.personUuid, true) - ctx.info('OTP signup success', emailSocialId) - } else { - await confirmHulyIds(ctx, db, account.uuid) + ctx.info('OTP signup success', emailSocialId) + } else { + await confirmHulyIds(ctx, db, account.uuid) - ctx.info('OTP login success', emailSocialId) - } + ctx.info('OTP login success', emailSocialId) + } - const person = await db.person.findOne({ uuid: emailSocialId.personUuid }) - if (person == null) { - throw new PlatformError(new Status(Severity.ERROR, platform.status.InternalServerError, {})) - } + const person = await db.person.findOne({ uuid: emailSocialId.personUuid }) + if (person == null) { + throw new PlatformError(new Status(Severity.ERROR, platform.status.InternalServerError, {})) + } - return { - account: emailSocialId.personUuid as AccountUuid, - name: getPersonName(person), - socialId: emailSocialId._id, - token: generateToken(emailSocialId.personUuid) + const extraToken: Record = isAdminEmail(normalizedEmail) ? { admin: 'true' } : {} + + return { + account: emailSocialId.personUuid as AccountUuid, + name: getPersonName(person), + socialId: emailSocialId._id, + token: generateToken(emailSocialId.personUuid, undefined, extraToken) + } + } catch (err: any) { + Analytics.handleError(err) + ctx.error('OTP login error', { email, err }) + throw err } } diff --git a/server/account/src/utils.ts b/server/account/src/utils.ts index 5e3e42ec74..af9174315a 100644 --- a/server/account/src/utils.ts +++ b/server/account/src/utils.ts @@ -61,6 +61,7 @@ import { type SocialId, type Workspace } from './types' +import { isAdminEmail } from './admin' export const GUEST_ACCOUNT = 'b6996120-416f-49cd-841e-e4a5d2e49c9b' @@ -1075,80 +1076,90 @@ export async function loginOrSignUpWithProvider ( socialId: SocialKey, signUpDisabled = false ): Promise { - const normalizedEmail = cleanEmail(email) + try { + const normalizedEmail = cleanEmail(email) - // Find if any of the target/email social ids exist - const targetSocialId = await db.socialId.findOne(socialId) - const emailSocialId = - normalizedEmail !== '' ? await db.socialId.findOne({ type: SocialIdType.EMAIL, value: normalizedEmail }) : undefined - let personUuid = targetSocialId?.personUuid ?? emailSocialId?.personUuid + // Find if any of the target/email social ids exist + const targetSocialId = await db.socialId.findOne(socialId) + const emailSocialId = + normalizedEmail !== '' + ? await db.socialId.findOne({ type: SocialIdType.EMAIL, value: normalizedEmail }) + : undefined + let personUuid = targetSocialId?.personUuid ?? emailSocialId?.personUuid - if (personUuid == null) { - if (signUpDisabled) { - return null + if (personUuid == null) { + if (signUpDisabled) { + return null + } + + personUuid = await db.person.insertOne({ firstName: first, lastName: last }) } - personUuid = await db.person.insertOne({ firstName: first, lastName: last }) - } - - if (personUuid == null) { - throw new PlatformError(new Status(Severity.ERROR, platform.status.InternalServerError, {})) - } - - const person = await db.person.findOne({ uuid: personUuid }) - - if (person == null) { - throw new PlatformError(new Status(Severity.ERROR, platform.status.InternalServerError, {})) - } - - const account = await db.account.findOne({ uuid: personUuid as AccountUuid }) - - if (account == null) { - if (signUpDisabled) { - return null + if (personUuid == null) { + throw new PlatformError(new Status(Severity.ERROR, platform.status.InternalServerError, {})) } - await createAccount(db, personUuid, true) - await db.person.updateOne({ uuid: personUuid }, { firstName: first, lastName: last }) - } + const person = await db.person.findOne({ uuid: personUuid }) - // We should check and reset password if there's an account with password but no social ids have been - // confirmed yet - const confirmedSocialId = await db.socialId.findOne({ personUuid, verifiedOn: { $gt: 0 } }) - - if (confirmedSocialId == null) { - await db.resetPassword(personUuid as AccountUuid) - } - - let socialIdId: PersonId | undefined - // Create and/or confirm missing social ids - if (targetSocialId == null) { - socialIdId = await db.socialId.insertOne({ ...socialId, personUuid, verifiedOn: Date.now() }) - } else if (targetSocialId.verifiedOn == null) { - await db.socialId.updateOne({ key: targetSocialId.key }, { verifiedOn: Date.now() }) - socialIdId = targetSocialId._id - } - - if (emailSocialId == null) { - if (normalizedEmail !== '') { - await db.socialId.insertOne({ - type: SocialIdType.EMAIL, - value: normalizedEmail, - personUuid, - verifiedOn: Date.now() - }) + if (person == null) { + throw new PlatformError(new Status(Severity.ERROR, platform.status.InternalServerError, {})) } - } else if (emailSocialId.verifiedOn == null) { - await db.socialId.updateOne({ key: emailSocialId.key }, { verifiedOn: Date.now() }) - } - await confirmHulyIds(ctx, db, personUuid as AccountUuid) + const account = await db.account.findOne({ uuid: personUuid as AccountUuid }) - return { - account: personUuid as AccountUuid, - socialId: socialIdId, - name: getPersonName(person), - token: generateToken(personUuid) + if (account == null) { + if (signUpDisabled) { + return null + } + + await createAccount(db, personUuid, true) + await db.person.updateOne({ uuid: personUuid }, { firstName: first, lastName: last }) + } + + // We should check and reset password if there's an account with password but no social ids have been + // confirmed yet + const confirmedSocialId = await db.socialId.findOne({ personUuid, verifiedOn: { $gt: 0 } }) + + if (confirmedSocialId == null) { + await db.resetPassword(personUuid as AccountUuid) + } + + let socialIdId: PersonId | undefined + // Create and/or confirm missing social ids + if (targetSocialId == null) { + socialIdId = await db.socialId.insertOne({ ...socialId, personUuid, verifiedOn: Date.now() }) + } else if (targetSocialId.verifiedOn == null) { + await db.socialId.updateOne({ key: targetSocialId.key }, { verifiedOn: Date.now() }) + socialIdId = targetSocialId._id + } + + if (emailSocialId == null) { + if (normalizedEmail !== '') { + await db.socialId.insertOne({ + type: SocialIdType.EMAIL, + value: normalizedEmail, + personUuid, + verifiedOn: Date.now() + }) + } + } else if (emailSocialId.verifiedOn == null) { + await db.socialId.updateOne({ key: emailSocialId.key }, { verifiedOn: Date.now() }) + } + + await confirmHulyIds(ctx, db, personUuid as AccountUuid) + const extraToken: Record = isAdminEmail(normalizedEmail) ? { admin: 'true' } : {} + ctx.info('Provider login succeeded', { email, normalizedEmail, emailSocialId, ...extraToken }) + + return { + account: personUuid as AccountUuid, + socialId: socialIdId, + name: getPersonName(person), + token: generateToken(personUuid, undefined, extraToken) + } + } catch (err: any) { + Analytics.handleError(err) + ctx.error('Provider login failed', { email, err }) + throw err } }