Rate limiting for authentication endpoints to prevent brute-force attacks.
This module provides comprehensive rate limiting protection for:
- Login attempts (prevents password brute-forcing)
- Magic link generation (prevents token enumeration)
- Password reset requests (prevents mass reset attacks)
- User registration (prevents spam account creation)
Configuration
Rate limits can be configured in your application config:
# config/config.exs
config :phoenix_kit, PhoenixKit.Users.RateLimiter,
login_limit: 5, # Max login attempts per window
login_window_ms: 60_000, # 1 minute window
magic_link_limit: 3, # Max magic link requests per window
magic_link_window_ms: 300_000, # 5 minute window
password_reset_limit: 3, # Max password reset requests per window
password_reset_window_ms: 300_000, # 5 minute window
registration_limit: 3, # Max registration attempts per window
registration_window_ms: 3600_000, # 1 hour window
registration_ip_limit: 10, # Max registrations per IP per window
registration_ip_window_ms: 3600_000 # 1 hour windowSecurity Features
- Email-based rate limiting: Prevents targeted attacks on specific accounts
- IP-based rate limiting: Prevents distributed attacks from single sources
- Timing attack mitigation: Consistent response times for valid/invalid emails
- Exponential backoff: Automatically enforced through time windows
- Comprehensive logging: All rate limit violations are logged for security monitoring
Usage Examples
# Check login rate limit
case PhoenixKit.Users.RateLimiter.check_login_rate_limit(email, ip_address) do
:ok -> proceed_with_login()
{:error, :rate_limit_exceeded} -> show_rate_limit_error()
end
# Check magic link rate limit
case PhoenixKit.Users.RateLimiter.check_magic_link_rate_limit(email) do
:ok -> generate_magic_link()
{:error, :rate_limit_exceeded} -> show_cooldown_message()
endProduction Recommendations
- Use Redis backend for distributed systems (hammer_backend_redis)
- Monitor rate limit violations for security threats
- Adjust limits based on your application's usage patterns
- Consider implementing CAPTCHA after multiple violations
Summary
Functions
Checks if login attempts are within rate limit.
Checks if magic link generation is within rate limit.
Checks if password reset requests are within rate limit.
Checks if registration attempts are within rate limit.
Gets the remaining attempts for a specific action and identifier.
Resets rate limit for a specific action and identifier.
Functions
Checks if login attempts are within rate limit.
Returns :ok if the request is allowed, or {:error, :rate_limit_exceeded} if the limit is exceeded.
This function implements dual rate limiting:
- Per-email rate limiting (prevents targeted attacks on specific accounts)
- Per-IP rate limiting (prevents distributed brute-force attacks)
Examples
iex> PhoenixKit.Users.RateLimiter.check_login_rate_limit("user@example.com", "192.168.1.1")
:ok
# After 5 failed attempts:
iex> PhoenixKit.Users.RateLimiter.check_login_rate_limit("user@example.com", "192.168.1.1")
{:error, :rate_limit_exceeded}
Checks if magic link generation is within rate limit.
Returns :ok if the request is allowed, or {:error, :rate_limit_exceeded} if the limit is exceeded.
Magic links have stricter rate limits to prevent token enumeration attacks.
Examples
iex> PhoenixKit.Users.RateLimiter.check_magic_link_rate_limit("user@example.com")
:ok
# After 3 requests in 5 minutes:
iex> PhoenixKit.Users.RateLimiter.check_magic_link_rate_limit("user@example.com")
{:error, :rate_limit_exceeded}
Checks if password reset requests are within rate limit.
Returns :ok if the request is allowed, or {:error, :rate_limit_exceeded} if the limit is exceeded.
Password reset requests have moderate rate limits to prevent mass reset attacks while still allowing legitimate users to recover their accounts.
Examples
iex> PhoenixKit.Users.RateLimiter.check_password_reset_rate_limit("user@example.com")
:ok
# After 3 requests in 5 minutes:
iex> PhoenixKit.Users.RateLimiter.check_password_reset_rate_limit("user@example.com")
{:error, :rate_limit_exceeded}
Checks if registration attempts are within rate limit.
Returns :ok if the request is allowed, or {:error, :rate_limit_exceeded} if the limit is exceeded.
Registration has dual rate limiting:
- Per-email rate limiting (prevents spam account creation with same email)
- Per-IP rate limiting (prevents mass account creation from single source)
Examples
iex> PhoenixKit.Users.RateLimiter.check_registration_rate_limit("user@example.com", "192.168.1.1")
:ok
# After limit exceeded:
iex> PhoenixKit.Users.RateLimiter.check_registration_rate_limit("user@example.com", "192.168.1.1")
{:error, :rate_limit_exceeded}
Gets the remaining attempts for a specific action and identifier.
Returns the number of attempts remaining before rate limit is exceeded.
For login and registration actions, returns the email-based limit. For magic_link and password_reset, returns the limit for the email.
Note: With Hammer 7.x, this uses the get/2 function to retrieve the current count.
Examples
iex> PhoenixKit.Users.RateLimiter.get_remaining_attempts(:login, "user@example.com")
5
iex> PhoenixKit.Users.RateLimiter.get_remaining_attempts(:magic_link, "user@example.com")
3
Resets rate limit for a specific action and identifier.
DEPRECATED: Hammer 7.x removed delete_buckets with no replacement.
This function now returns an error as Backend.set/3 requires positive integers (cannot set to 0).
Rate limits will naturally expire after their configured window period.
Migration
- For testing: Use
Application.put_envto disable rate limiting - For admin intervention: Wait for the time window to expire
- For immediate reset: Restart the application (clears ETS tables)
See: https://hexdocs.pm/hammer/upgrade-v7.html
Examples
iex> PhoenixKit.Users.RateLimiter.reset_rate_limit(:login, "email:user@example.com")
{:error, :not_supported}