DEMET Backend uses a robust JWT (JSON Web Token) authentication system with a dual-token strategy to provide both security and user convenience. Tokens are stored in HTTP-only cookies to protect against XSS attacks.
All authentication tokens are stored in HTTP-only cookies with secure and SameSite attributes, making them inaccessible to client-side JavaScript and protected against CSRF attacks.
The client sends email and password to /intern/login:
POST /intern/loginContent-Type: application/json{ "email": "[email protected]", "password": "123456"}
2
Server validates credentials
The server:
Looks up the employee by email
Compares the provided password with the hashed password in the database
Returns an error if credentials are invalid
//Servicio de Busqueda de Datos Segun Emailconst employeeData = await findEmail(email)//Verificar Resultado de la Busquedaif(!employeeData) return res.status(401).json({message : "Usuario No Encontrado"})//Servicio Comparar Contrasenhasconst match = await comparePassword(password, employeeData.password)//Verificar Resultado de la Comparacionif(!match) return res.status(401).json({message: "Contraseña Incorrecta"})
3
Server generates tokens
If credentials are valid, the server generates both access and refresh tokens:
//Servicio de Generacion de Tokensconst token = generateAccessToken(employeeData)const refreshToken = generateRefreshToken(employeeData)
4
Server sets cookies
The tokens are sent to the client as HTTP-only cookies:
//Envío de Tokens Mediante una Cookiesres.cookie("access_token", token, {httpOnly: false,secure: true, // obligatorio en producción (https)sameSite: "none" // permite enviar cookies cross-site});res.cookie("refresh_token", refreshToken, {httpOnly: false,secure: true,sameSite: "none"});
In production, secure: true ensures cookies are only sent over HTTPS. The sameSite: "none" attribute allows cross-site requests when credentials are included.
5
Client receives confirmation
The server responds with a success message:
{ "auth": true}
The cookies are automatically stored by the browser and sent with subsequent requests.
The verifyToken middleware validates the access token:
export const verifyToken = (req, res, next) => { //Obtener el token mediante la cookie const token = req.cookies.access_token; //Manejo de error, Sí no hay token: retornará status 401 if(!token) return res.status(401).send({auth:false, message: 'Token No Enviado'}); //Si hay token, verificar su validez jwt.verify(token, process.env.ACCESS_SECRET, async(err, decoded)=>{ //Manejo de error sí, la key no coincide if(err) return res.status(401).send({auth:false, message:'Token Invalido o Expirado'}); //Sí todo sale bien, se envían los datos en formato JSON req.user = decoded; next(); })}
The verifyRol middleware adds role-based access control:
export const verifyRol = (req, res, next) => { //Obtener el token mediante la cookie const token = req.cookies.access_token; //Manejo de error, Sí no hay token: retornará status 401 if(!token) return res.status(401).send({auth:false, message: 'Token No Enviado'}); //Si hay token, verificar su validez jwt.verify(token, process.env.ACCESS_SECRET, async(err, decoded)=>{ //Manejo de error sí, la key no coincide if(err) return res.status(401).send({auth:false, message:'Token Invalido o Expirado'}); //Sí todo sale bien, se envían los datos en formato JSON dentro de: decoded //Validar Rol de Usuario if(decoded.role != "Administrador") return res.status(401).send({auth:false, message:'Usuario/Rol No Autorizado', role: decoded.role}); //Asignar los datos en formato JSON en req.user req.user = decoded; //Dejarlo Seguir Sí es un Administrador next(); })}
This middleware performs the same verification as verifyToken, but also:
Checks if the user’s role is “Administrador”
Returns 401 if the user has insufficient permissions
//Asignar token a una Cookieres.cookie("access_token", token, {httpOnly: false,secure: true, // obligatorio en producción (https)sameSite: "none" // permite enviar cookies cross-site});
async function fetchWithAuth(url, options = {}) { let response = await fetch(url, { ...options, credentials: 'include' }); // If access token expired, refresh and retry if (response.status === 401) { await fetch('http://localhost:3002/intern/refresh', { credentials: 'include' }); // Retry original request response = await fetch(url, { ...options, credentials: 'include' }); } return response;}
6
Protect against CSRF
HTTP-only cookies with sameSite attribute provide CSRF protection. For additional security, consider implementing CSRF tokens for state-changing operations.