Description
The ChatWebSocketHandler has two potential issues:
- Token in Query Param:
extractUsernameFromJWT looks for the JWT in a query parameter (?token=...). This is a common pattern, but it's less secure than using headers (which WebSockets don't easily support) or a one-time ticket system. The token can be logged in server logs, proxy logs, and browser history.
- Anonymous ID Extraction:
extractAnonSessionId tries to get the ID from cookies, but falls back to a query parameter (?anonSessionId=...). This is flexible but also creates two different ways to connect.
A more secure approach for authenticated users is to have them hit a standard REST endpoint (/api/ws/ticket) to get a short-lived, one-time-use ticket, and then connect to the WebSocket with ?ticket=....
Acceptance Criteria
- Create a new endpoint (e.g.,
POST /api/auth/ws-ticket) that is secured (requires Authorization header).
- When a user hits this endpoint, generate a short-lived (e.g., 30 seconds), one-time-use ticket (e.g., a random UUID) and store it in Redis or a cache with the user's email/ID as the value (e.g.,
cache.put(ticket, email)).
- Modify
ChatWebSocketHandler.extractUsernameFromJWT:
- Rename it to
extractUserFromTicket.
- Change it to look for a
ticket query parameter.
- Validate the ticket against the cache. If it exists, retrieve the user's email/ID and immediately evict the ticket from the cache to prevent reuse.
- Remove the
token query param logic.
- Update documentation (
README.md) to reflect this new, more secure connection flow for authenticated users.
- The
anonSessionId logic can remain as-is, but the query param fallback should be considered for removal in favor of cookies-only.
Description
The
ChatWebSocketHandlerhas two potential issues:extractUsernameFromJWTlooks for the JWT in a query parameter (?token=...). This is a common pattern, but it's less secure than using headers (which WebSockets don't easily support) or a one-time ticket system. The token can be logged in server logs, proxy logs, and browser history.extractAnonSessionIdtries to get the ID from cookies, but falls back to a query parameter (?anonSessionId=...). This is flexible but also creates two different ways to connect.A more secure approach for authenticated users is to have them hit a standard REST endpoint (
/api/ws/ticket) to get a short-lived, one-time-use ticket, and then connect to the WebSocket with?ticket=....Acceptance Criteria
POST /api/auth/ws-ticket) that is secured (requires Authorization header).cache.put(ticket, email)).ChatWebSocketHandler.extractUsernameFromJWT:extractUserFromTicket.ticketquery parameter.tokenquery param logic.README.md) to reflect this new, more secure connection flow for authenticated users.anonSessionIdlogic can remain as-is, but the query param fallback should be considered for removal in favor of cookies-only.