Autenticación

En esta página

EmDash utiliza autenticación con passkeys como su método principal de inicio de sesión. Los passkeys son resistentes al phishing, no requieren contraseñas y funcionan entre dispositivos a través de tu navegador o gestor de contraseñas.

Más allá de los passkeys, puedes agregar proveedores de inicio de sesión conectables. GitHub y Google están incluidos con EmDash. El proveedor Atmosphere instalado por separado agrega cuentas AT Protocol, y la misma interfaz de proveedor está abierta a otros paquetes. Los proveedores documentados de GitHub, Google y Atmosphere pueden crear la primera cuenta de administrador o iniciar sesión con un usuario EmDash vinculado.

Para despliegues en Cloudflare, Cloudflare Access es un modo de autenticación separado y exclusivo en producción. Valida credenciales de Access en rutas protegidas de EmDash en lugar de mostrar los métodos de inicio de sesión de EmDash.

Elegir un modo de autenticación

Los passkeys utilizan WebAuthn, un estándar web que crea credenciales de clave pública almacenadas en tu dispositivo o sincronizadas a través de tu gestor de contraseñas. Cuando inicias sesión, tu dispositivo demuestra la posesión de la credencial sin enviar nunca una contraseña por la red.

Los passkeys son el método predeterminado. Los proveedores de GitHub, Google y Atmosphere son métodos de inicio de sesión adicionales: cada uno autentica al usuario, vincula o crea una cuenta EmDash y establece la misma sesión EmDash utilizada por un inicio de sesión con passkey.

La autenticación con passkeys proporciona:

  • Sin contraseñas que recordar o filtrar
  • Resistente al phishing — las credenciales están vinculadas al dominio de tu sitio
  • Sincronización entre dispositivos — funciona con iCloud Keychain, Google Password Manager, 1Password, etc.
  • Inicio de sesión rápido — un toque con biometría o PIN

Cloudflare Access utiliza la opción auth en lugar de authProviders. En producción se convierte en la autoridad para las rutas protegidas /_emdash. EmDash sigue almacenando un usuario local para que los roles, la propiedad y las verificaciones de usuarios deshabilitados continúen funcionando.

Configurar el primer usuario

La primera vez que accedes al panel de administración, el Asistente de Configuración te guía para crear tu cuenta de administrador.

  1. Navega a http://localhost:4321/_emdash/admin

  2. En Set up your site, ingresa el título del sitio y el eslogan opcional. Una plantilla también puede ofrecer contenido de ejemplo. Selecciona Continue.

  3. En Create your account, ingresa tu dirección de correo electrónico y nombre opcional. Selecciona Continue.

  4. En Secure your account, crea un passkey o elige uno de los proveedores de inicio de sesión configurados. Si eliges un passkey, tu navegador pregunta dónde guardarlo:

    • En macOS: Touch ID, contraseña del dispositivo o llave de seguridad
    • En Windows: Windows Hello o llave de seguridad
    • En móvil: Face ID, huella digital o PIN
  5. Completa el flujo del navegador o proveedor. EmDash crea el primer usuario como Admin y abre el panel de control.

Iniciar sesión con un passkey

Después de la configuración, volver al panel de administración activa la autenticación con passkey:

  1. Visita /_emdash/admin

  2. Si no has iniciado sesión, verás la página de inicio de sesión

  3. Haz clic en Sign in para autenticarte

  4. Tu navegador solicita tu passkey (biometría, PIN o llave de seguridad)

  5. Después de la verificación, serás redirigido al panel de administración

Iniciar sesión con un enlace mágico

Si no puedes usar tu passkey, un enlace mágico proporciona una alternativa. El sitio debe tener un proveedor de correo electrónico configurado antes de que EmDash pueda enviar el enlace.

  1. En la página de inicio de sesión, haz clic en Sign in with email

  2. Ingresa tu dirección de correo electrónico

  3. Revisa tu bandeja de entrada para un enlace de inicio de sesión

  4. Haz clic en el enlace para autenticarte (válido por 15 minutos)

Configurar proveedores de inicio de sesión

