Email service library for QAuth with token generation utilities. This library provides secure token generation and a flexible email service with provider abstraction.
The @qauth-labs/server-email library provides:
- Token Generator: Secure token generation and verification utilities
- Email Service: Factory-pattern email service with provider abstraction
- Email Providers: Resend, SMTP, and Mock providers for sending emails
- React Email Templates: Type-safe, component-based email templates
This library is part of the QAuth monorepo and is automatically available to other projects within the workspace.
import { generateVerificationToken, createEmailService } from '@qauth-labs/server-email';Generates a secure verification token pair (token and hash).
import { generateVerificationToken } from '@qauth-labs/server-email';
const { token, tokenHash } = generateVerificationToken();
// token: "a1b2c3d4e5f6..." (64 chars, send to user)
// tokenHash: "9f8e7d6c5b4a..." (64 chars, store in DB)Security Features:
- 32-byte (256-bit) random tokens using
crypto.randomBytes(32) - Hex encoding (64 characters) for URL-safe transmission
- SHA-256 hashing for secure storage
- High entropy prevents brute-force attacks
Hashes a token using SHA-256 before storing in the database.
import { hashToken } from '@qauth-labs/server-email';
const hash = hashToken('a1b2c3d4e5f6...');
// Returns: "9f8e7d6c5b4a..." (SHA-256 hash)Validates that a token is a valid 64-character hexadecimal string.
import { isValidTokenFormat } from '@qauth-labs/server-email';
isValidTokenFormat('a1b2c3d4e5f6...'); // true (64 hex chars)
isValidTokenFormat('invalid'); // false (too short)Compares two tokens in constant time to prevent timing attacks.
import { constantTimeCompare } from '@qauth-labs/server-email';
const isValid = constantTimeCompare(storedToken, providedToken);Security: Uses crypto.timingSafeEqual to prevent timing attacks.
The email service uses dependency injection for flexibility:
import { createEmailService, MockEmailProvider } from '@qauth-labs/server-email';
const provider = new MockEmailProvider();
const emailService = createEmailService(provider, {
defaultFrom: 'noreply@example.com',
baseUrl: 'https://example.com',
});Sends a verification email with a token.
await emailService.sendVerificationEmail('user@example.com', 'token123');With custom options:
await emailService.sendVerificationEmail('user@example.com', 'token123', {
subject: 'Custom Subject',
from: 'custom@example.com',
text: 'Custom text',
html: '<p>Custom HTML</p>',
});The email service is built on a provider abstraction, making it verification-agnostic and reusable for other email types (password reset, notifications, etc.):
interface EmailProvider {
sendEmail(options: EmailOptions): Promise<EmailResult>;
}The library supports multiple email providers:
For testing and development:
import { MockEmailProvider } from '@qauth-labs/server-email';
const provider = new MockEmailProvider();
await provider.sendEmail({
to: 'user@example.com',
subject: 'Test',
text: 'Body',
});
// Get sent emails for testing
const sentEmails = provider.getSentEmails();
console.log(sentEmails);
// Clear history
provider.clearSentEmails();For production use with Resend API:
import { ResendEmailProvider } from '@qauth-labs/server-email';
const provider = new ResendEmailProvider({
apiKey: 're_...',
fromAddress: 'noreply@example.com',
});
await provider.sendEmail({
to: 'user@example.com',
subject: 'Welcome',
html: '<p>Welcome!</p>',
text: 'Welcome!',
});Features:
- Automatic retry on transient failures
- Idempotency key support to prevent duplicate sends
- TypeScript-first SDK
For self-hosted deployments:
import { SmtpEmailProvider } from '@qauth-labs/server-email';
const provider = new SmtpEmailProvider({
host: 'smtp.example.com',
port: 587,
secure: false,
auth: {
user: 'user@example.com',
pass: 'password',
},
fromAddress: 'noreply@example.com',
});
await provider.sendEmail({
to: 'user@example.com',
subject: 'Welcome',
html: '<p>Welcome!</p>',
text: 'Welcome!',
});Features:
- Support for STARTTLS and direct SSL/TLS
- Configurable authentication
- Connection pooling via nodemailer
The library includes React Email templates for type-safe, component-based email creation:
import { VerifyEmail, renderEmail, renderEmailText } from '@qauth-labs/server-email';
import * as React from 'react';
// Create template
const template = React.createElement(VerifyEmail, {
verificationUrl: 'https://example.com/auth/verify?token=abc123',
expiresIn: '24 hours',
});
// Render to HTML
const html = await renderEmail(template);
// Render to plain text
const text = await renderEmailText(template);The email service automatically uses React Email templates for verification emails when custom HTML/text is not provided.
Generates a secure token pair.
Returns: { token: string, tokenHash: string }
Hashes a token using SHA-256.
Parameters:
token: Plain token string to hash
Returns: SHA-256 hash (64-character hex string)
Validates token format.
Parameters:
token: Token string to validate
Returns: true if valid, false otherwise
Compares tokens in constant time.
Parameters:
token1: First tokentoken2: Second token
Returns: true if tokens match, false otherwise
Creates an email service instance.
Parameters:
provider: Email provider implementationconfig: Optional service configuration
Returns: Email service instance
emailService.sendVerificationEmail(to: string, token: string, options?: Partial<EmailOptions>): Promise<EmailResult>
Sends a verification email.
Parameters:
to: Recipient email addresstoken: Verification tokenoptions: Optional email options
Returns: Promise resolving to email result
- High Entropy: 32 bytes (256 bits) of random data
- Secure Random: Uses
crypto.randomBytes(32)(CVE-2023-2781 mitigation) - Hashing: Tokens are hashed before storage (SHA-256)
- Constant-Time Comparison: Prevents timing attacks
- Never store plain tokens: Always store the hash (
tokenHash) - Send plain token to user: Include the plain
tokenin the verification email - Compare hashes: When verifying, hash the provided token and compare with stored hash
import {
generateVerificationToken,
hashToken,
constantTimeCompare,
} from '@qauth-labs/server-email';
// 1. Generate token on registration
const { token, tokenHash } = generateVerificationToken();
// Store tokenHash in database
await db.emailVerificationTokens.create({ tokenHash, userId, expiresAt });
// 2. Send token to user
await emailService.sendVerificationEmail(user.email, token);
// 3. Verify token when user clicks link
const providedToken = request.query.token;
const storedTokenHash = await db.emailVerificationTokens.findByTokenHash(tokenHash);
// Hash the provided token and compare
const providedTokenHash = hashToken(providedToken);
const isValid = constantTimeCompare(storedTokenHash, providedTokenHash);Run tests:
nx test server-emailimport type {
TokenPair,
EmailProvider,
EmailService,
EmailOptions,
EmailResult,
EmailServiceConfig,
} from '@qauth-labs/server-email';@qauth-labs/fastify-plugin-email: Fastify plugin for email service@qauth-labs/server-password: Password hashing library@qauth-labs/shared-validation: Email validation utilities
Apache-2.0