Rate Limiting in dracory/auth

Last Updated: 2026-10-29


Overview

dracory/auth includes built-in rate limiting for all authentication endpoints. The goal is to protect your application from:

By default, rate limiting is enabled and uses an in-memory sliding window implementation.


What Is Rate Limited?

Rate limiting is applied per IP address and logical endpoint for all auth-related operations, including:

Under NewMultiMethodAuth each method's routes keep their own logical endpoint identity, so limits apply per route (e.g. /login, /login-otp, /login-magiclink) rather than being shared globally.

When the limit is exceeded, the library returns:


Default Behavior

The default in-memory limiter is implemented in the internal utils package (see utils/rate_limiter.go) and is initialized automatically when you create an auth instance (passwordless or username/password) and do not provide your own rate-limiter function.

Defaults:

This means:

Note: The built-in limiter is in-memory and per-process. In a multi-instance deployment, each instance maintains its own counters.


Configuration Options

Rate limiting is configured via shared fields on ConfigOtp, ConfigUsernameAndPassword, and ConfigAuthKnight.

// Shared rate limiting options (for both configs)
DisableRateLimit   bool                                                                                 // Set to true to disable rate limiting (not recommended for production)
FuncCheckRateLimit func(ip string, endpoint string) (allowed bool, retryAfter time.Duration, err error) // Optional: override default rate limiter
MaxLoginAttempts   int                                                                                  // Maximum attempts before lockout (default: 5)
LockoutDuration    time.Duration                                                                        // Duration for sliding window and lockout (default: 15 minutes)

Examples

Otp Flow

authInstance, err := auth.NewOtpAuth(auth.ConfigOtp{
    Endpoint:             "/auth",
    UrlRedirectOnSuccess: "/dashboard",

    // Rate limiting (defaults shown explicitly)
    MaxLoginAttempts: 5,
    LockoutDuration:  15 * time.Minute,
})

Username/Password Flow

authInstance, err := auth.NewUsernameAndPasswordAuth(auth.ConfigUsernameAndPassword{
    Endpoint:             "/auth",
    UrlRedirectOnSuccess: "/dashboard",

    // Rate limiting (defaults shown explicitly)
    MaxLoginAttempts: 5,
    LockoutDuration:  15 * time.Minute,
})

AuthKnight Flow

authInstance, err := auth.NewAuthKnightAuth(types.ConfigAuthKnight{
    ConfigShared: types.ConfigShared{
        Endpoint:             "/auth",
        UrlRedirectOnSuccess: "/dashboard",

        // Rate limiting (defaults shown explicitly)
        MaxLoginAttempts: 5,
        LockoutDuration:  15 * time.Minute,
    },
    FuncUserFindByEmail: userFindByEmail,
    FuncUserRegister:    userRegister,
})

You can temporarily set DisableRateLimit: true in development environments, but this is strongly discouraged in production.


Using a Custom Rate Limiter

For production systems running multiple instances, you will usually want a shared rate limiter (for example, backed by Redis or another central store).

You can plug in your own implementation via FuncCheckRateLimit:

import "time"

func myRateLimiter(ip string, endpoint string) (bool, time.Duration, error) {
    // Look up and increment attempt counters in your store (e.g., Redis).
    // Return allowed = false and a positive retryAfter when the limit is exceeded.
    // Return an error only for internal failures (network, store, etc.).

    // Example (pseudo-code):
    // attempts := redis.Incr(key(ip, endpoint))
    // if attempts > 5 {
    //     return false, 15 * time.Minute, nil
    // }
    // return true, 0, nil

    return true, 0, nil
}

authInstance, err := auth.NewUsernameAndPasswordAuth(auth.ConfigUsernameAndPassword{
    Endpoint:             "/auth",
    UrlRedirectOnSuccess: "/dashboard",

    FuncCheckRateLimit: myRateLimiter,
})

Important notes:


When to Disable Rate Limiting

You might temporarily disable rate limiting by setting DisableRateLimit: true in:

However:


Related Documentation