Ir al contenido principal

Autenticación

An authentication gate plugin makes viewers sign in before reaching the resources selected by the server operator. The plugin supplies the login method, such as OAuth, a magic link, SAML, or a shared password. Owncast enforces the selected access mode.

  • Tu complemento es el proveedor de identidad. Renderiza la pantalla de inicio de sesión, se comunica con el proveedor externo y decide quién tiene permitido entrar.
  • The Owncast host is the gatekeeper and session authority. It owns the session cookie, enforces the selected access mode, and never puts your plugin in the per-request hot path.
Owncat informs youSe requiere auth.gate

Todo en esta página necesita el permiso auth.gate, además de users.register para crear el usuario autenticado y http.serve para renderizar el flujo de inicio de sesión.

Lo que está restringido

When an auth.gate plugin is enabled, the viewer page, chat, embeds, /api/config, and the rest of the public web surface require login. A short list of routes stays public in every mode, including Owncast's admin pages (they keep their own admin authentication, so an operator can always disable a broken gate), the instance logo, and the ActivityPub federation endpoints. See what bypasses the gate for the full list.

The operator selects one cumulative access mode on the plugin's Authentication tab:

Access modeEffect
Website only (default)The web interface requires sign-in. /hls/*, /api/status, and Owncast Directory listing stay public.
Website, video players, and other resourcesAlso gates Owncast-hosted /hls/*. Players such as VLC cannot complete the browser login. /api/status and directory listing stay public.
Website, video players, and server status requestsGates the web interface, Owncast-hosted /hls/*, and /api/status. Owncast Directory listing is disabled.

The modes are cumulative. There is no status-only mode that hides /api/status while leaving HLS public. The default protects the website without breaking existing players or uptime monitors.

Selecting either stream-protection mode blocks native players. VLC, QuickTime, mobile apps, and restreamers cannot complete a browser login or carry the session cookie. An Authorization header or query token does not bypass the gate.

A viewer with a valid session is always let through, regardless of the selected mode.

Owncat warns youDistribución de tu video con advertencia de almacenamiento externo (almacenamiento por objetos/CDN)

When distributing your video stream directly from your server, stream protection is airtight: every byte flows through Owncast. Con Almacenamiento por Objetos o CDN, las listas de reproducción se reescriben a URLs remotas absolutas y los segmentos se obtienen directamente del bucket, por lo que la puerta nunca ve esas solicitudes. La restricción aún detiene a un visitante anónimo de descubrir la lista de segmentos, pero una URL de segmento filtrada o compartida sigue siendo obtenible. Stream protection + local distribution is airtight. Stream protection + Object Storage is good friction, not airtight.

Cómo funciona

Once the gate is armed, every non-exempt request is checked. Under stream protection that includes each HLS segment, which a live viewer pulls every few seconds. Llamar a tu motor embebido del complemento en cada uno de esos sería abrumar el servidor, por lo que se mantiene al complemento fuera del camino crítico:

CuandoCostoQué sucede
Every non-exempt requestVerificar firma de cookie + caducidadvalid passes. Missing or invalid gets a redirect to login, or a 401 for anything that is not a GET or HEAD
La página / carga solamenteLlamada opcional al motor: onAuthCheckre-validate contra tu proveedor, devuelve ok / refresh / deny

Tu complemento solo ejecuta el flujo de inicio de sesión (poco frecuente, aproximadamente una vez por sesión de espectador) y el opcional onAuthCheck por carga de página. El host de Owncast crea y verifica una cookie de sesión firmada para que la verificación por solicitud sea solo de firma y caducidad: sin búsqueda en base de datos, sin llamada al complemento.

The cookie is a signed envelope carrying an Owncast access token plus a session expiry. The host mints a fresh access token for the user each time it grants a session. The Owncast host owns the cookie end to end: it reserves the cookie name (owncast_session), signs it with a host-held secret, and attaches it to the response. Tu complemento nunca ve o establece el token, por lo que no puede falsificarlo o filtrarlo. (Esto también es cómo el chat recoge automáticamente la identidad del espectador. Consulta Identidad de chat a continuación.)

Construyendo un complemento de puerta

Un complemento de puerta es un complemento que sirve HTTP con un flujo de inicio de sesión. El ciclo de control, por convención, está arraigado en el propio espacio de nombres de tu complemento /plugins/\<your-slug>/:

Tres piezas hacen el trabajo:

  1. Registra el usuario. Convierte la identidad externa en un verdadero usuario de Owncast con owncast.users.register. Pasa un authId estable de alcance del proveedor (ej. "github:583231"). El host cuenta con su slug para que los complementos no puedan chocar o suplantarse entre sí.
  2. Concede la sesión. Llama a owncast.auth.grantSession con ese userId. El host de Owncast crea la cookie firmada y la adjunta a la respuesta en vuelo. Esto solo funciona dentro de un manejador onHttpRequest.
  3. Redirigir a casa. El host de Owncast adjunta un parámetro de consulta return_to cuando reenvía a un visitante no autenticado a tu pantalla de inicio de sesión, y lo desinfecta a una ruta de mismo origen (para que no se pueda convertir en una redirección abierta). Envía al espectador allí después de un inicio de sesión exitoso.

Para cerrar la sesión de un espectador, llama a owncast.auth.endSession() y redirige. Tu complemento todavía controla a dónde (puede rebotar a la propia salida del proveedor).

Revocación con onAuthCheck

Las sesiones son sin estado, por lo que no hay una lista de '¿sigue permitido este usuario?' por solicitud. Eso volvería a poner el complemento en el camino crítico. En cambio, define el manejador opcional onAuthCheck. Se dispara en cada carga de página / con la identidad del espectador resuelta y devuelve ok, refresh (re-emite la cookie, opcionalmente con un nuevo TTL para la caducidad deslizante), o deny (finaliza la sesión y rebota a inicio de sesión). Un complemento respaldado por el proveedor revisa nuevamente la membresía aquí (¿organización sigue válida? ¿cuenta no eliminada?).

Debido a que la verificación solo se ejecuta en /, un espectador a quien revocas mantiene funcionando cualquier pestaña abierta hasta que se vuelve a cargar o la cookie expira. La TTL de la sesión es la última línea de defensa contra la revocación, así que mantenla corta si es importante la revocación rápida.

Ejemplo trabajado: una puerta de contraseña compartida

El complemento de basic-auth es la puerta más simple posible: una contraseña compartida, una identidad compartida de "Invitado", ningún proveedor externo. Se incluye tanto en examples/js/basic-auth como en examples/python/basic-auth.

Su manifiesto declara los permisos y un único campo de configuración para la contraseña:

{
"name": "Basic Auth",
"slug": "basic-auth",
"version": "0.1.0",
"permissions": ["auth.gate", "users.register", "http.serve", "storage.kv"],
"config": {
"password": {
"type": "string",
"default": "letmein",
"description": "Shared password viewers must enter to watch"
}
}
}

El manejador presenta un formulario de contraseña en /, verifica la contraseña enviada contra el valor configurado y, si tiene éxito, registra la identidad compartida, concede una sesión y redirige de vuelta. onAuthCheck lee una bandera revoked que se puede invertir por el administrador para expulsar a todos en su próxima carga de página. (El ayudante page() que construye el formulario HTML se omite a continuación por brevedad. Consulta el código fuente del ejemplo.)

const { definePlugin, owncast, authCheck } = require('@owncast/plugin-sdk');

module.exports = definePlugin({
onHttpRequest(req) {
const query = req.query || {};
const returnTo = query.return_to || '/';

if (req.method === 'GET' && req.path === '/') {
return {
status: 200,
headers: { 'content-type': 'text/html' },
body: page(returnTo),
};
}

if (req.path === '/login') {
const expected = owncast.config.get('password', 'letmein');
if ((query.password || '') !== expected) {
return {
status: 200,
headers: { 'content-type': 'text/html' },
body: page(returnTo, 'Incorrect password.'),
};
}
// Everyone who knows the password shares one authenticated identity.
const { userId } = owncast.users.register({
authId: 'shared',
displayName: 'Guest',
});
owncast.auth.grantSession({ userId });
return { status: 302, headers: { Location: returnTo } };
}

if (req.path === '/logout') {
owncast.auth.endSession();
return { status: 302, headers: { Location: '/' } };
}

// Admin-only revocation toggle. req.authenticated is true for admins only.
if (req.path === '/revoke' || req.path === '/unrevoke') {
if (!req.authenticated) return { status: 403, body: 'admin only' };
owncast.kv.set('revoked', req.path === '/revoke' ? '1' : '');
return {
status: 200,
body: req.path === '/revoke' ? 'revoked' : 'unrevoked',
};
}

return { status: 404, body: 'not found' };
},

// Re-validate on each page load. While revoked, end every session.
onAuthCheck() {
if (owncast.kv.get('revoked') === '1') return authCheck.deny('access has been revoked');
return authCheck.ok();
},
});

Para un flujo OAuth real (CSRF state en storage.kv, un intercambio de código a través de network.fetch, aplicación de membresía de organización, y un URL de callback construido a partir de owncast.server.info()), consulta el ejemplo github-auth en el SDK.

Habilitando la puerta

Declarar auth.gate no hace nada por sí solo. La puerta se activa al habilitar el complemento a través del ciclo de vida normal de habilitar/deshabilitar en el administrador. Desactivalo y la puerta cae instantáneamente.

  • Solo un complemento auth.gate puede estar habilitado a la vez. Owncast se niega a habilitar un segundo mientras uno ya esté activo ("deshabilita el otro primero").
  • Configura antes de habilitar. Un complemento puede ser instalado y configurado mientras está deshabilitado, luego habilitarse para entrar en funcionamiento. Usa el formulario de configuración autogenerado para credenciales como un ID de cliente OAuth y secreto.

Fallar cerrado

La postura de la puerta está desacoplada de la salud de tu complemento. Si la puerta está armada pero el complemento no está disponible (se bloqueó, falló al cargar, tuvo un error o se deshabilitó automáticamente después de fallos repetidos), Owncast niega todo el tráfico de los espectadores y sirve una página estática "autenticación temporalmente no disponible". Nunca se abre. El administrador siempre es accesible (las rutas de administrador utilizan la Autenticación Básica existente de Owncast y evitan la puerta) para que puedas corregir la configuración o deshabilitar el complemento. Las sesiones ya válidas sobreviven a una interrupción, porque verificar una cookie no necesita una llamada de complemento.

A gate that is enabled but not running is still a gate. No access-policy setting can turn a failing-closed gate into an open one.

Lo que elude la puerta

The gate covers the otherwise-public surface. Routes that enforce their own credentials bypass it. The selected access mode also leaves some resources public.

Always exempt:

  • The active gate plugin's own namespace /plugins/\<your-slug>/* and its static assets, so the login screen remains reachable.
  • /admin/* and /api/admin/*, which use admin authentication.
  • External API routes under /api/integrations/, which validate their own Bearer tokens.
  • Static viewer assets needed to render the page. HTML entry points are still gated.
  • /api/yp, which the Owncast Directory fetches anonymously. The most restrictive mode disables directory listing and makes this endpoint return 404.
  • /logo and /logo/external, the instance logo, which the viewer shell and federation metadata both reference.
  • /federation/*, the ActivityPub protocol surface. Those handlers enforce Owncast's own federation and privacy settings.

Mode-dependent:

  • /hls/* stays public only in Website only mode.
  • /api/status stays public in Website only and Website, video players, and other resources modes.

Everything else is gated, including embeds and /api/config.

Detalles de la sesión

  • Stateless signed cookie named owncast_session, HttpOnly, Secure (on HTTPS requests), SameSite=Lax, Path=/. Lax en lugar de Strict porque el callback del proveedor es una redirección a nivel superior entre sitios. The host owns the name: a plugin that tries to set it in its own response has that header stripped.
  • TTL is set by your plugin when it calls grantSession({ ttl }), defaulting to 24 hours and capped at 30 days. A sliding refresh is available through onAuthCheck's refresh verdict. Debido a que la TTL es el tope para la revocación, es un verdadero control de seguridad.
  • El secreto de firma es responsabilidad del host de Owncast. Se genera automáticamente en el primer uso y se persiste en la configuración. Rotarlo invalida cada sesión (un botón de pánico). Los autores de complementos nunca lo tocan, y está separado de cualquier secreto de cliente de OAuth, que es la preocupación de configuración de tu complemento.

Identidad de chat

Un inicio de sesión de puerta produce automáticamente una identidad de chat autenticada. Because users.register creates or links a real Owncast user (marked authenticated, with the display name you passed, or a generated one if you passed none) and the session cookie carries an access token for that user, chat reads the identity straight from the cookie: when /ws (or a chat REST call) arrives with no ?accessToken= query parameter, it falls back to the access token in the gate cookie. Ningún token se transporta nunca al localStorage del navegador. The viewer signs in once and shows up in chat under that name.

Relacionado

  • Permisos: auth.gate, users.register
  • APIs de Owncast: users.register, auth.grantSession, auth.endSession
  • Eventos: el manejador onAuthCheck
  • Sirviendo HTTP: el modelo de solicitud en el que se basa el flujo de inicio de sesión

Improve this page

See something missing or incorrect? Edit the English version of this page or help improve translations.

Contributors to this documentation