Además de los passkeys, EmDash soporta proveedores de inicio de sesión conectables que aparecen en la página de inicio de sesión y en el asistente de configuración. GitHub y Google están incluidos con EmDash. Los proveedores de Atmosphere y de terceros son paquetes separados que se registran a través de la misma interfaz.

Los proveedores son aditivos — los passkeys siguen funcionando cuando los proveedores están habilitados. GitHub y Google vinculan automáticamente un usuario EmDash existente solo cuando el proveedor proporciona la misma dirección de correo verificada. Las cuentas Atmosphere se vinculan por su identificador descentralizado (DID), porque el flujo Atmosphere de EmDash no recibe una dirección de correo. Cada proveedor incluido puede crear el primer usuario, así que una instalación nueva puede omitir los passkeys por completo.

Agregar proveedores a Astro

Pasa los proveedores al array authProviders en la integración de EmDash. El siguiente ejemplo habilita GitHub, Google y Atmosphere:

import { defineConfig } from "astro/config";
import emdash from "emdash/astro";
import { github } from "emdash/auth/providers/github";
import { google } from "emdash/auth/providers/google";
import { atproto } from "@emdash-cms/auth-atproto";

export default defineConfig({
	integrations: [
		emdash({
			authProviders: [github(), google(), atproto()],
		}),
	],
});

El orden importa para la página de inicio de sesión: los proveedores se renderizan en el orden en que los listas, con proveedores compactos de solo botón primero y proveedores que necesitan un formulario personalizado (como Atmosphere, que pide un handle) después.

GitHub

El siguiente ejemplo habilita el proveedor de GitHub:

import { github } from "emdash/auth/providers/github";

emdash({ authProviders: [github()] });

Establece las credenciales mediante variables de entorno. EmDash verifica primero los nombres con prefijo y recurre a los sin prefijo:

VariablePropósito
EMDASH_OAUTH_GITHUB_CLIENT_ID / GITHUB_CLIENT_IDID de cliente de app OAuth
EMDASH_OAUTH_GITHUB_CLIENT_SECRET / GITHUB_CLIENT_SECRETSecreto de app OAuth

Configura la URL de callback de tu app OAuth de GitHub como https://your-site.example.com/_emdash/api/auth/oauth/github/callback.

Google

El siguiente ejemplo habilita el proveedor de Google:

import { google } from "emdash/auth/providers/google";

emdash({ authProviders: [google()] });

Establece las credenciales mediante variables de entorno. EmDash verifica primero los nombres con prefijo y recurre a los sin prefijo:

VariablePropósito
EMDASH_OAUTH_GOOGLE_CLIENT_ID / GOOGLE_CLIENT_IDID de cliente de app OAuth
EMDASH_OAUTH_GOOGLE_CLIENT_SECRET / GOOGLE_CLIENT_SECRETSecreto de app OAuth

Configura el URI de redirección de tu cliente OAuth de Google como https://your-site.example.com/_emdash/api/auth/oauth/google/callback.

Atmosphere (AT Protocol)

Para sitios donde los colaboradores ya tienen una cuenta Atmosphere — la identidad propiedad del usuario detrás de Bluesky y la red AT Protocol más amplia — instala el proveedor Atmosphere:

pnpm add @emdash-cms/auth-atproto

El siguiente ejemplo habilita el proveedor Atmosphere con una lista de handles permitidos:

import { atproto } from "@emdash-cms/auth-atproto";

emdash({
	authProviders: [
		atproto({
			allowedHandles: ["*.example.com"],
		}),
	],
});

No se necesita secreto de cliente ni variable de entorno. Consulta la guía de inicio de sesión Atmosphere para listas de handles/DIDs permitidos, asignación de roles y la configuración de desarrollo local que requiere el perfil OAuth de AT Protocol.

Crear un proveedor

Un proveedor es un AuthProviderDescriptor: un id, una etiqueta legible, y los componentes de administración, manejadores de rutas, prefijos de rutas públicas y colecciones de almacenamiento que necesita su flujo de inicio de sesión. Exporta un SetupStep desde adminEntry si el proveedor debe aparecer durante la configuración del primer usuario. La forma se exporta desde emdash:

