Files
calendar/packages/features/auth/lib/verifyEmail.ts
T
0e6f44e2b9 feat: email confirmation on change (#13443)
* feat:show old and new email in confirmation dialog

* fix: update i18n

* chore: update styles

* wip: change email upon verification

* fix: email being changed w/o need to logout

* chore: tidy up - remove update sessison (WIP) will try do this serverside

* wip: try new approach of serverside call to nextjs then client update

* fix: tests

* fix: update without logout

* i18n: use i18n in toast

* fix: locale ready toast

* fix: restore email api template

* fix: revert changes from wrong branch

* fix: restore yarn.lock

* feat: happy e2e path

* fix: verification disabled e2e tests

* fix:move toast

* chore: cleanup early return

* fix: await input selector

* Update apps/web/pages/auth/verify-email-change.tsx

* fix: feedback

* fix: update the way we update session

* fix: reset password -> email verified

* fix: email change toast failure

* tests: add tests for error path

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2024-02-07 10:31:00 +00:00

132 lines
3.5 KiB
TypeScript

import { randomBytes, createHash } from "crypto";
import { totp } from "otplib";
import {
sendEmailVerificationCode,
sendEmailVerificationLink,
sendChangeOfEmailVerificationLink,
} from "@calcom/emails/email-manager";
import { getFeatureFlagMap } from "@calcom/features/flags/server/utils";
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
import { WEBAPP_URL } from "@calcom/lib/constants";
import logger from "@calcom/lib/logger";
import { getTranslation } from "@calcom/lib/server/i18n";
import { prisma } from "@calcom/prisma";
const log = logger.getSubLogger({ prefix: [`[[Auth] `] });
interface VerifyEmailType {
username?: string;
email: string;
language?: string;
}
export const sendEmailVerification = async ({ email, language, username }: VerifyEmailType) => {
const token = randomBytes(32).toString("hex");
const translation = await getTranslation(language ?? "en", "common");
const flags = await getFeatureFlagMap(prisma);
if (!flags["email-verification"]) {
log.warn("Email verification is disabled - Skipping");
return { ok: true, skipped: true };
}
await checkRateLimitAndThrowError({
rateLimitingType: "core",
identifier: email,
});
await prisma.verificationToken.create({
data: {
identifier: email,
token,
expires: new Date(Date.now() + 24 * 3600 * 1000), // +1 day
},
});
const params = new URLSearchParams({
token,
});
await sendEmailVerificationLink({
language: translation,
verificationEmailLink: `${WEBAPP_URL}/api/auth/verify-email?${params.toString()}`,
user: {
email,
name: username,
},
});
return { ok: true, skipped: false };
};
export const sendEmailVerificationByCode = async ({ email, language, username }: VerifyEmailType) => {
const translation = await getTranslation(language ?? "en", "common");
const secret = createHash("md5")
.update(email + process.env.CALENDSO_ENCRYPTION_KEY)
.digest("hex");
totp.options = { step: 900 };
const code = totp.generate(secret);
await sendEmailVerificationCode({
language: translation,
verificationEmailCode: code,
user: {
email,
name: username,
},
});
return { ok: true, skipped: false };
};
interface ChangeOfEmail {
user: {
username: string;
emailFrom: string;
emailTo: string;
};
language?: string;
}
export const sendChangeOfEmailVerification = async ({ user, language }: ChangeOfEmail) => {
const token = randomBytes(32).toString("hex");
const translation = await getTranslation(language ?? "en", "common");
const flags = await getFeatureFlagMap(prisma);
if (!flags["email-verification"]) {
log.warn("Email verification is disabled - Skipping");
return { ok: true, skipped: true };
}
await checkRateLimitAndThrowError({
rateLimitingType: "core",
identifier: user.emailFrom,
});
await prisma.verificationToken.create({
data: {
identifier: user.emailFrom, // We use from as this is the email use to get the metadata from
token,
expires: new Date(Date.now() + 24 * 3600 * 1000), // +1 day
},
});
const params = new URLSearchParams({
token,
});
await sendChangeOfEmailVerificationLink({
language: translation,
verificationEmailLink: `${WEBAPP_URL}/auth/verify-email-change?${params.toString()}`,
user: {
emailFrom: user.emailFrom,
emailTo: user.emailTo,
name: user.username,
},
});
return { ok: true, skipped: false };
};