UNPKG

svelte-guardian

Version:

Batteries included authentication for SvelteKit applications.

129 lines (128 loc) 4.44 kB
import { sendEmail } from '../email'; import crypto from 'crypto'; export class EmailVerificationService { options; adapter; emailProvider; constructor(options, adapter, emailProvider) { this.options = options; this.adapter = adapter; this.emailProvider = emailProvider; } generateOTP(length) { const min = 1 * (10 ** length); const max = 9 * (10 ** length); return crypto.randomInt(min, max) .toString() .substring(0, length); } async sendOTP(email) { const otp = this.generateOTP(this.options.otpLength || 6); // Store OTP in database await this.adapter.createVerificationToken({ identifier: email, token: otp, type: 'otp', expires: new Date(Date.now() + (this.options.otpExpiration || 15) * 60 * 1000) }); // Send OTP via email await sendEmail({ to: email, subject: 'Your Verification Code', type: 'otp', otp }, this.emailProvider); return otp; } async sendMagicLink(email) { const token = crypto.randomUUID(); // Store magic link token in database await this.adapter.createVerificationToken({ identifier: email, token, expires: new Date(Date.now() + 15 * 60 * 1000) // 15 minutes }); // Send magic link via email const verificationLink = `${process.env.APP_URL}/verify-email?token=${token}`; await sendEmail({ to: email, subject: 'Verify Your Email', type: 'magicLink', url: verificationLink }, this.emailProvider); return token; } async verifyOTP(email, otp) { const existingUser = await this.adapter.getUserByEmail(email); if (!existingUser) { return { success: false, error: 'User not registered!' }; } const verification = await this.adapter.useVerificationToken({ identifier: email, token: otp }); if (!verification) { return { success: false, error: 'OTP does not exist!' }; } else if (verification) { const hasExpired = new Date(verification.expires) < new Date(); if (hasExpired) { return { success: false, error: 'Token has expired!' }; } // Mark email as verified and remove verification record await this.adapter.updateUser({ id: existingUser.id, email, emailVerified: new Date() }); return true; } return false; } } export const getVerifyEmailActions = (options, adapter, emailProvider) => { if (!options) throw new Error('Email verification config is required'); if (!adapter) throw new Error('Adapter is required'); if (!emailProvider) throw new Error('Email Provider is required'); const emailService = new EmailVerificationService(options, adapter, emailProvider); const verifyEmailActions = { sendOTP: async ({ request }) => { const data = await request.formData(); const email = data.get('email'); try { await emailService.sendOTP(email); return { success: true }; } catch (error) { return { success: false, error: error.message }; } }, sendMagicLink: async ({ request }) => { const data = await request.formData(); const email = data.get('email'); try { await emailService.sendMagicLink(email); return { success: true }; } catch (error) { return { success: false, error: error.message }; } }, verifyOTP: async ({ request }) => { const data = await request.formData(); const email = data.get('email'); const otp = data.get('otp'); try { const verified = await emailService.verifyOTP(email, otp); return { success: verified }; } catch (error) { return { success: false, error: error.message }; } } }; return verifyEmailActions; };