Authentication & OAuth
Summary
GoLiveBro uses a three-layer security model separating account/entitlement, control-plane API auth, and relay publish/play auth. The OBS plugin authenticates via a browser-based login flow: the plugin creates a login attempt with the backend, opens the system browser for the user to sign in (via Twitch, Google, Discord, or email/password), and polls until the backend confirms completion. The backend issues a short-lived cp_access_jwt (10-15 min TTL) and an opaque refresh_token (90 days), both stored in DPAPI vault on the client. Relay access is gated by server-side entitlement checks on POST /relay/start, not by UI gating alone.
OAuth was introduced in the W2 redesign (2026-03-24), replacing the original operator-only auth where an admin manually approved plugin login attempts. The new system supports Twitch, Google, and Discord as OAuth providers, plus email/password signup. OAuth tokens are encrypted at rest with AES-256-GCM. Account linking is supported via a merge confirmation flow when email collisions are detected across providers. A user_oauth_accounts table stores linked provider credentials, and the session response includes linked_accounts status for the dock UI.
The web dashboard’s JWT storage was migrated from localStorage to an HttpOnly cookie (telemy_session) as part of security refactor C2 (2026-03-25). Cloudflare Pages Functions act as a BFF proxy, reading the cookie and forwarding API calls with a Bearer header. The Go backend remains unchanged. The OBS plugin continues to use Bearer tokens directly.
Timeline
- 2026-03-16: Auth login + entitlement plan drafted. Defined the browser-based plugin login flow,
cp_access_jwt/refresh_tokenmodel, and per-user stream ID design (RF-001). Replaced hardcodedlive_telemy/play_telemywith per-userstream_token(8-char random alphanumeric onuserstable). - 2026-03-16: RF-001 per-user stream ID auth approved. Three approaches evaluated; per-user SLS-layer stream ID selected over IP allowlist (breaks mobile) and HMAC SRTLA auth (breaks third-party clients).
- 2026-03-22: Phase 5 entitlement enforcement design approved. Defined free/standard/internal tier structure with server-side relay limit enforcement.
GetRelayEntitlement()extracted as pure function. Addedaddon_relay_countcolumn andGET /api/v1/capacity/statuspublic endpoint. - 2026-03-24: W2 OAuth auth redesign approved. Three OAuth providers (Twitch, Google, Discord) plus email/password. Migration 0020 adds
user_oauth_accounts,oauth_states,merge_requeststables. AES-256-GCM token encryption. Plugin login flow unchanged. - 2026-03-25: C2 HttpOnly cookie design approved (CRITICAL security refactor). JWT moved from localStorage to HttpOnly
telemy_sessioncookie. CF Pages Functions BFF proxy created atfunctions/api/v1/[[path]].js. SameSite=Strict for CSRF protection.
Current State
The auth system supports four sign-in methods: Twitch OAuth, Google OAuth, Discord OAuth, and email/password. All methods create the same users row, with multiple providers linkable to one account via verified email merge.
Plugin login flow: POST /auth/plugin/login/start returns an authorize_url pointing to the self-service login page (replaced operator approval page). After browser sign-in, the plugin receives JWT + refresh token via polling at POST /auth/plugin/login/poll. Token persistence: JWT (24h) + refresh token (90d) in DPAPI vault. Silent refresh on OBS relaunch if JWT expired.
Web dashboard auth: server-side cookie auth gate at functions/dashboard/index.js. Dashboard JS calls relative /api/v1/... paths (no Bearer header). Cookie cleared on logout via proxy response. No localStorage JWT storage.
Entitlement tiers: free (0 managed relays), standard (1 + addon_relay_count), internal (99). All tiers get full telemetry dock and unlimited BYOR. Entitlement enforced server-side at POST /relay/start. Deny reason codes: subscription_required, subscription_inactive, connection_limit_reached, user_not_found.
Per-user stream tokens: 8-char alphanumeric stream_token on users table, injected into relay user-data at provision time. SLS rejects publishers/players with wrong stream ID. Token regeneration endpoint planned but not yet implemented.
Key Decisions
- 2026-03-16: Browser-based plugin auth over embedded CEF login. CEF in OBS is a bad trust boundary for control-plane secrets. No loopback callback server needed for v1.
- 2026-03-16: Per-user SLS-layer stream ID selected over IP allowlist (breaks mobile streaming) and HMAC SRTLA auth (breaks IRL Pro, Larix, and all existing clients).
- 2026-03-16: License keys rejected as primary UX. Easy to share, awkward revocation, worse UX than browser login. Acceptable only as internal fallback.
- 2026-03-22: Three-tier model (free/standard/internal) replacing starter/pro. Free gets zero managed relays. All managed relays full-capability (4K/60fps) with hidden 48 Mbps soft cap.
- 2026-03-24: OAuth token encryption at rest with AES-256-GCM via
OAUTH_ENCRYPTION_KEYenv var. OAuth tokens never touch the plugin. Plugin only sees JWT +linked: true/false. - 2026-03-24: Account merge flow for email collisions. When a new OAuth sign-in matches an existing email, a
merge_requestsrow is created (10min expiry). User must re-auth with original provider to confirm. - 2026-03-25: JWT moved from localStorage to HttpOnly cookie. XSS can no longer exfiltrate the token. CSRF blocked by SameSite=Strict. Trade-off: XSS in-session API calls still possible (inherent), addressed by future CSP headers.
Gotchas & Known Issues
- Stream tokens are 8 hex chars. Acceptable risk for invite-only beta with non-public relay hosts, but revisit before public launch (security backlog).
cp_access_jwtmust never be exposed to dock JS.stream_tokenmay be displayed to the user as operational connection info.- Plugin login attempt expiry is 5 minutes with a one-time poll token.
- Refresh token rotation: each refresh invalidates the old token. If the client misses the new token, the user must re-authenticate.
- Per-IP rate limiting on auth endpoints: login 5/min, signup 3/min, forgot-password 3/hr, plugin poll 10/min.
- Email verification required before email/password login works. For v1, verification URLs are logged server-side (actual email service deferred).
- Discord bot-to-server OAuth flow deferred. Discord sign-in works for account creation/linking, but the bot WebSocket gateway is separate infrastructure.
- The
telemy_sessioncookie domain is set totelemyapp.comonly (not.telemyapp.com). Cookie is never sent toapi.telemyapp.comdirectly; only CF Functions read it. - Stripe env vars (
GLB_STRIPE_*) not yet configured on production. Billing webhooks return “not configured” until set.
Open Questions
- Token regeneration endpoint (
POST /api/v1/user/regenerate-tokens) planned but not implemented. Design supports it with no architectural changes. - CSP headers for remaining XSS in-session risk (L-items backlog).
- Annual pricing ($99/yr) deferred until monthly is proven.
- Whether
internaltier users should appear differently in admin panel search results.
Sources
- AUTH_ENTITLEMENT_MODEL.md
- 2026-03-16-auth-login-entitlement-plan.md
- 2026-03-16-rf001-per-user-stream-id-auth-design.md
- 2026-03-16-rf001-per-user-stream-id-auth.md
- 2026-03-24-w2-oauth-auth-plan.md
- 2026-03-24-w2-oauth-auth-redesign.md
- 2026-03-25-c2-httponly-cookie-design.md
- 2026-03-25-c2-httponly-cookie-plan.md
- 2026-03-22-phase5-entitlement-enforcement-design.md
- 2026-03-22-phase5-entitlement-enforcement.md