summaryrefslogtreecommitdiffstats
path: root/internal/handler/websocket/handler.go
diff options
context:
space:
mode:
authorbndw <ben@bdw.to>2026-02-15 10:06:26 -0800
committerbndw <ben@bdw.to>2026-02-15 10:06:26 -0800
commitae95a3fd20b5dc8c25ee947f5cc8e120a185b8bc (patch)
treed8df895e4480a3416b0e6a2a46f723afc8bd00e9 /internal/handler/websocket/handler.go
parentd744c32f1bc7411e04c97a9d14c172baaa0e4a89 (diff)
fix: improve WebSocket error responses and protocol compliance
- Send OK false for rate limit errors instead of NOTICE - Send OK false for auth errors (e.g. pubkey not in allowlist) - Remove OK response for AUTH events (AUTH is not an EVENT type) - Parse event before auth checks to get event ID for error responses These changes improve client UX by providing immediate, structured feedback for all rejection cases instead of generic NOTICE messages. The AUTH event OK removal fixes a bug where clients would read the wrong response when sending EVENT after AUTH.
Diffstat (limited to 'internal/handler/websocket/handler.go')
-rw-r--r--internal/handler/websocket/handler.go18
1 files changed, 10 insertions, 8 deletions
diff --git a/internal/handler/websocket/handler.go b/internal/handler/websocket/handler.go
index 5201698..a23dd60 100644
--- a/internal/handler/websocket/handler.go
+++ b/internal/handler/websocket/handler.go
@@ -257,18 +257,19 @@ func (h *Handler) handleEvent(ctx context.Context, conn *websocket.Conn, raw []j
257 return fmt.Errorf("EVENT expects 2 elements") 257 return fmt.Errorf("EVENT expects 2 elements")
258 } 258 }
259 259
260 if err := h.requireAuth(ctx, conn, true, state); err != nil { 260 // Parse event first to get ID for error responses
261 status = "error"
262 return err
263 }
264
265 // Parse event to get ID for OK response
266 var event nostr.Event 261 var event nostr.Event
267 if err := json.Unmarshal(raw[1], &event); err != nil { 262 if err := json.Unmarshal(raw[1], &event); err != nil {
268 status = "error" 263 status = "error"
269 return fmt.Errorf("invalid event: %w", err) 264 return fmt.Errorf("invalid event: %w", err)
270 } 265 }
271 266
267 if err := h.requireAuth(ctx, conn, true, state); err != nil {
268 status = "error"
269 h.sendOK(ctx, conn, event.ID, false, err.Error())
270 return nil
271 }
272
272 if state.authenticatedPubkey == "" && h.authConfig != nil && h.authConfig.WriteEnabled { 273 if state.authenticatedPubkey == "" && h.authConfig != nil && h.authConfig.WriteEnabled {
273 status = "unauthenticated" 274 status = "unauthenticated"
274 h.sendOK(ctx, conn, event.ID, false, "auth-required: authentication required") 275 h.sendOK(ctx, conn, event.ID, false, "auth-required: authentication required")
@@ -283,7 +284,8 @@ func (h *Handler) handleEvent(ctx context.Context, conn *websocket.Conn, raw []j
283 } 284 }
284 if !h.limiter.Allow(identifier, "EVENT") { 285 if !h.limiter.Allow(identifier, "EVENT") {
285 status = "rate_limited" 286 status = "rate_limited"
286 return fmt.Errorf("rate limit exceeded") 287 h.sendOK(ctx, conn, event.ID, false, "rate-limited: slow down")
288 return nil
287 } 289 }
288 } 290 }
289 291
@@ -558,7 +560,7 @@ func (h *Handler) handleAuth(ctx context.Context, conn *websocket.Conn, raw []js
558 state.authenticatedPubkey = authEvent.PubKey 560 state.authenticatedPubkey = authEvent.PubKey
559 log.Printf("WebSocket client authenticated: %s", authEvent.PubKey[:16]) 561 log.Printf("WebSocket client authenticated: %s", authEvent.PubKey[:16])
560 562
561 h.sendOK(ctx, conn, authEvent.ID, true, "") 563 // Don't send OK for AUTH - it's not an EVENT
562 return nil 564 return nil
563} 565}
564 566