Auch verfügbar in: 🇩🇪 Deutsch

Auth & Users

Inhalt

🇩🇪 Deutsche Version


Authentication and authorization are handled by Better Auth (apps/api/src/auth.ts). Sessions are managed server-side; all role checks are enforced at the API layer.


Authentication Providers

Provider Status Notes
Email / password Available Default — enabled on all instances
OIDC (Authentik, Keycloak, etc.) Planned v1.0 Standard OpenID Connect, configurable via env
Passkey / WebAuthn Planned v1.0 Passwordless, device-bound credentials
Social OAuth (GitHub, Google, etc.) Not planned Out of scope for self-hosted use case

User Roles

Role Description Permissions
Admin Full instance control Everything — settings, users, content, theme, legal
Moderator Content moderation View all content, hide/delete videos/comments, manage report queue
Creator Content creation Upload videos, manage own channels and videos, edit Studio
Viewer Consumption only Watch public and unlisted videos, submit comments (if enabled by admin)

Role assignment is managed in /admin/users. A user can hold one role at a time. Admins can promote or demote any account.


Admin Bootstrap

On a fresh instance, the first admin is bootstrapped via the INITIAL_ADMIN_EMAIL environment variable.

How it works:

  1. Set INITIAL_ADMIN_EMAIL=you@example.com in your environment or compose file
  2. Register a new account using that exact email address
  3. Better Auth's onAfterSignup hook checks the email and automatically promotes the account to Admin

This is a one-time operation. Once an Admin exists, further promotions are done through the admin panel. The env var can be left set without side effects — it only fires for the matching email once.


Session Management

  • Cookie: HTTP-only session cookie, set on successful login
  • SameSite: Lax — protects against CSRF while allowing top-level navigation
  • Secure flag: Set automatically when BASE_URL uses https://
  • Session lifetime: 30 days (sliding expiry on activity)
  • Storage: Sessions are stored server-side in the Postgres database (via Better Auth's built-in session table)

Clients never receive the raw session secret. All session validation happens on the API.


Visibility Enforcement

Video visibility is enforced centrally via canViewVideo() at every read endpoint. No client-side gating — the check happens in the tRPC procedure before any data is returned.

Visibility level Who can watch
public Everyone, including unauthenticated visitors
unlisted Anyone with the direct link — not shown in feeds or search
logged_in Authenticated users only (any role)
private Video owner and Admins only

Signed playback URLs for logged_in and private videos expire after 1 hour. Attempting to play an expired URL returns a 403.


Rate Limiting

Rate limits are enforced at the Fastify layer before requests reach tRPC.

Endpoint group Limit
Global 60 requests / minute / IP
File upload (tus) 5 parallel uploads per user
Account registration 3 new accounts / hour / IP
Login attempts 10 attempts / 15 minutes / IP (then 15 min lockout)

Limits are tracked in Redis with a sliding window. In the all-in-one container, Redis is internal and always available.


Registration Control

Admins can disable public registration in /admin/settings (toggle: Allow new registrations). When disabled:

  • The /register page shows a "Registration is closed" message
  • The registration API endpoint returns 403 REGISTRATION_DISABLED
  • Existing accounts are unaffected

This enables invite-only mode. Admins can still create accounts manually from /admin/users.


© Benjamin Weber · ITSWEBER — play.itsweber.net · GitHub