No hace falta poner contenido protegido directamente en el HTML y ocultarlo con JavaScript, porque cualquiera con las herramientas de desarrollador puede leerlo. En su lugar, puedes mover ese contenido detrás de una Webflow Cloud App que valide primero el token en el lado del servidor.
La función de Cuentas de usuario de Webflow dejó de estar disponible el 29 de enero de 2026, y las API y webhooks dedicados a las Cuentas de usuario se eliminaron de la API de datos de Webflow ese mismo día. Los sitios creados con contenido restringido ahora necesitan una capa de identidad de terceros.
Auth0 se encarga de la identidad, pero algunos tutoriales lo conectan con el SDK de SPA y localStorage, para luego usar JavaScript en la página de Webflow con el fin de mostrar u ocultar el contenido protegido. Ese patrón no es seguro: el contenido está en el DOM antes de que se ejecute el script, y cualquiera con las herramientas de desarrollador puede leerlo.
En esta guía, trasladamos el contenido protegido a los endpoints de una Webflow Cloud App y validamos el token de Auth0 en el servidor antes de que nada llegue al navegador: el contenido obtiene la información de autenticación de un usuario conectado, la envía a una Webflow Cloud App y la aplicación la verifica con Auth0.
¿Qué necesitas para añadir la autenticación de Auth0 a un sitio de Webflow?
Necesitas una cuenta de Auth0 configurada como una aplicación web regular (Regular Web Application), una Webflow Cloud App y tres variables de entorno antes de escribir una sola línea de código.
Estos son los requisitos previos:
- Unacuenta de Auth0: créala en auth0.com si aún no tienes una. En el panel de control de Auth0, crea una aplicación web regular (Regular Web Application). Esta aplicación genera un secreto de cliente que el servidor utiliza para intercambiar el código de autenticación por tokens en el lado del servidor.
- UnaWebflow Cloud App ya creada: usando Next.js (esta guía utiliza el App Router de Next.js, pero el mismo enfoque funciona con Astro); ejecuta
webflow auth login && webflow cloud initsi aún no lo has hecho. - Acceso a código personalizado en tu sitio de Webflow: para configurar los botones de inicio y cierre de sesión en el último paso. Disponible en cualquier plan de sitio de pago.
Una vez que tengas todo esto listo, puedes pasar a la configuración.
5 pasos para añadir la autenticación de Auth0 a un sitio de Webflow con Webflow Cloud
La configuración se divide claramente en tres sistemas:
- Auth0 se encarga de la identidad: inicio de sesión alojado, emisión de sesiones y firma de tokens.
- La Webflow Cloud App se encarga del proceso OAuth y de los datos protegidos: intercambia códigos por tokens, valida los JWT en cada solicitud y sirve el contenido restringido solo cuando la validación es correcta.
- El sitio de Webflow se encarga de la interfaz: botones de inicio y cierre de sesión, además de un cliente ligero que llama a la Cloud App para leer el estado de autenticación y renderizar el contenido según corresponda.
Exploremos los cinco pasos a continuación para configurar esas capas en orden.
Empieza por Auth0 para obtener las tres credenciales que introducirás en Webflow Cloud en el paso 2.
1. Configura la aplicación web regular (Regular Web Application) de Auth0
En el panel de control de Auth0, abre tu Regular Web Application y dirígete a Settings. Necesitas configurar tres campos de URL y copiar tres valores.
En Application URIs, completa lo siguiente:
Allowed Callback URLs:
http://localhost:3000/app/auth/callback, https://yoursite.webflow.io/app/auth/callback, https://yourdomain.com/app/auth/callback
Allowed Logout URLs:
http://localhost:3000, https://yoursite.webflow.io, https://yourdomain.com
Allowed Web Origins:
http://localhost:3000, https://yoursite.webflow.io, https://yourdomain.com
Registra tanto tu .webflow.io dominio de pruebas (staging) como tu dominio personalizado de producción en cada campo. Cada URL debe coincidir exactamente, incluyendo el protocolo y la ruta. Si Auth0 recibe una URL de callback que no está registrada aquí, rechazará la solicitud.
Haz clic en Guardar cambios.
Luego, sin salir de la pestaña Settings, copia los valores de Domain, Client ID y Client Secret. Estos se añadirán a tu entorno de Cloud App en el siguiente paso.
2. Añade las credenciales de Auth0 a tu entorno de Webflow Cloud
Tu Cloud App necesita cuatro variables de entorno.
Añádelas a .env.local para el desarrollo local:
# .env.local
AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_CLIENT_ID=your_client_id_here
AUTH0_CLIENT_SECRET=your_client_secret_here
AUTH0_CALLBACK_URL=http://localhost:3000/app/auth/callback
En la configuración de tu sitio Webflow, abre Webflow Cloud, selecciona tu entorno y añade las mismas cuatro variables en Environment Variables. AUTH0_CLIENT_SECRET le da a cualquiera que la posea la capacidad de intercambiar códigos de autenticación por tokens en tu nombre. Mantenla fuera del código del lado del cliente, del historial de git y de cualquier punto de conexión público.
Punto de control: Ejecuta next dev localmente y confirma que process.env.AUTH0_DOMAIN se resuelve.
3. Crea los controladores de ruta (Route Handlers) de autenticación
Necesitas crear tres controladores de ruta:
- Uno para iniciar el flujo de inicio de sesión
- Uno para gestionar el callback de Auth0
- Uno para cerrar la sesión de los usuarios
Estas son las únicas tres rutas que necesitan comunicarse directamente con Auth0.
Crea lib/auth.ts primero con el asistente de verificación JWT que usarás en cada punto de conexión protegido:
// lib/auth.ts
import { createRemoteJWKSet, jwtVerify, type JWTPayload } from 'jose'
const domain = process.env.AUTH0_DOMAIN!
export const JWKS = createRemoteJWKSet(
new URL(`https://${domain}/.well-known/jwks.json`)
)
export async function verifyToken(token: string): Promise<JWTPayload> {
const { payload } = await jwtVerify(token, JWKS, {
issuer: `https://${domain}/`,
audience: process.env.AUTH0_CLIENT_ID,
})
return payload
}
export function getTokenFromCookie(cookieHeader: string | null): string | null {
if (!cookieHeader) return null
const match = cookieHeader.match(/auth_token=([^;]+)/)
return match ? decodeURIComponent(match[1]) : null
}
createRemoteJWKSet obtiene las claves públicas de Auth0 una vez y las almacena en caché por aislamiento. jwtVerify valida la firma, el emisor y la audiencia del token sin necesidad de realizar llamadas de red externas por cada solicitud después de la obtención inicial de la clave.
Uso jose aquí porque es una de las pocas bibliotecas JWT diseñadas específicamente para entornos de ejecución en el borde (edge runtimes). Bibliotecas como jsonwebtoken dependen de APIs de Node.js que están restringidas o no disponibles en el entorno Edge proporcionado por Cloudflare Workers. jose utiliza la Web Crypto API de forma nativa, por lo que funciona sin necesidad de polyfills ni flags de compatibilidad.
Ahora crea los tres controladores de ruta:
// app/auth/login/route.ts
export const runtime = 'edge'
export async function GET() {
const params = new URLSearchParams({
response_type: 'code',
client_id: process.env.AUTH0_CLIENT_ID!,
redirect_uri: process.env.AUTH0_CALLBACK_URL!,
scope: 'openid profile email',
})
return Response.redirect(
`https://${process.env.AUTH0_DOMAIN}/authorize?${params}`
)
}
La ruta de inicio de sesión simplemente construye la URL de /authorize de Auth0 con los parámetros de consulta correctos y redirige el navegador hacia ella. A partir de ahí, Auth0 se encarga de la interfaz de inicio de sesión. No se expone ningún secreto de cliente porque este no se utiliza en esta parte del flujo.
Gestión de la devolución de llamada de Auth0
Cuando el usuario termina de iniciar sesión en Auth0, el navegador es redirigido a tu URL de devolución de llamada con un parámetro de consulta de autorización. El gestor de devolución de llamada intercambia ese código por tokens utilizando tu secreto de cliente y, a continuación, almacena el token de ID en una cookie.codehttpOnly
El siguiente código realiza ese intercambio:
// app/auth/callback/route.ts
export const runtime = 'edge'
export async function GET(request: Request) {
const url = new URL(request.url)
const code = url.searchParams.get('code')
if (!code) {
return Response.redirect(new URL('/auth/login', request.url))
}
// Exchange the authorization code for tokens
const tokenRes = await fetch(
`https://${process.env.AUTH0_DOMAIN}/oauth/token`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
grant_type: 'authorization_code',
client_id: process.env.AUTH0_CLIENT_ID,
client_secret: process.env.AUTH0_CLIENT_SECRET,
code,
redirect_uri: process.env.AUTH0_CALLBACK_URL,
}),
}
)
if (!tokenRes.ok) {
console.error('Token exchange failed:', await tokenRes.text())
return Response.redirect(new URL('/?error=auth_failed', request.url))
}
const tokens = (await tokenRes.json()) as { id_token: string }
// Store the ID token in an httpOnly cookie
const cookieValue = encodeURIComponent(tokens.id_token)
return new Response(null, {
status: 302,
headers: {
Location: '/',
'Set-Cookie': `auth_token=${cookieValue}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=86400`,
},
})
}
El intercambio de tokens ocurre en el lado del servidor, por lo que el secreto de cliente nunca llega al navegador. El token de ID aparece en la respuesta JSON y se establece como una cookie con las banderas , , y . El usuario regresa a la raíz del sitio con una sesión activa.HttpOnlySecureSameSite=Lax
Gestión del cierre de sesión
El cierre de sesión debe hacer dos cosas: borrar la cookie de sesión local y decirle a Auth0 que finalice su propia sesión; de lo contrario, el siguiente clic en iniciar sesión volverá a autenticar al mismo usuario de forma silenciosa.
Este gestor hace ambas cosas:
// app/auth/logout/route.ts
export const runtime = 'edge'
export async function GET(request: Request) {
const returnTo = encodeURIComponent(new URL('/', request.url).toString())
const logoutUrl = `https://${process.env.AUTH0_DOMAIN}/v2/logout?client_id=${process.env.AUTH0_CLIENT_ID}&returnTo=${returnTo}`
return new Response(null, {
status: 302,
headers: {
Location: logoutUrl,
'Set-Cookie': 'auth_token=; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=0',
},
})
}
Almaceno el token de ID en una cookie. El JavaScript en la página de Webflow no puede leerlo. Solo pueden hacerlo los gestores de rutas del lado del servidor de la Cloud App. Es por esto que este enfoque es seguro, a diferencia del patrón SPA SDK + localStorage: el token nunca toca el código del lado del cliente.httpOnly
Punto de control: Inicia el servidor de desarrollo y navega a . Deberías ser redirigido a la página de inicio de sesión de Auth0. Después de autenticarte, deberías volver a tu aplicación con una cookie establecida.http://localhost:3000/app/auth/loginauth_token
4. Protege los puntos finales de contenido de tu Cloud App
Cualquier gestor de rutas que devuelva contenido que requiera inicio de sesión debe verificar el token antes de responder. Esta es la comprobación en la capa de datos que hace que la protección sea real. El middleware por sí solo puede ser evadido en ciertas condiciones. , una vulnerabilidad de omisión de autorización en Next.js revelada en marzo de 2025, demostró exactamente esto: un encabezado x-middleware-subrequest manipulado omitía el middleware por completo.
La verificación debe ocurrir en el punto donde se accede a los datos.
Aquí tienes un ejemplo de un punto final protegido que devuelve datos específicos del usuario:
// app/api/me/route.ts
export const runtime = 'edge'
import { verifyToken, getTokenFromCookie } from '@/lib/auth'
export async function GET(request: Request) {
const token = getTokenFromCookie(request.headers.get('cookie'))
if (!token) {
return Response.json({ error: 'Unauthorized' }, { status: 401 })
}
try {
const payload = await verifyToken(token)
return Response.json({
name: payload.name,
email: payload.email,
sub: payload.sub,
})
} catch {
return Response.json({ error: 'Invalid session' }, { status: 401 })
}
}
Para cualquier gestor de rutas que sirva contenido restringido, sigue el mismo patrón: llama a , llama a , y devuelve un 401 si alguno de los pasos falla. El contenido solo se envía si el JWT es válido.getTokenFromCookie()verifyToken()
También puedes añadir una capa de middleware en el borde (edge) como una comprobación secundaria para la protección a nivel de ruta.
Next.js 16 renombró a , pero solo se ejecuta en el entorno de ejecución de Node.js, el cual Webflow Cloud no admite para middleware. Sigue usando con hasta que el adaptador de Webflow Cloud añada soporte para middleware en el entorno de ejecución de Node.js:middleware.tsproxy.tsproxy.tsmiddleware.tsexport const runtime = 'edge'
// middleware.ts
export const runtime = 'edge'
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { getTokenFromCookie, verifyToken } from '@/lib/auth'
export async function middleware(request: NextRequest) {
// Protect everything under /app/api/protected/*
if (request.nextUrl.pathname.startsWith('/app/api/protected')) {
const token = getTokenFromCookie(request.headers.get('cookie'))
if (!token) {
return NextResponse.redirect(new URL('/app/auth/login', request.url))
}
try {
await verifyToken(token)
} catch {
return NextResponse.redirect(new URL('/app/auth/login', request.url))
}
}
return NextResponse.next()
}
export const config = {
matcher: ['/app/api/protected/:path*'],
}
El middleware es un primer filtro útil, pero no es la única puerta. Verifica siempre de nuevo en el propio gestor de rutas.
Punto de control: Con el servidor en marcha, llama a sin la cookie (curl sin cookies). Deberías obtener un 401. Luego, inicia sesión a través de , copia el valor de la cookie y vuelve a llamar al punto final con la cookie. Deberías recibir tu perfil de usuario de Auth0.GET /app/api/me/app/auth/loginauth_token
5. Conecta el inicio y cierre de sesión en el sitio de Webflow
Añade esto a un bloque de Código personalizado (código del pie de página, antes de ) en cualquier página que tenga botones de inicio o cierre de sesión. Los botones necesitan IDs: para la acción de inicio de sesión y para la acción de cierre de sesión.</body>login-btnlogout-btn
Para las páginas donde quieras mostrar el nombre del usuario autenticado o recuperar contenido protegido, el patrón es: llama a la API de Cloud App con la cookie (el navegador la envía automáticamente) y renderiza lo que recibas.
Aquí tienes el script. Pégalo en el campo de código del pie de página (Footer code) en la configuración del sitio → Código personalizado (o en la configuración de la página si es un sitio de una sola página):
(function () {
const loginBtn = document.getElementById('login-btn')
const logoutBtn = document.getElementById('logout-btn')
const userDisplay = document.getElementById('user-display')
// Wire login and logout buttons
if (loginBtn) {
loginBtn.addEventListener('click', function () {
window.location.href = '/app/auth/login'
})
}
if (logoutBtn) {
logoutBtn.addEventListener('click', function () {
window.location.href = '/app/auth/logout'
})
}
// Fetch user info from the authenticated Cloud App endpoint
// The browser sends the httpOnly auth_token cookie automatically
async function loadUserInfo() {
try {
const res = await fetch('/app/api/me', { credentials: 'include' })
if (!res.ok) {
// Not authenticated: show login state
if (loginBtn) loginBtn.style.display = 'block'
if (logoutBtn) logoutBtn.style.display = 'none'
return
}
const user = await res.json()
// Authenticated: update UI
if (loginBtn) loginBtn.style.display = 'none'
if (logoutBtn) logoutBtn.style.display = 'block'
if (userDisplay) userDisplay.textContent = `Hi, ${user.name}`
} catch {
// Network error: show login state as fallback
if (loginBtn) loginBtn.style.display = 'block'
if (logoutBtn) logoutBtn.style.display = 'none'
}
}
loadUserInfo()
})()
Esto es distinto al patrón de mostrar/ocultar con JS. La página de Webflow no contiene el nombre del usuario ni datos protegidos al cargarse. El JavaScript llama a la API de Cloud App, la API valida la cookie en el servidor y solo entonces los datos regresan al navegador para ser renderizados.
Si la cookie falta o ha caducado, la API devuelve un código de estado 401 y la página muestra un estado de no autenticado. Ningún contenido sensible estuvo nunca en el DOM.
Punto de control: Publica tu sitio de Webflow. Haz clic en el botón de inicio de sesión, autentícate con Auth0 y confirma que regresas al sitio con el botón de cierre de sesión visible y tu nombre en pantalla. Haz clic en cerrar sesión y confirma que vuelves al estado de inicio de sesión.
¿Qué causa que la autenticación de Auth0 falle en una Cloud App de Webflow?
La mayoría de los fallos se deben a una de estas tres causas:
- URLs de callback mal configuradas en Auth0
- Una discrepancia en la audiencia del token durante el paso de verificación JWT
- Lectura de variables de entorno que no se inyectaron realmente en el entorno de Webflow Cloud
Las dos primeras son responsables de casi todos los fallos de autenticación en la práctica.
Veamos cómo identificar y solucionar cada una.
Auth0 rechaza el callback con "callback URL mismatch"
Esto significa que la URL a la que tu aplicación redirige después de iniciar sesión no está registrada en el campo de URLs de callback permitidas (Allowed Callback URLs).
Comprueba dos cosas:
- La
AUTH0_CALLBACK_URLvariable de entorno coincide con la URL que registraste en Auth0 - Tanto el
.webflow.iodominio de pruebas (staging) como el de producción están registrados
Un error común es registrar uno pero no el otro antes de probar en el sitio en vivo.
La verificación del token arroja "jwt audience invalid"
Esto significa que el audience parámetro en jwtVerify() no coincide con la aud declaración (claim) en el token. La audiencia debe ser tu ID de cliente de Auth0, no el identificador de tu API (si tienes uno configurado).
Verifica que AUTH0_CLIENT_ID en tu entorno coincida con el campo Client ID en el panel de control de Auth0 bajo tu Regular Web Application, y no con ninguna otra aplicación de tu tenant.
Conecta Auth0 al resto de tu desarrollo en Webflow
Una vez configurada la autenticación, la misma Webflow Cloud App puede hacer mucho más que restringir contenido. El token de ID validado en cada solicitud contiene la sub del usuario, que es un identificador estable que puedes vincular con la API de CMS de Webflow, los registros de clientes de Stripe o cualquier API de terceros para construir una capa de membresía real sobre ella.
Un paso natural a seguir es el acceso basado en roles. Las Auth0 Actions pueden inyectar claims personalizados en el token de ID al iniciar sesión, y tu verifyToken() helper ya devuelve el payload completo, por lo que añadir una verificación de rol es cuestión de añadir unas pocas líneas a cada manejador de rutas protegidas.
Explora Webflow + Auth0 para conocer opciones de integración específicas de Webflow más allá de esta guía, incluyendo el widget Auth0 Lock y opciones de plataformas de automatización para la sincronización del CMS.
Preguntas frecuentes
¿Puedo usar Auth0 con el contenido del CMS de Webflow?
Las páginas del CMS de Webflow son HTML estático. Cualquiera con la URL puede acceder a ellas. Si quieres restringir contenido del CMS, mantenlo en un almacén de datos separado (Airtable, una base de datos o un CMS headless) y sírvelo desde un endpoint de Cloud App protegido que valide la autenticación antes de devolver los datos. La página de Webflow es el envoltorio público; el endpoint de la Cloud App es la puerta.
¿Cómo añado el inicio de sesión social con Google o GitHub?
En el panel de control de Auth0, navega a Authentication → Social y activa Google OAuth2 o GitHub. Configura tus credenciales de OAuth en los ajustes de cada conexión. Una vez activadas, esas opciones de inicio de sesión aparecerán automáticamente en la página de Universal Login de Auth0. No se requieren cambios en la Cloud App. El flujo de inicio de sesión, callback y cierre de sesión es idéntico independientemente del proveedor que elija el usuario.
¿Cómo muestro contenido diferente a usuarios con distintos roles?
Auth0 admite roles a través de su función RBAC. Asigna roles a los usuarios en el panel de control de Auth0 y luego añade una Auth0 Action que inyecte un claim de espacio de nombres personalizado en el token de ID. Tu Cloud App lee ese claim después de verifyToken() y devuelve contenido diferente según los roles del usuario. Este es el mismo patrón basado en jose extendido con una verificación de claim.
¿Qué sucede cuando caduca la cookie auth_token?
La cookie se establece con Max-Age=86400 (24 horas). Tras la caducidad, la siguiente llamada a /app/api/me devuelve un 401 y la página de Webflow muestra el estado de no autenticado. El usuario vuelve a hacer clic en iniciar sesión y realiza todo el flujo de OAuth. Para sesiones de larga duración, puedes emitir un token de refresco añadiendo offline_access al scope de OAuth y almacenando el token de refresco en el lado del servidor. Eso requiere un almacén de sesiones en el lado del servidor, lo cual es una implementación aparte.