import type { AuthProviderDescriptor } from "emdash";

export function myProvider(): AuthProviderDescriptor {
	return {
		id: "my-provider",
		label: "My Provider",
		adminEntry: "my-provider/admin", // exporta LoginButton / LoginForm / SetupStep
		routes: [
			{ pattern: "/_emdash/api/auth/my-provider/login", entrypoint: "my-provider/routes/login.ts" },
			{ pattern: "/_emdash/api/auth/my-provider/callback", entrypoint: "my-provider/routes/callback.ts" },
		],
		publicRoutes: ["/_emdash/api/auth/my-provider/"],
		storage: {
			sessions: {},
		},
	};
}

El paquete Atmosphere (@emdash-cms/auth-atproto) es la referencia real más completa para un proveedor que necesita un formulario de inicio de sesión personalizado, manejadores de rutas OAuth y almacenamiento persistente.

Roles de usuario

EmDash utiliza control de acceso basado en roles con cinco niveles:

RolNivelDescripción
Subscriber10Leer contenido publicado (sin acceso a borradores)
Contributor20Crear contenido (necesita aprobación para publicar)
Author30Crear/editar/publicar contenido propio
Editor40Gestionar todo el contenido
Admin50Acceso completo incluyendo configuraciones

Cada rol hereda permisos de todos los niveles inferiores. El primer usuario siempre se crea como Admin.

Subscribers y contenido en borrador

Los Subscribers tienen el permiso content:read para que el contenido publicado solo para miembros pueda servirse a lectores autenticados. No pueden ver borradores, elementos programados, elementos eliminados, revisiones o URLs de vista previa — estos están controlados por content:read_drafts, otorgado a Contributor y superiores. Los endpoints de lista y obtención filtran transparentemente a status=published para Subscribers; las vistas solo para editores (/compare, /revisions, /trash, /preview-url) rechazan directamente las solicitudes de Subscribers.

Invitar usuarios

Los admins pueden invitar nuevos usuarios a través del panel de administración:

  1. Ve a Settings > Users

  2. Haz clic en Invite User

  3. Ingresa el correo del usuario y selecciona un rol

  4. Haz clic en Send Invite

  5. Si el correo está configurado, EmDash envía la invitación. De lo contrario, copia el enlace generado y envíalo tú mismo al usuario.

  6. El usuario abre el enlace y crea la cuenta con un passkey o un proveedor de inicio de sesión ofrecido en la página de invitación.

Los enlaces de invitación son de un solo uso y expiran después de 7 días.

Gestionar passkeys

Los usuarios pueden gestionar sus passkeys desde la configuración de la cuenta:

  • Agregar passkey — Registrar passkeys adicionales como respaldo o para otros dispositivos
  • Eliminar passkey — Eliminar passkeys que ya no usas
  • Renombrar passkey — Dar nombres descriptivos a los passkeys

Cada usuario puede tener hasta 10 passkeys registrados.

EmDash no permite que un usuario elimine su último passkey. Agrega un reemplazo antes de eliminar el antiguo.

Permitir que un grupo inicie sesión sin invitaciones

Para permitir que un grupo inicie sesión sin invitar a cada usuario, configura un proveedor de inicio de sesión con una lista de permitidos. El proveedor Atmosphere acepta allowedHandles y allowedDIDs (ver inicio de sesión Atmosphere); el adaptador de Cloudflare Access aprovisiona usuarios desde tu proveedor de identidad a través de autoProvision y roleMapping. Los proveedores documentados de GitHub, Google y Atmosphere también pueden crear la cuenta de administrador inicial.

Sesiones

Los callbacks de passkey, enlace mágico, invitación y proveedor de inicio de sesión almacenan el ID de usuario EmDash en el almacén de sesiones de Astro. El navegador recibe el identificador opaco astro-session de Astro; los registros de usuario y credenciales permanecen en la base de datos de EmDash.

Cloudflare Access también escribe el usuario EmDash resuelto en la sesión de Astro. Esto permite que las páginas públicas identifiquen a un usuario conectado cuando leen Astro.locals.user. La sesión no reemplaza la autenticación de Access en las rutas protegidas /_emdash: EmDash valida el JSON Web Token (JWT) de Access nuevamente en esas solicitudes.

