@cocalc/server
Version:
CoCalc server functionality: functions used by either the hub and the next.js server
58 lines (52 loc) • 1.92 kB
text/typescript
/*
"Redeem" an email verification token. This checks everything is valid, then sets that the email
has been verified.
*/
import getPool from "@cocalc/database/pool";
import { is_valid_email_address as isValidEmailAddress } from "@cocalc/util/misc";
export default async function redeemVerifyEmail(
email_address: string,
token: string
): Promise<void> {
if (token.length < 16) {
throw Error("token is too short");
}
if (!isValidEmailAddress(email_address)) {
throw Error("email_address is not valid");
}
const pool = getPool();
const { rows } = await pool.query(
"SELECT account_id, email_address_challenge, email_address_verified FROM accounts WHERE email_address=$1",
[email_address]
);
if (rows.length == 0) {
throw Error(`no account with email address ${email_address}`);
}
const { account_id, email_address_challenge, email_address_verified } =
rows[0];
if (!email_address_challenge?.email) {
if (email_address_verified?.[email_address]) {
// nothing to do.
return;
}
throw Error(`no email verification configured for ${email_address}`);
}
if (email_address_challenge.token != token) {
throw Error("tokens do not match");
}
// we're good, save this in the email_address_verified JSONB record and also delete the challenge
await pool.query(
"UPDATE accounts SET email_address_challenge=NULL, email_address_verified=$1::JSONB WHERE account_id=$2",
[{ ...email_address_verified, [email_address]: new Date() }, account_id]
);
}
export async function isEmailVerified(email_address: string): Promise<boolean> {
const pool = getPool();
const { rows } = await pool.query(
"SELECT email_address_verified FROM accounts WHERE email_address=$1::TEXT",
[email_address]
);
if (rows.length == 0) return false;
const { email_address_verified } = rows[0];
return !!email_address_verified[email_address];
}