Skip to content

fix: require server-issued viewer session token - closes #725 - #1660

Merged
davedumto merged 1 commit into
StreamFi-x:devfrom
robertocarlous:fix/725-viewer-count-auth
Oct 15, 2026
Merged

davedumto merged 1 commit into
StreamFi-x:devfrom
robertocarlous:fix/725-viewer-count-auth

Conversation

@robertocarlous

@robertocarlous robertocarlous commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Description

Closes #725
Closes #751
Closes #727
Closes #652

Changes proposed

What were you told to do?

Fix #725: Viewer count tracking has no auth, enabling trivial metrics fraud. app/api/streams/viewers trusted an arbitrary, client-supplied sessionId on POST and DELETE, letting anyone inflate a stream's current_viewers/total_views with fresh random UUIDs, or zero out a competitor's count by guessing a session id.

What did you do?

Anonymous viewing must keep working, so this is not a verifySession gate. Instead:

  • New lib/stream/viewer-session-token.ts — mints an HMAC-signed token (existing signToken/verifyToken keyring) that binds a server-generated session id (sid) to one stream (pid, the Mux playback id), with a 12h expiry.
  • app/api/streams/viewers/route.ts
    • Added GET ?playbackId=... to mint a viewer session (stream must be live; force-dynamic + no-store so a cached response can never hand everyone the same id).
    • POST, PATCH (heartbeat), and DELETE now require the token and verify it matches the request's playbackId/sessionId — a client can no longer choose its own session id, and a token for one stream can't be replayed against another (401 otherwise).
    • Rate limiting re-keyed: 120 req/min per IP plus 60 req/min per IP + token-bound stream, instead of per freely-chosen sessionId.
    • DELETE's fallback decrement uses the token's pid, never the client-supplied playbackId.
  • app/[username]/watch/page.tsx — mints the viewer session before joining and sends the token on join, heartbeat, and leave. Existing heartbeat + reconciliation (feat: Build a reconciliation job correcting current_viewers drift #1403) still handles viewers who never send a graceful DELETE.
  • Tests — route suite expanded 3 → 15 (mint, missing/forged/stream-mismatched/session-mismatched token rejection, happy paths for POST/PATCH/DELETE).
  • docs/security-audit.md — updated the rate-limit inventory entry for this route.

Check List (Check all the applicable boxes)

  • My code follows the code style of this project.
  • This PR does not contain plagiarized content.
  • The title and description of the PR is clear and explains the approach.
  • I am making a pull request against the main branch (left side).
  • My commit messages styles matches our requested structure.
  • My code additions will fail neither code linting checks nor unit test.
  • I am only making changes to files I was requested to.

Screenshots/Videos

N/A — API/security change. Test evidence: app/api/streams/viewers suite passes 15/15; full-suite failure set is byte-identical to the clean dev baseline (no new failures). ESLint 0 errors and Prettier clean on touched files.

@vercel

vercel Bot commented Oct 15, 2026

Copy link
Copy Markdown

@robertocarlous is attempting to deploy a commit to the david's projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Oct 15, 2026

Copy link
Copy Markdown

@robertocarlous Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@davedumto
davedumto merged commit 60b922b into StreamFi-x:dev Oct 15, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants