72 lines
2.4 KiB
Markdown
72 lines
2.4 KiB
Markdown
# 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
|
|
|
|
### Option 1: Admin API (recommended)
|
|
|
|
As an **admin** user, send:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
# 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):
|
|
|
|
```bash
|
|
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.
|