slowapi==0.1.9
Rebuild the API image after pulling: docker compose ... up -d --build api
| Variable | Default | Purpose |
|---|---|---|
ACCESS_TOKEN_EXPIRE_MINUTES |
15 |
Short-lived JWT for API calls |
REFRESH_TOKEN_EXPIRE_DAYS |
7 |
Refresh token lifetime |
RATE_LIMIT_DEFAULT |
60/minute |
Global API limit per IP |
RATE_LIMIT_LOGIN |
5/minute |
/token |
RATE_LIMIT_REGISTER |
10/minute |
/register |
RATE_LIMIT_REFRESH |
10/minute |
/token/refresh |
RATE_LIMIT_AUDIT |
30/minute |
/audit-logs |
LOGIN_MAX_ATTEMPTS |
5 |
Failures before lockout |
LOGIN_LOCKOUT_MINUTES |
15 |
Lockout duration |
POST /token→{ access_token, refresh_token, expires_in }- Call APIs with
Authorization: Bearer <access_token> - When access expires →
POST /token/refreshwith{ "refresh_token": "..." } - Use new pair; old refresh token should be discarded client-side
Refresh tokens include typ: refresh and are rejected on protected routes.
If /docs is blank but /openapi.json returns 200, the API is fine — the browser blocked
Swagger UI scripts due to Content-Security-Policy: default-src 'none'. Documentation paths
now use a relaxed CSP (see app/middleware/security_headers.py).
- Register with strong password:
Admin123! - Login via
/token— copyaccess_tokenandrefresh_token - Authorize with access token
- Call
/audit-logs,/devices, etc. /token/refreshwith JSON body{ "refresh_token": "..." }- Use expired/wrong token →
401+INVALID_TOKENin audit logs - Hit
/token6+ times with wrong password →429lockout +LOGIN_LOCKOUTaudit
for i in {1..10}; do curl -s -o /dev/null -w "%{http_code}\n" -X POST http://localhost/token -d "username=x&password=y"; doneExpect 429 after the configured login limit.