Nuxt Secure uses JSON Web Tokens (JWT) for stateless session management combined with Cloudflare Turnstile CAPTCHA to block automated login attempts. Sessions are stored in an HTTP cookie so they survive page refreshes and work during server-side rendering.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Israel-Perez/Nuxt-Secure/llms.txt
Use this file to discover all available pages before exploring further.
Login flow
User submits the login form
The login page collects
strNombreUsuario (username), strPwd (password), and a Turnstile token generated automatically by the @nuxtjs/turnstile widget when the user solves the challenge.Turnstile token is validated
The API handler posts the token to
https://challenges.cloudflare.com/turnstile/v0/siteverify using the server-side TURNSTILE_SECRET_KEY. If validation fails, the request is rejected with HTTP 400 before the database is ever queried.Database lookup by username
Drizzle ORM queries the
usuario table using eq(usuario.strNombreUsuario, strNombreUsuario). If no row is found, or the user’s idEstadoUsuario is false (inactive), HTTP 401 is returned.Password verification
bcrypt.compare() compares the submitted plain-text password against the hashed value stored in strPwd. A mismatch returns HTTP 401.JWT is issued
jwt.sign() creates a token containing { id, idPerfil, nombre } signed with JWT_SECRET. The token expires in 8 hours.Token stored in cookie
The client composable writes the token to the
auth_token cookie with maxAge: 60 * 60 * 8 (28 800 seconds). useCookie from Nuxt ensures the cookie is accessible both server-side and client-side.User data persisted
The user object (
id, nombre, idPerfil, correo, celular, imagenUrl) is serialised to localStorage under the key usuario and stored in useState('usuarioLogueado') for reactive access across all components.Permissions loaded
cargarMisPermisos(idPerfil) fetches /api/permisos/mis-permisos/:idPerfil and stores the result in useState('misPermisos') as a Record<string, PermisosAccion> keyed by module name in uppercase.Login handler source
server/api/auth/login.post.ts
Global route middleware
app/middleware/auth.global.ts runs before every route navigation — on the server during SSR and on the client during SPA navigation. It enforces three rules:
- Root redirect — visiting
/always redirects to/login. - Route protection — any route other than
/loginrequires a validauth_tokencookie; missing token redirects to/login. - Double-login prevention — a user who already has a token and tries to visit
/loginis sent to/principal-1instead.
app/middleware/auth.global.ts
Session persistence
Nuxt’suseState is reset when the page is hard-refreshed (F5). restaurarSesion() in useAuth.ts handles this by reading the user object back from localStorage and re-fetching permissions if the in-memory state is empty:
app/composables/useAuth.ts
restaurarSesion() in the onMounted hook of any page or layout that needs reactive user data after a refresh.
JWT token structure
| Field | Type | Description |
|---|---|---|
id | number | User’s database row ID |
idPerfil | number | Profile (role) ID — used to load permissions |
nombre | string | Username (strNombreUsuario) |
exp | number | Unix timestamp — automatically set to 8 hours from issue time |
JWT_SECRET environment variable via useRuntimeConfig().jwtSecret.
Security notes
- bcrypt hashing — passwords are never stored in plain text; bcrypt’s work factor makes brute-force attacks computationally expensive.
- Turnstile CAPTCHA — the Cloudflare challenge runs before any database query, blocking credential-stuffing bots without storing any user fingerprint.
- Short token lifetime — the 8-hour expiry limits the window of exposure if a token is leaked.
- Status check — inactive users (
idEstadoUsuario: false) are rejected at login without revealing which check failed.