-
Notifications
You must be signed in to change notification settings - Fork 2
AuthAttemptLimiter
Pair\Core\AuthAttemptLimiter rate-limits password authentication attempts through RateLimiter.
It is used by User during normal web login and token-based mobile login, so both flows share the same protection and the same generic failure message.
Use AuthAttemptLimiter when a custom authentication flow checks local Pair credentials and should apply the same login-attempt limits as the framework.
The limiter protects two buckets for each attempt:
- the normalized login identifier, such as email or username
- the client IP address
Both keys are hashed before reaching the storage-backed rate limiter. This slows down brute-force attempts against one account and broad attempts from one address without exposing raw identifiers in limiter storage.
The default login limiter reads:
PAIR_AUTH_RATE_LIMIT_ENABLED=true
PAIR_AUTH_RATE_LIMIT_MAX_ATTEMPTS=10
PAIR_AUTH_RATE_LIMIT_DECAY_SECONDS=900Invalid or non-positive numeric values fall back to safe minimums.
Builds the default limiter for password login attempts from Env.
$limiter = \Pair\Core\AuthAttemptLimiter::login();__construct(bool $enabled = true, int $maxAttempts = 10, int $decaySeconds = 900, ?RateLimiter $limiter = null)
Creates a limiter with explicit settings. Supplying a RateLimiter instance is useful in tests or in a custom integration that already owns limiter storage.
$limiter = new \Pair\Core\AuthAttemptLimiter(true, 5, 300);Records one authentication attempt and returns the stricter decision between the IP bucket and the identifier bucket.
$attempt = $limiter->attempt('login', $email, $_SERVER['REMOTE_ADDR'] ?? null);
if (!$attempt->allowed) {
return \Pair\Api\ApiResponse::errorResponse('AUTHENTICATION_FAILED');
}The $scope lets applications keep separate buckets for different auth surfaces, such as login, admin-login, or password-reset.
Clears both buckets after a successful authentication challenge.
$limiter->clear('login', $email, $_SERVER['REMOTE_ADDR'] ?? null);User calls AuthAttemptLimiter::login() before validating credentials.
When the attempt is blocked, Pair:
- writes the failed-login audit record
- returns the normal generic authentication failure message
- does not reveal whether the identifier exists
When login succeeds, Pair clears the matching limiter buckets.
- Using different scopes for
attempt()andclear(). - Keying only by IP and missing distributed attempts against one account.
- Returning a distinct "too many attempts" message that reveals account existence.
- Forgetting that file-backed limits are local to the current node when Redis is not configured.
See also: User, RateLimiter, Configuration file, Env, API.