Límites de tasa de autenticación

EmDash limita los endpoints que inician flujos de inicio de sesión o registro no autenticados. Los límites son separados para cada endpoint e IP de cliente de confianza:

EndpointLímite
POST /_emdash/api/auth/passkey/options10 solicitudes por minuto
POST /_emdash/api/auth/magic-link/send3 solicitudes por 5 minutos
POST /_emdash/api/auth/signup/request3 solicitudes por 5 minutos

En Cloudflare, EmDash lee la IP del cliente de los metadatos de solicitud de Cloudflare. Un sitio autoalojado detrás de un proxy inverso debe configurar trustedProxyHeaders antes de que EmDash pueda usar el encabezado de IP del cliente del proxy. Cuando no hay una IP de confianza disponible, estas verificaciones por IP se omiten porque no hay una clave segura para contar.

Los passkeys almacenan credenciales de clave pública; la clave privada permanece con el autenticador del usuario. Los tokens de enlace mágico se almacenan como hashes SHA-256 y se eliminan después del uso.

Solución de problemas

”No passkeys registered”

Si ves este error al iniciar sesión, tu passkey puede haber sido eliminado de tu gestor de contraseñas. Pide a un admin que envíe un enlace mágico de recuperación; el sitio debe tener correo configurado.

”Passkey authentication failed”

Esto generalmente significa que el passkey fue creado para un dominio diferente. Los passkeys están vinculados al dominio — un passkey para localhost:4321 no funcionará en example.com. Registra un nuevo passkey para cada dominio.

Perdidos todos los passkeys

Si has perdido acceso a todos tus passkeys registrados:

  1. Pide a otro admin que envíe un enlace mágico de recuperación. El sitio debe tener correo configurado.
  2. Usa el enlace dentro de 15 minutos para iniciar sesión.
  3. Registra un nuevo passkey en la configuración de la cuenta.

Si eres el único admin y el correo no está configurado, necesitarás restablecer la autenticación de tu sitio a través de la base de datos.

Cloudflare Access

Al desplegar en Cloudflare, puedes usar Cloudflare Access en lugar de los métodos de inicio de sesión integrados. Access autentica al usuario en el borde con tu proveedor de identidad. EmDash valida el JWT de Access firmado, carga la identidad y grupos de la persona, y mapea esa identidad a un usuario EmDash local.

Cuándo usar Cloudflare Access

  • Single Sign-On — Los usuarios se autentican con el IdP de tu empresa
  • Control de acceso centralizado — Gestiona quién puede acceder al admin en el panel de Cloudflare
  • Sin gestión de passkeys — No es necesario registrar ni gestionar passkeys
  • Roles basados en grupos — Mapea grupos del IdP a roles de EmDash automáticamente

