Advanced

Rate Limiting

Protect your authentication endpoints from abuse with rate limiting

Protect your authentication and API endpoints from abuse using rate limiting with nuxthub-ratelimit.

Why Rate Limit?

Authentication endpoints are prime targets for:

  • Brute force attacks - Trying many passwords
  • Credential stuffing - Using leaked credentials
  • Account enumeration - Discovering valid emails
  • Email abuse - Spamming password reset emails

Rate limiting restricts how many requests a client can make in a given time window.

Installation

pnpm add nuxthub-ratelimit

Basic Configuration

Add the module and configure routes in your nuxt.config.ts:

// nuxt.config.ts
export default defineNuxtConfig({
  extends: ['@fyit/crouton-auth'],
  modules: ['nuxthub-ratelimit'],

  rateLimit: {
    routes: {
      // Strict limits for auth endpoints
      '/api/auth/*': {
        maxRequests: 15,
        intervalSeconds: 60
      },
      // More lenient for general API
      '/api/*': {
        maxRequests: 150,
        intervalSeconds: 60
      }
    }
  }
})

For production applications with @fyit/crouton-auth, we recommend these limits:

// nuxt.config.ts
export default defineNuxtConfig({
  extends: ['@fyit/crouton-auth'],
  modules: ['nuxthub-ratelimit'],

  rateLimit: {
    routes: {
      // Sign-in: Prevent brute force
      '/api/auth/sign-in/*': {
        maxRequests: 10,
        intervalSeconds: 60
      },
      // Sign-up: Prevent account spam
      '/api/auth/sign-up/*': {
        maxRequests: 5,
        intervalSeconds: 60
      },
      // Password reset: Prevent email abuse
      '/api/auth/forgot-password': {
        maxRequests: 3,
        intervalSeconds: 60
      },
      // Email verification: Prevent verification spam
      '/api/auth/verify-email': {
        maxRequests: 5,
        intervalSeconds: 60
      },
      // OAuth: Slightly higher for redirects
      '/api/auth/callback/*': {
        maxRequests: 20,
        intervalSeconds: 60
      },
      // General auth fallback
      '/api/auth/*': {
        maxRequests: 15,
        intervalSeconds: 60
      },
      // Team API endpoints
      '/api/teams/*': {
        maxRequests: 100,
        intervalSeconds: 60
      },
      // General API
      '/api/*': {
        maxRequests: 150,
        intervalSeconds: 60
      }
    }
  }
})

Limits Reference

Endpoint PatternRecommended LimitReason
/api/auth/sign-in/*10/minBrute force protection
/api/auth/sign-up/*5/minPrevent account spam
/api/auth/forgot-password3/minPrevent email abuse
/api/auth/verify-email5/minPrevent verification spam
/api/auth/callback/*20/minOAuth redirect allowance
/api/auth/*15/minGeneral auth fallback
/api/teams/*100/minTeam operations
/api/*150/minGeneral API

Requirements

NuxtHub Required: Rate limiting uses NuxtHub KV for storage. Ensure you have KV enabled in your NuxtHub project.

NuxtHub Configuration

// nuxt.config.ts
export default defineNuxtConfig({
  hub: {
    kv: true  // Enable KV storage
  }
})

Limitations

  • Minimum TTL: 60 seconds (NuxtHub KV limitation)
  • Storage: Uses KV, not in-memory (persists across deployments)
  • Edge: Works on Cloudflare Workers edge runtime

How It Works

  1. Each request is tracked by client IP + route pattern
  2. Counter stored in NuxtHub KV with TTL
  3. When limit exceeded, returns 429 Too Many Requests
  4. Counter resets after interval expires

Handling Rate Limit Errors

On the client, handle 429 errors gracefully:

<script setup lang="ts">
const { signIn } = useAuth()
const error = ref<string | null>(null)

const handleLogin = async () => {
  try {
    await signIn.email({ email, password })
  } catch (e: any) {
    if (e.statusCode === 429) {
      error.value = 'Too many attempts. Please wait a minute and try again.'
    } else {
      error.value = e.message
    }
  }
}
</script>

<template>
  <UAlert v-if="error" color="red" :title="error" />
</template>

Testing Rate Limits

During development, you can test rate limits:

# Quick test with curl
for i in {1..20}; do
  curl -s -o /dev/null -w "%{http_code}\n" \
    -X POST http://localhost:3000/api/auth/sign-in/email \
    -H "Content-Type: application/json" \
    -d '{"email":"test@example.com","password":"wrong"}'
done

You should see 200 responses turn to 429 after hitting the limit.