veza/veza-backend-api/docs/ACCOUNT_LOCKOUT.md
2026-02-07 20:36:48 +01:00

2.4 KiB

Account lockout (BE-SEC-007)

How it works

After too many failed login attempts, the account is temporarily locked to slow down brute-force attacks.

  1. Storage: Lock state and attempt counts are stored in Redis (keys account_lockout:attempts:{email} and account_lockout:locked:{email}).
  2. Failed attempt: Each failed login (wrong password, user not found, or email not verified) calls RecordFailedAttempt(email). The attempt counter is incremented; it expires after a window (default 15 minutes).
  3. Lock: When the number of failed attempts in the window reaches max attempts (default 5), the account is locked for a lockout duration (default 30 minutes).
  4. While locked: Any login for that email returns HTTP 423 Locked with message "Account is locked. Please try again later."
  5. Unlock: The lock key has a TTL; when it expires, the account is automatically unlocked. A successful login also clears the lock and the attempt counter.

Defaults (see internal/services/account_lockout_service.go):

  • Max attempts: 5
  • Window: 15 minutes
  • Lockout duration: 30 minutes

If Redis is unavailable, lockout is disabled (no locking, no recording).


Unlock an account

As an admin user, send:

curl -X POST http://localhost:8080/api/v1/admin/auth/unlock-account \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <ADMIN_ACCESS_TOKEN>" \
  -d '{"email":"user@example.com"}'

Response: 200 OK with {"message":"account unlocked","email":"user@example.com"}.

Option 2: Redis CLI

If you have access to Redis:

# Replace with the locked user's email
EMAIL="user@example.com"

redis-cli DEL "account_lockout:locked:${EMAIL}" "account_lockout:attempts:${EMAIL}"

Disable lockout for specific accounts

Use exempt emails so those accounts are never locked and failed attempts are not recorded.

Environment variable (comma-separated list):

ACCOUNT_LOCKOUT_EXEMPT_EMAILS=testuser@example.com,admin@test.com

Example in .env or .env.development:

ACCOUNT_LOCKOUT_EXEMPT_EMAILS=testuser@example.com

After restarting the API, that email will:

  • Never be considered locked (IsAccountLocked returns false).
  • Not have failed attempts recorded (RecordFailedAttempt is a no-op).

This is intended for test / dev accounts only; avoid exempting real user emails in production.