# 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": "", "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.