import type { EmailConfig, EmailUserConfig } from '@auth/core/providers/email'; import { generateRandomString, RandomReader } from '@oslojs/crypto/random'; import { alphabet } from 'oslo/crypto'; type UseSendEmailPayload = { from: string; to: string[]; subject: string; text: string; html: string; }; const sendUseSendEmail = async ( apiKey: string, useSendUrl: string, payload: UseSendEmailPayload, ) => { const response = await fetch(`${useSendUrl}/api/v1/emails`, { method: 'POST', headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, body: JSON.stringify(payload), }); if (!response.ok) { let errorBody: unknown; try { errorBody = await response.json(); } catch { errorBody = response.statusText; } throw new Error(`UseSend error: ${JSON.stringify(errorBody)}`); } }; export default function UseSendProvider(config: EmailUserConfig): EmailConfig { return { id: 'usesend', type: 'email', name: 'UseSend', from: process.env.USESEND_FROM_EMAIL ?? 'noreply@example.com', maxAge: 24 * 60 * 60, // 24 hours generateVerificationToken: () => { const random: RandomReader = { read: (bytes) => { crypto.getRandomValues(bytes as Uint8Array); }, }; return generateRandomString(random, alphabet('0-9'), 6); }, sendVerificationRequest: async (params) => { const { identifier: to, provider, url, token } = params; // Derive a display name from the site URL, fallback to 'App' const siteUrl = process.env.USESEND_FROM_EMAIL ?? ''; const appName = siteUrl.split('@')[1]?.split('.')[0] ?? 'App'; const apiKey = process.env.USESEND_API_KEY; const useSendUrl = process.env.USESEND_URL; if (!apiKey || !useSendUrl) { throw new Error('USESEND_API_KEY and USESEND_URL must be set.'); } // For password reset, we want to send the code, not the magic link const isPasswordReset = url.includes('reset') || provider.id.includes('reset'); await sendUseSendEmail(apiKey, useSendUrl, { from: provider.from ?? 'noreply@example.com', to: [to], subject: isPasswordReset ? `Reset your password - ${appName}` : `Sign in to ${appName}`, text: isPasswordReset ? `Your password reset code is ${token}` : `Your sign in code is ${token}`, html: isPasswordReset ? `

Password Reset Request

You requested a password reset. Your reset code is:

${token}

This code expires in 1 hour.

If you didn't request this, please ignore this email.

` : `

Your Sign In Code

Your verification code is:

${token}

This code expires in 24 hours.

`, }); }, options: config, }; } // Create specific instances for password reset and email verification export const UseSendOTPPasswordReset = UseSendProvider({ id: 'usesend-otp-password-reset', apiKey: process.env.USESEND_API_KEY, maxAge: 60 * 60, // 1 hour }); export const UseSendOTP = UseSendProvider({ id: 'usesend-otp', apiKey: process.env.USESEND_API_KEY, maxAge: 60 * 20, // 20 minutes });