# API Contract: Authentication

## POST /api/v1/auth/token

Authenticates the owner and returns a JWT access token.

**This endpoint is modified by feature 009** to enforce brute-force protection.
All previous behaviour is preserved. One new response code (429) is added.

### Request

```
POST /api/v1/auth/token
Content-Type: application/json
```

```json
{
  "username": "string",
  "password": "string"
}
```

### Responses

#### 200 OK — Credentials accepted

```json
{
  "access_token": "<jwt>",
  "token_type": "bearer",
  "expires_in": 86400
}
```

Side effect: resets the failure counter for the caller's IP address.

---

#### 401 Unauthorized — Credentials rejected

```json
{
  "detail": "Invalid credentials",
  "code": "invalid_credentials"
}
```

Side effect: increments the failure counter for the caller's IP address. If the
counter reaches `LOGIN_MAX_FAILURES`, subsequent requests from this IP will receive
429 until the cooldown expires.

---

#### 429 Too Many Requests — Source blocked after repeated failures

**This response is new in feature 009.**

```
HTTP/1.1 429 Too Many Requests
Retry-After: 900
Content-Type: application/json
```

```json
{
  "detail": "Too many failed login attempts. Please try again later.",
  "code": "login_rate_limited"
}
```

The `Retry-After` header value is the configured cooldown duration in seconds (default: 900).
It reflects the maximum possible wait, not the exact remaining lockout time.

No credentials are verified when this response is returned — the request is
rejected before authentication is attempted.

---

### Notes

- The failure counter is per source IP address (TCP peer, not forwarded headers).
- Threshold values (`LOGIN_MAX_FAILURES`, `LOGIN_WINDOW_SECONDS`, `LOGIN_COOLDOWN_SECONDS`)
  are not disclosed in any response.
- Counters are in-memory and reset on process restart.
