Skip to content
🚧 These docs are a work in progress and may contain inaccuracies. Content is being actively reviewed and validated.

Stream Tokens

Streaming endpoints use a special authentication method because native video players (AVPlayer on Apple TV, ExoPlayer on Android TV) cannot set HTTP headers on individual segment requests.

When a playback session is created, the client receives a session ID. Streaming URLs authenticate using the same Bearer token as the REST API, but support two methods:

GET /api/stream/{sessionId}/master.m3u8
Authorization: Bearer <token>

The web client uses hls.js which can inject the Authorization header on every XHR request via the xhrSetup callback.

GET /api/stream/{sessionId}/master.m3u8?token=<token>

Native video players (AVPlayer, ExoPlayer) load HLS playlists and segments via their built-in HTTP stack, which doesn’t support custom headers. The token is passed as a query parameter instead.

Stream tokens are not the same as session Bearer tokens. They are short-lived, HMAC-signed tokens scoped specifically to streaming:

PropertyStream tokenBearer token
FormatuserId:sessionId:expiresAt:hmacOpaque session ID
AlgorithmHMAC-SHA256Database-backed session
Lifetime4 hours7 days (auto-extends)
ScopeStreaming endpoints onlyAll API endpoints
Comparisoncrypto.timingSafeEqualDatabase lookup
KeyBETTER_AUTH_SECRETN/A

Stream tokens are embedded in HLS playlist URLs so that native video players (which can’t set HTTP headers on segment requests) can authenticate. If a segment URL leaks, the blast radius is limited — the token expires in 4 hours and only grants access to streaming.

  • Limited scope — Stream tokens only authenticate streaming requests, not the full API. They cannot be used to modify data or access non-streaming endpoints.
  • Session binding — Streaming sessions are bound to the user who created them. A valid token for a different user cannot access another user’s session.
ContextAuth method
REST API callsAuthorization: Bearer header
HLS playlist/segments (web)Authorization: Bearer header via hls.js
HLS playlist/segments (TV)?token= query parameter
Trickplay sprite images?token= query parameter (needed for <img> tags)
SSE event stream (web)Cookie (same-origin EventSource)
SSE event stream (TV)?token= query parameter
Image proxyOptional (public images may not require auth)

If building a custom player that connects to Dubby:

  1. AuthenticatePOST /api/auth/sign-in/email to get a Bearer token
  2. Create sessionPOST /v1/playback/sessions with the token to get a session ID and master playlist URL
  3. Load playlist — Fetch the master playlist URL, appending ?token=<token> if your player can’t set headers
  4. Heartbeat — Send POST /v1/playback/sessions/:id/heartbeat every 10 seconds with the current position
  5. End sessionDELETE /v1/playback/sessions/:id when playback stops

Sessions are automatically cleaned up after 2 minutes of no heartbeat.