🔌 Dymo API in Better-Auth

Take Better-Auth security to the next level with built-in resilience system.

Overview

In order to facilitate the use of Dymo API in applications that integrate Better-Auth, we have launched a new library specialized in projects that use Better-Auth as an authentication system. This integration includes the complete resilience system for reliable API operations during authentication flows.

Installation

npm install @dymo-api/better-auth
#or
pnpm install @dymo-api/better-auth
#or
yarn add @dymo-api/better-auth

Plugin Features

Sign Up General Protection (Coming Soon)
Protect your sign-up from fraudulent users
User-Agent Validation (Coming Soon)
Easily validate User-Agents in Better-Auth

Resilience System

All Better-Auth plugins come with the built-in resilience system that provides:

  • 🔄 Automatic Retries: Configurable retry attempts with exponential backoff
  • 🚦 Rate Limiting: Intelligent tracking and management of API rate limits
  • 🛡️ Fallback Data: Automatic generation of safe fallback responses when API is unavailable
  • ⚡ Smart Error Handling: Differentiates between retry-worthy and non-retry-worthy errors
  • 🔐 Seamless Integration: Works automatically during authentication flows

Basic Configuration

import { betterAuth } from "better-auth";
import { dymoEmailPlugin } from "@dymo-api/better-auth";

const auth = betterAuth({
  // Your Better-Auth configuration
  emailAndPassword: { ... },
  socialProviders: { ... },
  
  // Dymo plugins with resilience
  plugins: [
      dymoEmailPlugin({
          apiKey: "",
          emailRules: { 
              deny: ["FRAUD", "INVALID", "DISPOSABLE"] 
          },
          resilience: {
              fallbackEnabled: true,    // Enable fallback data generation
              retryAttempts: 2,         // Number of additional retry attempts
              retryDelay: 500          // Base delay in milliseconds
          }
      }),
      dymoIPPlugin({
          apiKey: "",
          ipRules: { 
              deny: ["FRAUD", "TOR_NETWORK"] 
          },
          resilience: {
              fallbackEnabled: true,
              retryAttempts: 1,
              retryDelay: 200
          }
      })
  ]
});

Production vs Development

const productionAuth = betterAuth({
  plugins: [
      dymoEmailPlugin({
          apiKey: "",
          emailRules: { deny: ["FRAUD", "INVALID", "DISPOSABLE"] },
          resilience: {
              fallbackEnabled: true,     // Keep authentication working during outages
              retryAttempts: 2,         // 1 normal + 2 retries = 3 total attempts
              retryDelay: 500          // Faster retries for better UX
          }
      }),
      dymoIPPlugin({
          apiKey: "",
          ipRules: { deny: ["FRAUD", "TOR_NETWORK"] },
          resilience: {
              fallbackEnabled: true,
              retryAttempts: 1,
              retryDelay: 200
          }
      })
  ]
});

// Benefits:
// ✅ Users can authenticate even during API issues
// ✅ Balanced security and user experience  
// ✅ Automatic fallback to safe responses

Benefits:

  • ✅ User authentication maintained during API issues
  • ✅ Balanced retry speed and reliability
  • ✅ Automatic fallback to safe responses
  • ✅ Better user experience during temporary issues

Usage Examples

Email Validation with Resilience

import { betterAuth } from "better-auth";
import { dymoEmailPlugin } from "@dymo-api/better-auth";

const auth = betterAuth({
  emailAndPassword: {
      enabled: true,
      requireEmailVerification: true
  },
  plugins: [
      dymoEmailPlugin({
          apiKey: "",
          emailRules: { deny: ["FRAUD", "INVALID"] },
          resilience: {
              fallbackEnabled: true,
              retryAttempts: 2,
              retryDelay: 500
          }
      })
  ]
});

// The plugin will automatically:
// 1. Validate email on sign-up, sign-in, password reset
// 2. Retry on network failures with exponential backoff
// 3. Use fallback data if API is completely unavailable
// 4. Block fraudulent emails during authentication flows

IP Validation with Resilience

import { betterAuth } from "better-auth";
import { dymoIPPlugin } from "@dymo-api/better-auth";

