Quickback Docs

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-ses

This plugin is auto-included when emailOtp or magicLink is enabled in your Quickback config.

Email Types

The plugin handles 8 email types:

EndpointWhen Sent
/ses/send-verificationSignup email verification
/ses/send-password-resetPassword reset request
/ses/send-magic-linkPasswordless sign-in link
/ses/send-otpOne-time password code
/ses/send-welcomePost-signup welcome email
/ses/send-organization-invitationTeam/org invite with role
Combo authMagic link + OTP in a single email
Delete accountAccount 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

VariableDescription
AWS_ACCESS_KEY_IDAWS access key (use Wrangler secrets)
AWS_SECRET_ACCESS_KEYAWS secret key (use Wrangler secrets)
AWS_SES_REGIONSES region (e.g., us-east-2)
EMAIL_FROMSender email address (must be SES-verified)

Optional Variables

VariableDescription
EMAIL_FROM_NAMESender display name
EMAIL_REPLY_TOReply-to address (defaults to EMAIL_FROM)
APP_NAMEApplication name shown in email templates
APP_URLApplication URL for links in emails
BETTER_AUTH_URLAuth 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

On this page