diff --git a/apps/web/pages/api/auth/changepw.ts b/apps/web/pages/api/auth/changepw.ts index 5202221250..da9b7af6c1 100644 --- a/apps/web/pages/api/auth/changepw.ts +++ b/apps/web/pages/api/auth/changepw.ts @@ -38,7 +38,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const oldPassword = req.body.oldPassword; const newPassword = req.body.newPassword; - const currentPassword = user.password?.hash; + const currentPassword = user.password; if (!currentPassword) { return res.status(400).json({ error: ErrorCode.UserMissingPassword }); } @@ -53,12 +53,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) } const hashedPassword = await hashPassword(newPassword); - await prisma.userPassword.update({ + await prisma.user.update({ where: { - userId: user.id, + id: user.id, }, data: { - hash: hashedPassword, + password: hashedPassword, }, }); diff --git a/apps/web/pages/api/auth/reset-password.ts b/apps/web/pages/api/auth/reset-password.ts index f510c96b23..e2d1109c68 100644 --- a/apps/web/pages/api/auth/reset-password.ts +++ b/apps/web/pages/api/auth/reset-password.ts @@ -41,11 +41,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) email: maybeRequest.email, }, data: { - password: { - update: { - hash: hashedPassword, - }, - }, + password: hashedPassword, emailVerified: new Date(), }, }); diff --git a/apps/web/pages/api/auth/setup.ts b/apps/web/pages/api/auth/setup.ts index fb196cc325..f63f2bb9b4 100644 --- a/apps/web/pages/api/auth/setup.ts +++ b/apps/web/pages/api/auth/setup.ts @@ -41,7 +41,7 @@ async function handler(req: NextApiRequest) { data: { username, email: userEmail, - password: { create: { hash: hashedPassword } }, + password: hashedPassword, role: "ADMIN", name: parsedQuery.data.full_name, emailVerified: new Date(), diff --git a/apps/web/pages/api/auth/two-factor/totp/disable.ts b/apps/web/pages/api/auth/two-factor/totp/disable.ts index bcb8fbe1bb..fecb75d92f 100644 --- a/apps/web/pages/api/auth/two-factor/totp/disable.ts +++ b/apps/web/pages/api/auth/two-factor/totp/disable.ts @@ -23,13 +23,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) return res.status(500).json({ error: ErrorCode.InternalServerError }); } - const user = await prisma.user.findUnique({ where: { id: session.user.id }, include: { password: true } }); + const user = await prisma.user.findUnique({ where: { id: session.user.id } }); if (!user) { console.error(`Session references user that no longer exists.`); return res.status(401).json({ message: "Not authenticated" }); } - if (!user.password?.hash && user.identityProvider === IdentityProvider.CAL) { + if (!user.password && user.identityProvider === IdentityProvider.CAL) { return res.status(400).json({ error: ErrorCode.UserMissingPassword }); } @@ -37,8 +37,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) return res.json({ message: "Two factor disabled" }); } - if (user.password?.hash && user.identityProvider === IdentityProvider.CAL) { - const isCorrectPassword = await verifyPassword(req.body.password, user.password.hash); + if (user.password && user.identityProvider === IdentityProvider.CAL) { + const isCorrectPassword = await verifyPassword(req.body.password, user.password); if (!isCorrectPassword) { return res.status(400).json({ error: ErrorCode.IncorrectPassword }); } diff --git a/apps/web/pages/api/auth/two-factor/totp/setup.ts b/apps/web/pages/api/auth/two-factor/totp/setup.ts index 628346d2ca..a6fbed0391 100644 --- a/apps/web/pages/api/auth/two-factor/totp/setup.ts +++ b/apps/web/pages/api/auth/two-factor/totp/setup.ts @@ -25,7 +25,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) return res.status(500).json({ error: ErrorCode.InternalServerError }); } - const user = await prisma.user.findUnique({ where: { id: session.user.id }, include: { password: true } }); + const user = await prisma.user.findUnique({ where: { id: session.user.id } }); if (!user) { console.error(`Session references user that no longer exists.`); return res.status(401).json({ message: "Not authenticated" }); @@ -35,7 +35,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) return res.status(400).json({ error: ErrorCode.ThirdPartyIdentityProviderEnabled }); } - if (!user.password?.hash) { + if (!user.password) { return res.status(400).json({ error: ErrorCode.UserMissingPassword }); } @@ -48,7 +48,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) return res.status(500).json({ error: ErrorCode.InternalServerError }); } - const isCorrectPassword = await verifyPassword(req.body.password, user.password.hash); + const isCorrectPassword = await verifyPassword(req.body.password, user.password); if (!isCorrectPassword) { return res.status(400).json({ error: ErrorCode.IncorrectPassword }); } diff --git a/apps/web/playwright/auth/forgot-password.e2e.ts b/apps/web/playwright/auth/forgot-password.e2e.ts index 7b7ceb349f..79bf74ccfa 100644 --- a/apps/web/playwright/auth/forgot-password.e2e.ts +++ b/apps/web/playwright/auth/forgot-password.e2e.ts @@ -86,8 +86,7 @@ testBothFutureAndLegacyRoutes.describe("Forgot password", async () => { }); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const updatedPassword = updatedUser.password!.hash; - expect(await verifyPassword(newPassword, updatedPassword)).toBeTruthy(); + expect(await verifyPassword(newPassword, updatedUser.password!)).toBeTruthy(); // finally, make sure the same URL cannot be used to reset the password again, as it should be expired. await page.goto(`/auth/forgot-password/${id}`); diff --git a/apps/web/playwright/fixtures/users.ts b/apps/web/playwright/fixtures/users.ts index 1fa59debef..c55ab2f9e4 100644 --- a/apps/web/playwright/fixtures/users.ts +++ b/apps/web/playwright/fixtures/users.ts @@ -652,6 +652,7 @@ type SupportedTestWorkflows = PrismaType.WorkflowCreateInput; type CustomUserOptsKeys = | "username" + | "password" | "completedOnboarding" | "locale" | "name" @@ -668,7 +669,6 @@ type CustomUserOpts = Partial> & { useExactUsername?: boolean; roleInOrganization?: MembershipRole; schedule?: Schedule; - password?: string | null; }; // creates the actual user in the db. @@ -690,11 +690,7 @@ const createUser = ( username: uname, name: opts?.name, email: opts?.email ?? `${uname}@example.com`, - password: { - create: { - hash: hashPassword(uname), - }, - }, + password: hashPassword(uname), emailVerified: new Date(), completedOnboarding: opts?.completedOnboarding ?? true, timeZone: opts?.timeZone ?? TimeZoneEnum.UK, @@ -795,7 +791,7 @@ async function confirmPendingPayment(page: Page) { // login using a replay of an E2E routine. export async function login( - user: Pick & Partial> & { password?: string | null }, + user: Pick & Partial>, page: Page ) { // get locators @@ -816,7 +812,7 @@ export async function login( } export async function apiLogin( - user: Pick & Partial> & { password: string | null }, + user: Pick & Partial>, page: Page ) { const csrfToken = await page diff --git a/packages/features/auth/lib/next-auth-options.ts b/packages/features/auth/lib/next-auth-options.ts index aba2c07697..706e000b72 100644 --- a/packages/features/auth/lib/next-auth-options.ts +++ b/packages/features/auth/lib/next-auth-options.ts @@ -42,8 +42,8 @@ const ORGANIZATIONS_AUTOLINK = const usernameSlug = (username: string) => `${slugify(username)}-${randomString(6).toLowerCase()}`; -const loginWithTotp = async (email: string) => - `/auth/login?totp=${await (await import("./signJwt")).default({ email })}`; +const loginWithTotp = async (user: { email: string }) => + `/auth/login?totp=${await (await import("./signJwt")).default({ email: user.email })}`; type UserTeams = { teams: (Membership & { @@ -126,18 +126,18 @@ const providers: Provider[] = [ if (user.identityProvider !== IdentityProvider.CAL && !credentials.totpCode) { throw new Error(ErrorCode.ThirdPartyIdentityProviderEnabled); } - if (!user.password?.hash && user.identityProvider == IdentityProvider.CAL) { + if (!user.password && user.identityProvider == IdentityProvider.CAL) { throw new Error(ErrorCode.IncorrectEmailPassword); } - if (!user.password?.hash && user.identityProvider !== IdentityProvider.CAL && !credentials.totpCode) { + if (!user.password && user.identityProvider !== IdentityProvider.CAL && !credentials.totpCode) { throw new Error(ErrorCode.IncorrectEmailPassword); } - if (user.password?.hash && !credentials.totpCode) { - if (!user.password?.hash) { + if (user.password && !credentials.totpCode) { + if (!user.password) { throw new Error(ErrorCode.IncorrectEmailPassword); } - const isCorrectPassword = await verifyPassword(credentials.password, user.password.hash); + const isCorrectPassword = await verifyPassword(credentials.password, user.password); if (!isCorrectPassword) { throw new Error(ErrorCode.IncorrectEmailPassword); } @@ -717,7 +717,7 @@ export const AUTH_OPTIONS: AuthOptions = { } } if (existingUser.twoFactorEnabled && existingUser.identityProvider === idP) { - return loginWithTotp(existingUser.email); + return loginWithTotp(existingUser); } else { return true; } @@ -733,7 +733,7 @@ export const AUTH_OPTIONS: AuthOptions = { if (!userWithNewEmail) { await prisma.user.update({ where: { id: existingUser.id }, data: { email: user.email } }); if (existingUser.twoFactorEnabled) { - return loginWithTotp(existingUser.email); + return loginWithTotp(existingUser); } else { return true; } @@ -752,9 +752,6 @@ export const AUTH_OPTIONS: AuthOptions = { mode: "insensitive", }, }, - include: { - password: true, - }, }); if (existingUserWithEmail) { @@ -765,7 +762,7 @@ export const AUTH_OPTIONS: AuthOptions = { existingUserWithEmail.identityProvider !== IdentityProvider.CAL ) { if (existingUserWithEmail.twoFactorEnabled) { - return loginWithTotp(existingUserWithEmail.email); + return loginWithTotp(existingUserWithEmail); } else { return true; } @@ -773,7 +770,7 @@ export const AUTH_OPTIONS: AuthOptions = { // check if user was invited if ( - !existingUserWithEmail.password?.hash && + !existingUserWithEmail.password && !existingUserWithEmail.emailVerified && !existingUserWithEmail.username ) { @@ -795,7 +792,7 @@ export const AUTH_OPTIONS: AuthOptions = { }); if (existingUserWithEmail.twoFactorEnabled) { - return loginWithTotp(existingUserWithEmail.email); + return loginWithTotp(existingUserWithEmail); } else { return true; } @@ -810,16 +807,14 @@ export const AUTH_OPTIONS: AuthOptions = { where: { email: existingUserWithEmail.email }, // also update email to the IdP email data: { - password: { - delete: true, - }, + password: null, email: user.email, identityProvider: idP, identityProviderId: account.providerAccountId, }, }); if (existingUserWithEmail.twoFactorEnabled) { - return loginWithTotp(existingUserWithEmail.email); + return loginWithTotp(existingUserWithEmail); } else { return true; } @@ -858,7 +853,7 @@ export const AUTH_OPTIONS: AuthOptions = { await calcomAdapter.linkAccount(linkAccountNewUserData); if (account.twoFactorEnabled) { - return loginWithTotp(newUser.email); + return loginWithTotp(newUser); } else { return true; } diff --git a/packages/features/auth/signup/handlers/calcomHandler.ts b/packages/features/auth/signup/handlers/calcomHandler.ts index b3aba50ab2..b82299e3e6 100644 --- a/packages/features/auth/signup/handlers/calcomHandler.ts +++ b/packages/features/auth/signup/handlers/calcomHandler.ts @@ -138,22 +138,18 @@ async function handler(req: RequestWithUsernameStatus, res: NextApiResponse) { where: { email }, update: { username, + password: hashedPassword, emailVerified: new Date(Date.now()), identityProvider: IdentityProvider.CAL, - password: { - upsert: { - create: { hash: hashedPassword }, - update: { hash: hashedPassword }, - }, - }, }, create: { username, email, + password: hashedPassword, identityProvider: IdentityProvider.CAL, - password: { create: { hash: hashedPassword } }, }, }); + // Wrapping in a transaction as if one fails we want to rollback the whole thing to preventa any data inconsistencies const { membership } = await createOrUpdateMemberships({ teamMetadata, @@ -184,7 +180,7 @@ async function handler(req: RequestWithUsernameStatus, res: NextApiResponse) { data: { username, email, - password: { create: { hash: hashedPassword } }, + password: hashedPassword, metadata: { stripeCustomerId: customer.id, checkoutSessionId, diff --git a/packages/features/auth/signup/handlers/selfHostedHandler.ts b/packages/features/auth/signup/handlers/selfHostedHandler.ts index e98b5b52a1..26f48dc5e0 100644 --- a/packages/features/auth/signup/handlers/selfHostedHandler.ts +++ b/packages/features/auth/signup/handlers/selfHostedHandler.ts @@ -79,19 +79,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) where: { email: userEmail }, update: { username: correctedUsername, - password: { - upsert: { - create: { hash: hashedPassword }, - update: { hash: hashedPassword }, - }, - }, + password: hashedPassword, emailVerified: new Date(Date.now()), identityProvider: IdentityProvider.CAL, }, create: { username: correctedUsername, email: userEmail, - password: { create: { hash: hashedPassword } }, + password: hashedPassword, identityProvider: IdentityProvider.CAL, }, }); @@ -133,19 +128,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) where: { email: userEmail }, update: { username: correctedUsername, - password: { - upsert: { - create: { hash: hashedPassword }, - update: { hash: hashedPassword }, - }, - }, + password: hashedPassword, emailVerified: new Date(Date.now()), identityProvider: IdentityProvider.CAL, }, create: { username: correctedUsername, email: userEmail, - password: { create: { hash: hashedPassword } }, + password: hashedPassword, identityProvider: IdentityProvider.CAL, }, }); diff --git a/packages/features/ee/users/server/trpc-router.ts b/packages/features/ee/users/server/trpc-router.ts index 001c214953..ff1a9edd40 100644 --- a/packages/features/ee/users/server/trpc-router.ts +++ b/packages/features/ee/users/server/trpc-router.ts @@ -67,7 +67,9 @@ const authedAdminProcedureWithRequestedUser = authedAdminProcedure.use(async ({ return next({ ctx: { user: ctx.user, - requestedUser: user, + requestedUser: + /** Don't leak the password */ + exclude(user, ["password"]), }, }); }); @@ -82,7 +84,8 @@ export const userAdminRouter = router({ // TODO: Add search, pagination, etc. const users = await prisma.user.findMany(); return users.map((user) => ({ - ...user, + /** Don't leak the password */ + ...exclude(user, ["password"]), /** * FIXME: This should be either a prisma extension or middleware * @see https://www.prisma.io/docs/concepts/components/prisma-client/middleware diff --git a/packages/lib/test/builder.ts b/packages/lib/test/builder.ts index dd6df48196..6ac789000a 100644 --- a/packages/lib/test/builder.ts +++ b/packages/lib/test/builder.ts @@ -229,6 +229,7 @@ export const buildUser = >( invitedTo: null, locale: "en", metadata: null, + password: null, role: "USER", schedules: [], selectedCalendars: [], diff --git a/packages/lib/validateUsername.ts b/packages/lib/validateUsername.ts index 4ebbd5741a..56d0a10de9 100644 --- a/packages/lib/validateUsername.ts +++ b/packages/lib/validateUsername.ts @@ -56,7 +56,9 @@ export const validateAndGetCorrectedUsernameAndEmail = async ({ { OR: [ { emailVerified: { not: null } }, - { AND: [{ password: { isNot: null } }, { username: { not: null } }] }, + { + AND: [{ password: { not: null } }, { username: { not: null } }], + }, ], }, ], diff --git a/packages/prisma/migrations/20240209223121_adds_user_password/migration.sql b/packages/prisma/migrations/20240209223121_adds_user_password/migration.sql deleted file mode 100644 index 774998b48d..0000000000 --- a/packages/prisma/migrations/20240209223121_adds_user_password/migration.sql +++ /dev/null @@ -1,32 +0,0 @@ -/* -Warnings: - -- You are about to drop the column `password` on the `users` table. All the data in the column will be lost. - - */ --- CreateTable -CREATE TABLE - "UserPassword" ( - "hash" TEXT NOT NULL, - "userId" INTEGER NOT NULL - ); - --- CreateIndex -CREATE UNIQUE INDEX "UserPassword_userId_key" ON "UserPassword" ("userId"); - --- AddForeignKey -ALTER TABLE "UserPassword" ADD CONSTRAINT "UserPassword_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- Inserts current user password into new table -INSERT INTO - "UserPassword" ("hash", "userId") -SELECT - u."password", - u.id -FROM - users u -WHERE - u."password" IS NOT NULL; - --- TODO for next migration. --- ALTER TABLE "users" DROP COLUMN "password"; \ No newline at end of file diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index b1921a1922..7458c26ced 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -194,13 +194,6 @@ enum UserPermissionRole { ADMIN } -// It holds the password of a User, separate from the User model to avoid leaking the password hash -model UserPassword { - hash String - userId Int @unique - user User @relation(fields: [userId], references: [id], onDelete: Cascade) -} - // It holds Personal Profiles of a User plus it has email, password and other core things model User { id Int @id @default(autoincrement()) @@ -209,7 +202,7 @@ model User { /// @zod.email() email String emailVerified DateTime? - password UserPassword? + password String? bio String? avatar String? avatarUrl String? diff --git a/packages/prisma/seed-insights.ts b/packages/prisma/seed-insights.ts index 7f4ad14529..440e0e2bfc 100644 --- a/packages/prisma/seed-insights.ts +++ b/packages/prisma/seed-insights.ts @@ -78,11 +78,7 @@ async function main() { insightsAdmin = await prisma.user.create({ data: { email: "insights@example.com", - password: { - create: { - hash: await hashPassword("insightsinsightsinsights...!"), - }, - }, + password: await hashPassword("insightsinsightsinsights...!"), name: "Insights Admin", role: "ADMIN", username: "insights-admin", @@ -106,11 +102,7 @@ async function main() { insightsUser = await prisma.user.create({ data: { email: "insightsuser@example.com", - password: { - create: { - hash: await hashPassword("insightsuser"), - }, - }, + password: await hashPassword("insightsuser"), name: "Insights User", role: "USER", username: "insights-user", @@ -423,11 +415,7 @@ async function createPerformanceData() { const email = `insightsuser${timestamp}@example.com`; const insightsUser = { email, - password: { - create: { - hash: await hashPassword("insightsuser"), - }, - }, + password: await hashPassword("insightsuser"), name: `Insights User ${timestamp}`, username: `insights-user-${timestamp}`, completedOnboarding: true, diff --git a/packages/prisma/seed-utils.ts b/packages/prisma/seed-utils.ts index 4aac63a4f5..d42741dc2b 100644 --- a/packages/prisma/seed-utils.ts +++ b/packages/prisma/seed-utils.ts @@ -40,11 +40,7 @@ export async function createUserAndEventType({ }) { const userData = { ...user, - password: { - create: { - hash: await hashPassword(user.password), - }, - }, + password: await hashPassword(user.password), emailVerified: new Date(), completedOnboarding: user.completedOnboarding ?? true, locale: "en", diff --git a/packages/prisma/seed.ts b/packages/prisma/seed.ts index 1fa05fd754..6e0d6d2727 100644 --- a/packages/prisma/seed.ts +++ b/packages/prisma/seed.ts @@ -122,11 +122,7 @@ async function createOrganizationAndAddMembersAndTeams({ data: { ...member.memberData, emailVerified: new Date(), - password: { - create: { - hash: await hashPassword(member.memberData.password.create?.hash || ""), - }, - }, + password: await hashPassword(member.memberData.password), }, })), inTeams: member.inTeams, @@ -153,11 +149,7 @@ async function createOrganizationAndAddMembersAndTeams({ name: user.name, email: user.email, emailVerified: new Date(), - password: { - create: { - hash: await hashPassword(user.username), - }, - }, + password: await hashPassword(user.username), }, }); }), @@ -263,11 +255,7 @@ async function createOrganizationAndAddMembersAndTeams({ await prisma.user.create({ data: { ...nonOrgMember, - password: { - create: { - hash: await hashPassword(nonOrgMember.username), - }, - }, + password: await hashPassword(nonOrgMember.password), emailVerified: new Date(), }, }) @@ -819,11 +807,7 @@ async function main() { { memberData: { email: "owner1-acme@example.com", - password: { - create: { - hash: "owner1-acme", - }, - }, + password: "owner1-acme", username: "owner1-acme", name: "Owner 1", }, @@ -852,11 +836,7 @@ async function main() { nonOrgMembers: [ { email: "non-acme-member-1@example.com", - password: { - create: { - hash: "non-acme-member-1", - }, - }, + password: "non-acme-member-1", username: "non-acme-member-1", name: "NonAcme Member1", }, @@ -886,11 +866,7 @@ async function main() { { memberData: { email: "owner1-dunder@example.com", - password: { - create: { - hash: "owner1-dunder", - }, - }, + password: "owner1-dunder", username: "owner1-dunder", name: "Owner 1", }, @@ -919,11 +895,7 @@ async function main() { nonOrgMembers: [ { email: "non-dunder-member-1@example.com", - password: { - create: { - hash: "non-dunder-member-1", - }, - }, + password: "non-dunder-member-1", username: "non-dunder-member-1", name: "NonDunder Member1", }, diff --git a/packages/trpc/server/routers/loggedInViewer/deleteMe.handler.ts b/packages/trpc/server/routers/loggedInViewer/deleteMe.handler.ts index 5dac2356ad..723b4df4e0 100644 --- a/packages/trpc/server/routers/loggedInViewer/deleteMe.handler.ts +++ b/packages/trpc/server/routers/loggedInViewer/deleteMe.handler.ts @@ -43,11 +43,11 @@ export const deleteMeHandler = async ({ ctx, input }: DeleteMeOptions) => { throw new HttpError({ statusCode: 400, message: ErrorCode.ThirdPartyIdentityProviderEnabled }); } - if (!user.password?.hash) { + if (!user.password) { throw new HttpError({ statusCode: 400, message: ErrorCode.UserMissingPassword }); } - const isCorrectPassword = await verifyPassword(input.password, user.password.hash); + const isCorrectPassword = await verifyPassword(input.password, user.password); if (!isCorrectPassword) { throw new HttpError({ statusCode: 403, message: ErrorCode.IncorrectPassword }); } diff --git a/packages/trpc/server/routers/viewer/auth/changePassword.handler.ts b/packages/trpc/server/routers/viewer/auth/changePassword.handler.ts index 83b6cfa876..022dfe8cec 100644 --- a/packages/trpc/server/routers/viewer/auth/changePassword.handler.ts +++ b/packages/trpc/server/routers/viewer/auth/changePassword.handler.ts @@ -25,11 +25,16 @@ export const changePasswordHandler = async ({ input, ctx }: ChangePasswordOption throw new TRPCError({ code: "FORBIDDEN", message: "THIRD_PARTY_IDENTITY_PROVIDER_ENABLED" }); } - const currentPasswordQuery = await prisma.userPassword.findFirst({ - where: { userId: user.id }, + const currentPasswordQuery = await prisma.user.findFirst({ + where: { + id: user.id, + }, + select: { + password: true, + }, }); - const currentPassword = currentPasswordQuery?.hash; + const currentPassword = currentPasswordQuery?.password; if (!currentPassword) { throw new TRPCError({ code: "NOT_FOUND", message: "MISSING_PASSWORD" }); @@ -49,12 +54,12 @@ export const changePasswordHandler = async ({ input, ctx }: ChangePasswordOption } const hashedPassword = await hashPassword(newPassword); - await prisma.userPassword.update({ + await prisma.user.update({ where: { - userId: user.id, + id: user.id, }, data: { - hash: hashedPassword, + password: hashedPassword, }, }); }; diff --git a/packages/trpc/server/routers/viewer/auth/verifyPassword.handler.ts b/packages/trpc/server/routers/viewer/auth/verifyPassword.handler.ts index 08f6a7e984..e663141c33 100644 --- a/packages/trpc/server/routers/viewer/auth/verifyPassword.handler.ts +++ b/packages/trpc/server/routers/viewer/auth/verifyPassword.handler.ts @@ -14,17 +14,17 @@ type VerifyPasswordOptions = { }; export const verifyPasswordHandler = async ({ input, ctx }: VerifyPasswordOptions) => { - const userPassword = await prisma.userPassword.findUnique({ + const user = await prisma.user.findUnique({ where: { - userId: ctx.user.id, + id: ctx.user.id, }, }); - if (!userPassword?.hash) { + if (!user?.password) { throw new TRPCError({ code: "INTERNAL_SERVER_ERROR" }); } - const passwordsMatch = await verifyPassword(input.passwordInput, userPassword.hash); + const passwordsMatch = await verifyPassword(input.passwordInput, user.password); if (!passwordsMatch) { throw new TRPCError({ code: "UNAUTHORIZED" }); diff --git a/packages/trpc/server/routers/viewer/organizations/create.handler.ts b/packages/trpc/server/routers/viewer/organizations/create.handler.ts index dc0691dc23..02aa40dc65 100644 --- a/packages/trpc/server/routers/viewer/organizations/create.handler.ts +++ b/packages/trpc/server/routers/viewer/organizations/create.handler.ts @@ -119,7 +119,7 @@ export const createHandler = async ({ input, ctx }: CreateOptions) => { username: slugify(adminUsername), email: adminEmail, emailVerified: new Date(), - password: { create: { hash: hashedPassword } }, + password: hashedPassword, organizationId: organization.id, // Default schedule schedules: { diff --git a/packages/trpc/server/routers/viewer/organizations/setPassword.handler.ts b/packages/trpc/server/routers/viewer/organizations/setPassword.handler.ts index 60e9de5f84..bb762f1aa8 100644 --- a/packages/trpc/server/routers/viewer/organizations/setPassword.handler.ts +++ b/packages/trpc/server/routers/viewer/organizations/setPassword.handler.ts @@ -30,13 +30,12 @@ export const setPasswordHandler = async ({ ctx, input }: UpdateOptions) => { }); if (!user) throw new TRPCError({ code: "BAD_REQUEST", message: "User not found" }); - if (!user.password?.hash) - throw new TRPCError({ code: "BAD_REQUEST", message: "Password not set by default" }); + if (!user.password) throw new TRPCError({ code: "BAD_REQUEST", message: "Password not set by default" }); const generatedPassword = createHash("md5") .update(`${user?.email ?? ""}${process.env.CALENDSO_ENCRYPTION_KEY}`) .digest("hex"); - const isCorrectPassword = await verifyPassword(generatedPassword, user.password.hash); + const isCorrectPassword = await verifyPassword(generatedPassword, user?.password); if (!isCorrectPassword) throw new TRPCError({ @@ -45,12 +44,12 @@ export const setPasswordHandler = async ({ ctx, input }: UpdateOptions) => { }); const hashedPassword = await hashPassword(newPassword); - await prisma.userPassword.update({ + await prisma.user.update({ where: { - userId: ctx.user.id, + id: ctx.user.id, }, data: { - hash: hashedPassword, + password: hashedPassword, }, }); diff --git a/packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts b/packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts index bfe6a35453..2c8793231e 100644 --- a/packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts +++ b/packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts @@ -13,7 +13,7 @@ import { UserRepository } from "@calcom/lib/server/repository/user"; import slugify from "@calcom/lib/slugify"; import { prisma } from "@calcom/prisma"; import type { Membership, Team } from "@calcom/prisma/client"; -import { Prisma, type User as UserType, type UserPassword } from "@calcom/prisma/client"; +import { Prisma, type User as UserType } from "@calcom/prisma/client"; import type { Profile as ProfileType } from "@calcom/prisma/client"; import { MembershipRole } from "@calcom/prisma/enums"; import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; @@ -27,13 +27,12 @@ import type { InviteMemberOptions, TeamWithParent } from "./types"; const log = logger.getSubLogger({ prefix: ["inviteMember.utils"] }); export type Invitee = Pick< UserType, - "id" | "email" | "username" | "identityProvider" | "completedOnboarding" + "id" | "email" | "username" | "password" | "identityProvider" | "completedOnboarding" >; export type UserWithMembership = Invitee & { teams?: Pick[]; profiles: ProfileType[]; - password: UserPassword | null; }; export async function checkPermissions({ @@ -571,7 +570,7 @@ export const sendExistingUserTeamInviteEmails = async ({ * Here we want to redirect to a different place if onboarding has been completed or not. This prevents the flash of going to teams -> Then to onboarding - also show a different email template. * This only changes if the user is a CAL user and has not completed onboarding and has no password */ - if (!user.completedOnboarding && !user.password?.hash && user.identityProvider === "CAL") { + if (!user.completedOnboarding && !user.password && user.identityProvider === "CAL") { const token = randomBytes(32).toString("hex"); await prisma.verificationToken.create({ data: { diff --git a/packages/trpc/server/routers/viewer/teams/removeMember.handler.ts b/packages/trpc/server/routers/viewer/teams/removeMember.handler.ts index e71ded20d7..701f3cd6f7 100644 --- a/packages/trpc/server/routers/viewer/teams/removeMember.handler.ts +++ b/packages/trpc/server/routers/viewer/teams/removeMember.handler.ts @@ -73,6 +73,7 @@ export const removeMemberHandler = async ({ ctx, input }: RemoveMemberOptions) = id: true, movedToProfileId: true, email: true, + password: true, username: true, completedOnboarding: true, },