const auth = betterAuth({
    plugins: [
        dymoIPPlugin({
            apiKey: "",
            
            // Strict IP validation rules
            ipRules: { 
                deny: [
                    "FRAUD",           // Block fraudulent IP addresses
                    "TOR_NETWORK",       // Block Tor exit nodes
                    "HIGH_RISK_SCORE",  // Block high-risk IP ranges
                    "PROXY",           // Block known proxy servers
                    "COUNTRY:RU",      // Block specific countries if needed
                    "COUNTRY:CN"       // Block specific countries if needed
                ]
            },
            
            resilience: {
                fallbackEnabled: true,
                retryAttempts: 1,
                retryDelay: 200
            }
        })
    ]
});

// The plugin automatically:
// 1. Extracts IP from common headers: x-forwarded-for, cf-connecting-ip, etc.
// 2. Validates IP on authentication attempts
// 3. Retries on network failures
// 4. Blocks requests from problematic IPs
// 5. Normalizes IP in request context if successful

Phone Validation with Resilience

import { betterAuth } from "better-auth";
import { dymoPhonePlugin } from "@dymo-api/better-auth";

const auth = betterAuth({
    phoneNumber: { enabled: true },
    
    plugins: [
        dymoPhonePlugin({
            apiKey: "",
            
            // Phone validation rules
            phoneRules: { 
                deny: [
                    "FRAUD",           // Block fraudulent phone numbers
                    "INVALID",          // Block invalid format numbers
                    "VOIP",            // Optionally block VOIP numbers
                    "PREMIUM_RATE"     // Optionally block premium rate numbers
                ]
            },
            
            resilience: {
                fallbackEnabled: true,
                retryAttempts: 1,
                retryDelay: 300
            }
        })
    ]
});

// Validates phone numbers on:
// - /sign-in/phone-number
// - /phone-number/forget-password  
// - /phone-number/send-otp
// - /phone-number/verify

Error Handling & Monitoring

Monitoring Resilience

Monitor console for resilience warnings during authentication flows to understand API behavior and user experience impact.

Common log messages during authentication:

[WARN] [Dymo API] Client xxx is rate limited. Waiting 60 seconds...
# User authentication temporarily delayed

[WARN] [Dymo API] Attempt 1 failed. Retrying in 500ms...
# Authentication temporarily paused, but will retry

[WARN] [Dymo API] Request failed after 3 attempts. Using fallback data.
# Authentication continues with safe fallback response

Fallback Behavior in Authentication

When fallbackEnabled: true and all retries fail during authentication:

// Example: Email validation fallback during user registration
const auth = betterAuth({
    plugins: [
        dymoEmailPlugin({
            apiKey: "",
            resilience: { fallbackEnabled: true }
        })
    ]
});

// If API fails completely during sign-up:
// - User will be blocked (safe fallback = allow: false)
// - Registration will fail gracefully  
// - No user data leaked due to fallback
// - Authentication system remains functional

// Fallback email response:
// {
//   email: "[email protected]",
//   allow: false, 
//   reasons: ["INVALID"]
// }

Rate Limiting in Authentication

The Better-Auth SDK automatically handles rate limiting during authentication flows:

// Multiple concurrent authentication attempts
const handleBurst = async (authRequests: Request[]) => {
    // The SDK will automatically:
    // 1. Track rate limits per client (API key)
    // 2. Respect retry-after headers on 429 responses
    // 3. Queue requests during rate limit periods
    // 4. Not retry rate-limited requests (wait instead)
    
    // Results in graceful degradation rather than complete failures
    const results = await Promise.allSettled(authRequests);
    return results;
};

Best Practices

Security Configuration

const strictAuth = betterAuth({
  plugins: [
      dymoEmailPlugin({
          apiKey: "",
          emailRules: { 
              deny: [
                  "FRAUD", "INVALID", "DISPOSABLE", 
                  "NO_MX_RECORDS", "HIGH_RISK_SCORE", 
                  "NO_REPLY_EMAIL", "FREE_EMAIL"
              ]
          },
          resilience: {
              fallbackEnabled: false,    // No fallbacks for high security
              retryAttempts: 1,         // Minimal retries
              retryDelay: 1500          // Cautious retry timing
          }
      }),
      dymoIPPlugin({
          apiKey: "",
          ipRules: { 
              deny: [
                  "FRAUD", "TOR_NETWORK", "HIGH_RISK_SCORE", 
                  "PROXY", "VPN", "COUNTRY:RU", "COUNTRY:CN"
              ]
          },
          resilience: {
              fallbackEnabled: false,
              retryAttempts: 1,
              retryDelay: 2000
          }
      })
  ]
});