Configurar Access

  1. Crea una aplicación y política de Cloudflare Access para la ruta /_emdash/* de tu sitio. Proteger solo /_emdash/admin/* deja la API REST sin el JWT que EmDash espera.
  2. Copia el Application Audience (AUD) Tag de la aplicación.
  3. Almacena el tag en la variable de entorno de ejecución CF_ACCESS_AUDIENCE. Sigue la guía de secretos de EmDash para valores locales y desplegados.
  4. Configura EmDash para leer ese valor en tiempo de ejecución:
import { defineConfig } from "astro/config";
import cloudflare from "@astrojs/cloudflare";
import emdash from "emdash/astro";
import { d1, access } from "@emdash-cms/cloudflare";

export default defineConfig({
	output: "server",
	adapter: cloudflare(),
	integrations: [
		emdash({
			database: d1({ binding: "DB" }),
			auth: access({
				teamDomain: "myteam.cloudflareaccess.com",
				audienceEnvVar: "CF_ACCESS_AUDIENCE",
			}),
		}),
	],
});

La audiencia de la aplicación identifica qué aplicación de Access emitió el JWT. EmDash la verifica junto con el emisor y la firma; un token para otra aplicación de Access es rechazado.

Opciones de configuración

OpciónTipoPredeterminadoDescripción
teamDomainstringrequeridoTu dominio de equipo Access (ej., myteam.cloudflareaccess.com)
audiencestringApplication Audience (AUD) tag proporcionado directamente. Prefiere audienceEnvVar en Workers.
autoProvisionbooleantrueCrear usuarios EmDash en el primer inicio de sesión con Access
defaultRolenumber30Rol para usuarios que no coinciden con ningún grupo (30 = Author)
syncRolesbooleanfalseActualizar rol en cada inicio de sesión basado en grupos del IdP
roleMappingobjectMapear nombres de grupos del IdP a niveles de rol
audienceEnvVarstring"CF_ACCESS_AUDIENCE"Variable de entorno que contiene el tag de audiencia. Se usa cuando se omite audience.

Proporciona audience o un valor de entorno bajo audienceEnvVar.

Mapeo de roles

Mapea tus grupos del IdP a roles de EmDash:

emdash({
	auth: access({
		teamDomain: "myteam.cloudflareaccess.com",
		audienceEnvVar: "CF_ACCESS_AUDIENCE",
		roleMapping: {
			Admins: 50, // Admin
			"Content Editors": 40, // Editor
			Writers: 30, // Author
		},
		defaultRole: 20, // Contributor para usuarios no en ningún grupo
	}),
});

El primer grupo que coincida gana si un usuario pertenece a múltiples grupos. El primer usuario en acceder al sitio siempre se convierte en Admin, independientemente de los grupos.

Comportamiento de sincronización de roles

Por defecto (syncRoles: false), el rol de un usuario se establece cuando inicia sesión por primera vez y no cambia después. Esto permite a los admins ajustar roles manualmente en EmDash.

Establece syncRoles: true si quieres que los grupos del IdP sean autoritativos — el rol del usuario se actualizará en cada inicio de sesión basado en sus grupos actuales.

Flujo de solicitud y sesión

  1. El usuario visita una ruta protegida por la aplicación de Access.
  2. Cloudflare Access redirige al usuario a tu proveedor de identidad cuando no existe una sesión de Access.
  3. Después de la autenticación, Access envía un JWT firmado al origen en Cf-Access-Jwt-Assertion.
  4. EmDash valida la firma, el emisor y la audiencia del token, luego lee la identidad y grupos de Access.
  5. EmDash encuentra o aprovisiona el usuario local, aplica el comportamiento de rol configurado y registra al usuario en la sesión de Astro.
  6. Las solicitudes posteriores a rutas protegidas de EmDash repiten la validación de Access. Las páginas públicas pueden usar la sesión de EmDash para identificar al usuario sin tratarla como prueba de una nueva solicitud de Access.

Funciones reemplazadas por Access

Cuando Access está habilitado, estas funciones no están disponibles:

  • Página de inicio de sesión (/_emdash/admin/login)
  • Registro y gestión de passkeys
  • Inicio de sesión con GitHub, Google y Atmosphere
  • Inicio de sesión con enlace mágico
  • Autorregistro
  • Invitaciones de usuario

Las políticas de Access deciden quién llega a EmDash. EmDash sigue siendo dueño de los roles locales, la propiedad del contenido y la bandera de usuario deshabilitado. Con syncRoles: false, los administradores pueden cambiar el rol de un usuario aprovisionado en EmDash. Con syncRoles: true, los grupos de Access mapeados reemplazan ese rol en cada inicio de sesión.

Solución de problemas

”No Access JWT present”

La solicitud llegó a EmDash sin un JWT de Access. Esto significa:

  • Access no está configurado para proteger tu aplicación
  • La política de Access no está coincidiendo con las rutas de administración

Verifica que la aplicación de Access cubra la ruta completa /_emdash/* y que su política incluya al usuario.

”JWT audience mismatch”

La audience en tu configuración no coincide con el JWT. Verifica el Application Audience Tag en la configuración de tu aplicación de Access.

”User not authorized”

El usuario se autenticó a través de Access pero autoProvision es false y no existe en EmDash. Opciones:

  • Establece autoProvision: true, o
  • Crea el usuario manualmente antes de que inicie sesión