> ## Documentation Index
> Fetch the complete documentation index at: https://woku.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Brute force protection

> Automatic lockout after failed attempts, user notification, and administrative unlock

Woku detects and blocks brute force attacks against login to
protect user accounts.

## How it works

1. Every login attempt (successful or failed) is recorded in a
   `LoginAttempt` table with the identifier used (lowercased), the
   source IP, and the result.
2. If an account accumulates **5 failed attempts in the last 15
   minutes**, the system:
   * Sets the user's `lockedUntil` field to **15 minutes in the
     future**.
   * Records an `auth.lockout` entry in the [audit
     log](/docs/en/seguridad/audit-log) with `failedAttempts`,
     `lockedUntil`, and the IP of the last attempt.
   * Sends an **email** to the user notifying them of the lockout (see
     below).
3. During the lockout, any login attempt (even with the correct
   password) responds with `403 Forbidden`.
4. Once the 15-minute window passes, the lockout is lifted
   automatically on the next attempt, no manual action is
   required.

## Notification email

When the lockout is triggered, the system sends the user an email
through AWS SES with:

* **Subject**: "Temporary lock on your woku account".
* **Body**: explains that we detected several failed attempts,
  when the lockout is lifted, from which IP, and a CTA to reset
  the password.

The email is **fire-and-forget**: if SES is down, the lockout still
happens and the email failure is logged internally; there is no impact
on security.

## Administrative unlock

If the user needs to log in before the window passes, an
internal administrator can unlock them from the backoffice. The
action is recorded as `auth.unlock` in the audit log with the IP of
the request.

## Limitations

* Detection is **per-user**, not per-IP. A distributed attacker
  probing many different users from multiple IPs does not trigger
  the per-user lockout, but is still exposed to the global rate
  limit (10 req/s, 50 req/10s, 100 req/min per IP, managed
  by `@nestjs/throttler`).
* If the attacker uses an identifier that does not match any
  user, there is no lockout (there is nothing to lock), but the rate
  limit per IP still applies.

## Status check

A user's lockout status is not exposed publicly to the
front end. The UI shows the same generic message ("Invalid
credentials or locked account") to avoid revealing whether an account
exists, this reduces the risk of user enumeration.
