AWS SES Plugin
@kardoe/better-auth-aws-ses — AWS SES email delivery for Better Auth.
@kardoe/better-auth-aws-ses is a Better Auth plugin that sends transactional emails via AWS SES. It handles AWS Signature V4 signing, HTML/text email templates, and rate limiting — designed to work in Cloudflare Workers (no Node.js aws-sdk dependency).
Installation
npm install @kardoe/better-auth-aws-sesThis plugin is auto-included when emailOtp or magicLink is enabled in your Quickback config.
Email Types
The plugin handles 8 email types:
| Endpoint | When Sent |
|---|---|
/ses/send-verification | Signup email verification |
/ses/send-password-reset | Password reset request |
/ses/send-magic-link | Passwordless sign-in link |
/ses/send-otp | One-time password code |
/ses/send-welcome | Post-signup welcome email |
/ses/send-organization-invitation | Team/org invite with role |
| Combo auth | Magic link + OTP in a single email |
| Delete account | Account deletion confirmation |
Combo Auth Mode
When useComboAuthEmail: true is set, the plugin sends a single email that includes both a magic link button and an OTP code. Users can choose either method to authenticate.
AWS Signature V4
The plugin signs requests to SES using AWS Signature V4 with the Web Crypto API (crypto.subtle), making it compatible with Cloudflare Workers without any AWS SDK dependency.
Templates
Each email type has both HTML and plain text templates. Templates include:
- Responsive HTML layout with MSO (Microsoft Outlook) support
- Security warning banners for sensitive operations
- Fallback plain text versions for all email clients
- XSS prevention via HTML escaping and URL sanitization
Configuration
Server Plugin
import { awsSESPlugin } from "@kardoe/better-auth-aws-ses";
const auth = betterAuth({
plugins: [
awsSESPlugin({
region: env.AWS_SES_REGION,
accessKeyId: env.AWS_ACCESS_KEY_ID,
secretAccessKey: env.AWS_SECRET_ACCESS_KEY,
fromEmail: env.EMAIL_FROM,
fromName: env.EMAIL_FROM_NAME || "My App",
replyTo: env.EMAIL_REPLY_TO,
appName: env.APP_NAME || "My App",
appUrl: env.APP_URL,
useComboAuthEmail: true, // Optional: combine magic link + OTP
}),
],
});Client Plugin
import { awsSESClientPlugin } from "@kardoe/better-auth-aws-ses/client";
const authClient = createAuthClient({
plugins: [awsSESClientPlugin()],
});Required Environment Variables
| Variable | Description |
|---|---|
AWS_ACCESS_KEY_ID | AWS access key (use Wrangler secrets) |
AWS_SECRET_ACCESS_KEY | AWS secret key (use Wrangler secrets) |
AWS_SES_REGION | SES region (e.g., us-east-2) |
EMAIL_FROM | Sender email address (must be SES-verified) |
Optional Variables
| Variable | Description |
|---|---|
EMAIL_FROM_NAME | Sender display name |
EMAIL_REPLY_TO | Reply-to address (defaults to EMAIL_FROM) |
APP_NAME | Application name shown in email templates |
APP_URL | Application URL for links in emails |
BETTER_AUTH_URL | Auth API URL (for verification/reset links) |
Rate Limiting
The plugin includes basic in-memory rate limiting per recipient:
- Configurable hourly and daily limits
- Per-recipient tracking
- For production, consider using an external store (Redis, KV)
awsSESPlugin({
// ...
rateLimit: {
maxEmailsPerHour: 10,
maxEmailsPerDay: 50,
},
})Error Handling
- Email failures return
false(don't throw) — signup/login flows continue even if email delivery fails - Invalid or placeholder AWS credentials are detected at startup
- Detailed console logging for debugging delivery issues
See Also
- Combo Auth Plugin — Combined magic link + OTP authentication
- Auth Configuration — Configuring auth plugins