234 lines
6.3 KiB
JavaScript
234 lines
6.3 KiB
JavaScript
/**
|
||
* Email Verification and Password Reset
|
||
* Handles email verification tokens and password reset tokens
|
||
*/
|
||
|
||
import { create, findOne, deleteWhere } from '../../../core/database/crud.js';
|
||
import { generateToken, generateId } from './password.js';
|
||
import { sendAuthEmail } from '../../../core/email/index.js';
|
||
import { renderVerificationEmail, renderPasswordResetEmail, renderPasswordChangedEmail } from '../../../core/email/templates/index.js';
|
||
|
||
/**
|
||
* Create an email verification token
|
||
* @param {string} email - User email
|
||
* @returns {Promise<Object>} Verification object with token
|
||
*/
|
||
async function createEmailVerification(email) {
|
||
const token = generateToken(32);
|
||
const verificationId = generateId();
|
||
|
||
// Token expires in 24 hours
|
||
const expiresAt = new Date();
|
||
expiresAt.setHours(expiresAt.getHours() + 24);
|
||
|
||
// Delete any existing verification tokens for this email
|
||
await deleteWhere('zen_auth_verifications', {
|
||
identifier: 'email_verification',
|
||
value: email
|
||
});
|
||
|
||
const verification = await create('zen_auth_verifications', {
|
||
id: verificationId,
|
||
identifier: 'email_verification',
|
||
value: email,
|
||
token,
|
||
expires_at: expiresAt,
|
||
updated_at: new Date()
|
||
});
|
||
|
||
return {
|
||
...verification,
|
||
token
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Verify an email token
|
||
* @param {string} email - User email
|
||
* @param {string} token - Verification token
|
||
* @returns {Promise<boolean>} True if valid, false otherwise
|
||
*/
|
||
async function verifyEmailToken(email, token) {
|
||
const verification = await findOne('zen_auth_verifications', {
|
||
identifier: 'email_verification',
|
||
value: email
|
||
});
|
||
|
||
if (!verification) return false;
|
||
|
||
// Verify token matches
|
||
if (verification.token !== token) return false;
|
||
|
||
// Check if token is expired
|
||
if (new Date(verification.expires_at) < new Date()) {
|
||
await deleteWhere('zen_auth_verifications', { id: verification.id });
|
||
return false;
|
||
}
|
||
|
||
// Delete the verification token after use
|
||
await deleteWhere('zen_auth_verifications', { id: verification.id });
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Create a password reset token
|
||
* @param {string} email - User email
|
||
* @returns {Promise<Object>} Reset object with token
|
||
*/
|
||
async function createPasswordReset(email) {
|
||
const token = generateToken(32);
|
||
const resetId = generateId();
|
||
|
||
// Token expires in 1 hour
|
||
const expiresAt = new Date();
|
||
expiresAt.setHours(expiresAt.getHours() + 1);
|
||
|
||
// Delete any existing reset tokens for this email
|
||
await deleteWhere('zen_auth_verifications', {
|
||
identifier: 'password_reset',
|
||
value: email
|
||
});
|
||
|
||
const reset = await create('zen_auth_verifications', {
|
||
id: resetId,
|
||
identifier: 'password_reset',
|
||
value: email,
|
||
token,
|
||
expires_at: expiresAt,
|
||
updated_at: new Date()
|
||
});
|
||
|
||
return {
|
||
...reset,
|
||
token
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Verify a password reset token
|
||
* @param {string} email - User email
|
||
* @param {string} token - Reset token
|
||
* @returns {Promise<boolean>} True if valid, false otherwise
|
||
*/
|
||
async function verifyResetToken(email, token) {
|
||
const reset = await findOne('zen_auth_verifications', {
|
||
identifier: 'password_reset',
|
||
value: email
|
||
});
|
||
|
||
if (!reset) return false;
|
||
|
||
// Verify token matches
|
||
if (reset.token !== token) return false;
|
||
|
||
// Check if token is expired
|
||
if (new Date(reset.expires_at) < new Date()) {
|
||
await deleteWhere('zen_auth_verifications', { id: reset.id });
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Delete a password reset token
|
||
* @param {string} email - User email
|
||
* @returns {Promise<number>} Number of deleted tokens
|
||
*/
|
||
async function deleteResetToken(email) {
|
||
return await deleteWhere('zen_auth_verifications', {
|
||
identifier: 'password_reset',
|
||
value: email
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Send verification email using Resend
|
||
* @param {string} email - User email
|
||
* @param {string} token - Verification token
|
||
* @param {string} baseUrl - Base URL of the application
|
||
*/
|
||
async function sendVerificationEmail(email, token, baseUrl) {
|
||
const verificationUrl = `${baseUrl}/auth/confirm?email=${encodeURIComponent(email)}&token=${token}`;
|
||
const appName = process.env.ZEN_NAME || 'ZEN';
|
||
|
||
const html = await renderVerificationEmail(verificationUrl, email, appName);
|
||
|
||
const result = await sendAuthEmail({
|
||
to: email,
|
||
subject: `Confirmez votre adresse courriel – ${appName}`,
|
||
html
|
||
});
|
||
|
||
if (!result.success) {
|
||
console.error(`[ZEN AUTH] Failed to send verification email to ${email}:`, result.error);
|
||
throw new Error('Failed to send verification email');
|
||
}
|
||
|
||
console.log(`[ZEN AUTH] Verification email sent to ${email}`);
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* Send password reset email using Resend
|
||
* @param {string} email - User email
|
||
* @param {string} token - Reset token
|
||
* @param {string} baseUrl - Base URL of the application
|
||
*/
|
||
async function sendPasswordResetEmail(email, token, baseUrl) {
|
||
const resetUrl = `${baseUrl}/auth/reset?email=${encodeURIComponent(email)}&token=${token}`;
|
||
const appName = process.env.ZEN_NAME || 'ZEN';
|
||
|
||
const html = await renderPasswordResetEmail(resetUrl, email, appName);
|
||
|
||
const result = await sendAuthEmail({
|
||
to: email,
|
||
subject: `Réinitialisation du mot de passe – ${appName}`,
|
||
html
|
||
});
|
||
|
||
if (!result.success) {
|
||
console.error(`[ZEN AUTH] Failed to send password reset email to ${email}:`, result.error);
|
||
throw new Error('Failed to send password reset email');
|
||
}
|
||
|
||
console.log(`[ZEN AUTH] Password reset email sent to ${email}`);
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* Send password changed confirmation email using Resend
|
||
* @param {string} email - User email
|
||
*/
|
||
async function sendPasswordChangedEmail(email) {
|
||
const appName = process.env.ZEN_NAME || 'ZEN';
|
||
|
||
const html = await renderPasswordChangedEmail(email, appName);
|
||
|
||
const result = await sendAuthEmail({
|
||
to: email,
|
||
subject: `Mot de passe modifié – ${appName}`,
|
||
html
|
||
});
|
||
|
||
if (!result.success) {
|
||
console.error(`[ZEN AUTH] Failed to send password changed email to ${email}:`, result.error);
|
||
throw new Error('Failed to send password changed email');
|
||
}
|
||
|
||
console.log(`[ZEN AUTH] Password changed email sent to ${email}`);
|
||
return result;
|
||
}
|
||
|
||
export {
|
||
createEmailVerification,
|
||
verifyEmailToken,
|
||
createPasswordReset,
|
||
verifyResetToken,
|
||
deleteResetToken,
|
||
sendVerificationEmail,
|
||
sendPasswordResetEmail,
|
||
sendPasswordChangedEmail
|
||
};
|