security: httpOnly cookies, rate limiting, nginx headers, SSE sanitization

Auth / token storage:
- JWT is now set as an httpOnly Secure SameSite=Lax cookie on login
- Add POST /auth/logout endpoint that clears the cookie
- get_current_member falls back to rh_token cookie when no Authorization header
- WebSocket auth now accepts cookie (rh_token) or optional ?token= query param
- Frontend removes all localStorage JWT access; uses credentials:"include" on
  every fetch so the httpOnly cookie is sent automatically
- Replace clearToken() with logout() that calls the server logout endpoint
- Non-sensitive rh_session flag in localStorage used only for client-side routing

Rate limiting:
- Add slowapi>=0.1.9 dependency
- /auth/login limited to 10 req/min per IP
- /auth/register limited to 5 req/min per IP

Nginx security headers:
- Add X-Frame-Options, X-Content-Type-Options, Referrer-Policy,
  X-XSS-Protection, Permissions-Policy to all responses

SSE error leakage:
- songs.py nc-scan/stream no longer leaks str(exc) to clients

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Mistral Vibe
2026-03-30 21:11:53 +02:00
parent 68da26588a
commit c1941ed9ac
14 changed files with 109 additions and 40 deletions

View File

@@ -40,9 +40,8 @@ export function useWaveform(
normalize: true,
});
const token = localStorage.getItem("rh_token");
const audioUrl = token ? `${options.url}?token=${encodeURIComponent(token)}` : options.url;
ws.load(audioUrl);
// The rh_token httpOnly cookie is sent automatically by the browser.
ws.load(options.url);
ws.on("ready", () => {
setIsReady(true);