// Benefits:
// ✅ Maximum security validation
// ✅ No acceptance of potentially invalid data
// ✅ Fail-safe approach for sensitive operations
// ✅ Protection against edge cases and bypasses

Use Cases:

  • Financial applications
  • High-security authentication
  • Compliance-required systems
  • Sensitive data protection

Performance Optimization

// Cache validation results to reduce API calls during authentication
const validationCache = new Map<string, { result: boolean, timestamp: number }>();

const cachedValidationPlugin = (options: any) => {
  const plugin = dymoEmailPlugin({
      ...options,
      resilience: { fallbackEnabled: true, retryAttempts: 2, retryDelay: 500 }
  });
  
  // Add caching middleware around the plugin
  const originalMatcher = plugin.hooks?.before?.[0]?.matcher;
  
  if (originalMatcher) {
      plugin.hooks!.before![0].matcher = (context: any) => {
          const email = context.body?.email;
          
          // Check cache first
          if (email && validationCache.has(email)) {
              const cached = validationCache.get(email)!;
              if (Date.now() - cached.timestamp < 300000) { // 5 minutes
                  if (!cached.result) {
                      throw new Error("Cached: Email is invalid or blocked");
                  }
                  return originalMatcher(context);
              }
          }
          
          return originalMatcher(context);
      };
  }
  
  return plugin;
};

// Clear cache periodically
setInterval(() => {
  const now = Date.now();
  for (const [key, value] of validationCache.entries()) {
      if (now - value.timestamp > 300000) { // 5 minutes
          validationCache.delete(key);
      }
  }
}, 60000); // Check every minute

Benefits:

  • ✅ Reduced API calls and costs
  • ✅ Faster authentication for repeat users
  • ✅ Automatic cache management
  • ✅ Still maintains resilience for cache misses

Migration from Previous Version

Updating from Manual Validation

// ❌ Manual validation without resilience
import { betterAuth } from "better-auth";

const oldAuth = betterAuth({
  emailAndPassword: { enabled: true },
  
  // ❌ Manual middleware with no resilience
  middleware: {
      before: [
          {
              matcher: (context) => context.path === "/sign-up",
              handler: async (ctx) => {
                  // ❌ Single API call with no retry logic
                  const response = await fetch('/api/validate-email', {
                      method: 'POST',
                      body: JSON.stringify({ email: ctx.body.email })
                  });
                  
                  if (!response.ok) {
                      // ❌ Immediate failure on network issues
                      throw new Error("Invalid email");
                  }
                  
                  // ❌ No fallback handling
                  // ❌ No rate limit management
                  // ❌ Poor user experience during API issues
              }
          }
      ]
  }
});

Problems:

  • ❌ Manual error handling required
  • ❌ No automatic recovery from failures
  • ❌ Poor user experience during API issues
  • ❌ No rate limit management
  • ❌ Maintenance burden on development team

Updating Configuration

If you were using previous resilience configuration:

// Previous behavior (3 total attempts max)
emailPlugin: { 
    resilience: { retryAttempts: 2 }  // Was: 1 normal + 2 retries = 3 total
}

// Same behavior now  
emailPlugin: { 
    resilience: { retryAttempts: 2 }  // Still: 1 normal + 2 retries = 3 total
}

The resilience behavior remains the same - only the parameter documentation has been clarified. Your existing code will continue to work without changes.

Support

Common Issues

Getting Help

If you encounter issues with the Better-Auth SDK resilience system:

  1. Check Console: Look for resilience warning messages during authentication
  2. Verify Configuration: Ensure resilience options are properly set in plugins
  3. Test Network: Check internet connectivity and API access
  4. Monitor Status: Check API Status Page
  5. Review Rules: Validate your email/IP/phone rules configuration
  6. Contact Support: Include logs, configuration, and authentication flow details

When reporting issues, please include:

  • Your plugin configuration (with resilience settings)
  • Relevant console logs from authentication attempts
  • Validation rules being applied
  • Authentication flows where issues occur
  • Your API key (last 4 characters only)