diff options
| author | bndw <ben@bdw.to> | 2026-02-15 10:31:06 -0800 |
|---|---|---|
| committer | bndw <ben@bdw.to> | 2026-02-15 10:31:06 -0800 |
| commit | f658ef072394ff9fd28244ad475859c210e8ec16 (patch) | |
| tree | cc08f5f3e09a1a75dd307a1439f53c5e6a27d0ac | |
| parent | 57bc300fe26812aad568c8119f04d92e94c9ab14 (diff) | |
feat: track authorized (authenticated + successful) requests
Add 'authorized' status for requests that complete successfully after
authentication. This complements the existing 'unauthenticated' (pre-auth)
status tracking.
Now the dashboard shows:
- Authorized: Authenticated requests that succeeded
- Unauthorized: Authenticated requests rejected (not in allowlist)
- Pre-Auth: Requests sent before authentication
This gives full visibility into the auth flow:
1. Challenges: How many clients authenticated
2. Authorized: How many authenticated requests succeeded
3. Unauthorized: How many were rejected despite valid auth
4. Pre-Auth: How many tried before authenticating
Updated metrics:
- requests_total{status="authorized"} - authenticated successes
- requests_total{status="ok"} - unauthenticated successes (when no auth)
| -rw-r--r-- | internal/handler/websocket/handler.go | 14 | ||||
| -rw-r--r-- | internal/handler/websocket/handler_test.go | 7 | ||||
| -rw-r--r-- | internal/metrics/dashboard.html | 7 |
3 files changed, 26 insertions, 2 deletions
diff --git a/internal/handler/websocket/handler.go b/internal/handler/websocket/handler.go index dfe7b9e..909e2ec 100644 --- a/internal/handler/websocket/handler.go +++ b/internal/handler/websocket/handler.go | |||
| @@ -348,7 +348,12 @@ func (h *Handler) handleEvent(ctx context.Context, conn *websocket.Conn, raw []j | |||
| 348 | 348 | ||
| 349 | h.subs.MatchAndFan(pbEvent) | 349 | h.subs.MatchAndFan(pbEvent) |
| 350 | 350 | ||
| 351 | status = "ok" | 351 | // Track whether request was authenticated for metrics |
| 352 | if state.authenticatedPubkey != "" { | ||
| 353 | status = "authorized" | ||
| 354 | } else { | ||
| 355 | status = "ok" | ||
| 356 | } | ||
| 352 | h.sendOK(ctx, conn, event.ID, true, "") | 357 | h.sendOK(ctx, conn, event.ID, true, "") |
| 353 | return nil | 358 | return nil |
| 354 | } | 359 | } |
| @@ -448,7 +453,12 @@ func (h *Handler) handleReq(ctx context.Context, conn *websocket.Conn, raw []jso | |||
| 448 | 453 | ||
| 449 | go h.streamEvents(ctx, conn, sub) | 454 | go h.streamEvents(ctx, conn, sub) |
| 450 | 455 | ||
| 451 | status = "ok" | 456 | // Track whether request was authenticated for metrics |
| 457 | if state.authenticatedPubkey != "" { | ||
| 458 | status = "authorized" | ||
| 459 | } else { | ||
| 460 | status = "ok" | ||
| 461 | } | ||
| 452 | return nil | 462 | return nil |
| 453 | } | 463 | } |
| 454 | 464 | ||
diff --git a/internal/handler/websocket/handler_test.go b/internal/handler/websocket/handler_test.go index 10405b2..604a190 100644 --- a/internal/handler/websocket/handler_test.go +++ b/internal/handler/websocket/handler_test.go | |||
| @@ -305,6 +305,13 @@ func TestAuthRequired(t *testing.T) { | |||
| 305 | t.Errorf("Expected OK true after auth, got false: %v", msg3[3]) | 305 | t.Errorf("Expected OK true after auth, got false: %v", msg3[3]) |
| 306 | } | 306 | } |
| 307 | t.Logf("Publish succeeded after auth") | 307 | t.Logf("Publish succeeded after auth") |
| 308 | |||
| 309 | // Verify authorized requests are tracked in metrics | ||
| 310 | authorizedCount := ts.metrics.getRequestCount("EVENT", "authorized") | ||
| 311 | if authorizedCount == 0 { | ||
| 312 | t.Errorf("Expected authorized requests to be tracked in metrics, got 0") | ||
| 313 | } | ||
| 314 | t.Logf("Metrics: %d authorized requests tracked", authorizedCount) | ||
| 308 | } | 315 | } |
| 309 | 316 | ||
| 310 | // TestAuthNotInAllowlist verifies that pubkeys not in allowlist are rejected | 317 | // TestAuthNotInAllowlist verifies that pubkeys not in allowlist are rejected |
diff --git a/internal/metrics/dashboard.html b/internal/metrics/dashboard.html index ab08eab..b7be1d7 100644 --- a/internal/metrics/dashboard.html +++ b/internal/metrics/dashboard.html | |||
| @@ -172,6 +172,10 @@ | |||
| 172 | <span class="metric-value" id="auth_success">0</span> | 172 | <span class="metric-value" id="auth_success">0</span> |
| 173 | </div> | 173 | </div> |
| 174 | <div class="metric"> | 174 | <div class="metric"> |
| 175 | <span class="metric-label">Authorized</span> | ||
| 176 | <span class="metric-value" id="auth_authorized">0</span> | ||
| 177 | </div> | ||
| 178 | <div class="metric"> | ||
| 175 | <span class="metric-label">Unauthorized</span> | 179 | <span class="metric-label">Unauthorized</span> |
| 176 | <span class="metric-value" id="auth_unauthorized">0</span> | 180 | <span class="metric-value" id="auth_unauthorized">0</span> |
| 177 | </div> | 181 | </div> |
| @@ -309,6 +313,9 @@ | |||
| 309 | document.getElementById('auth_success').textContent = | 313 | document.getElementById('auth_success').textContent = |
| 310 | getMetricByLabel(metrics, `${prefix}_relay_auth_attempts_total`, 'result', 'success'); | 314 | getMetricByLabel(metrics, `${prefix}_relay_auth_attempts_total`, 'result', 'success'); |
| 311 | 315 | ||
| 316 | document.getElementById('auth_authorized').textContent = | ||
| 317 | getMetricByLabel(metrics, `${prefix}_relay_requests_total`, 'status', 'authorized'); | ||
| 318 | |||
| 312 | document.getElementById('auth_unauthorized').textContent = | 319 | document.getElementById('auth_unauthorized').textContent = |
| 313 | getMetricByLabel(metrics, `${prefix}_relay_requests_total`, 'status', 'unauthorized'); | 320 | getMetricByLabel(metrics, `${prefix}_relay_requests_total`, 'status', 'unauthorized'); |
| 314 | 321 | ||
