Yo diria que es un 403, no tiene permisos de acceso. 401 yo lo uso solo para si es sesion o no.
Introducción
Cómo autenticar usuarios con Node.js
Autenticación vs. autorización
Tienda en línea: instalación del proyecto
Protección de contraseñas
Middleware de verificación
Hashing de contraseñas con bcryptjs
Implementando hashing para usuarios
Passport y JSON Web Tokens
Implemetando login con Passport.js
¿Qué es un JWT?
Firmar y verificar tokens
Generar JWT en el servicio
Protección de rutas
Control de roles
Obteniendo órdenes del perfil
Manejo de la autenticación desde el cliente
Envío de emails con Node.js
Cómo enviar emails con Node.js
Implementando el envío de emails
Recuperación de contraseñas
Generando links de recuperación
Validando tokens para cambio de contraseña
Despliegue a producción
Deploy en Heroku
Próximos pasos
Cómo seguir aprendiendo backend con Node.js
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
Aportes 11
Preguntas 5
Yo diria que es un 403, no tiene permisos de acceso. 401 yo lo uso solo para si es sesion o no.
Se debe trabajar en la gestión de permisos y roles ya que no todos deben poder crear categorías o crear usuarios, únicamente un usuario administrador podría hacer eso.
Se crea un middleware que verifique que tipo de rol es, y lo que lo deje seguir o no.
En auth.handler.js
se crea la función checkAdminRole
la cual verificará si el rol del usuario es admin o customer, si es admin entonces pasa al siguiente middleware, de lo contrario lanza un error unauthorized.
function checkAdminRole(req, res, next) {
const user = req.user;
if (user.role === 'admin') {
next();
} else {
next(boom.unauthorized());
}
}
A la ruta se le agrega el middleware, la lógica es:
Autenticar, verificar el token y obtener los datos del user (*passport.authenticate
)*.
Verificar el tipo de rol de user (checkAdminRole
).
Validar los datos del body (validatorHandler
).
Conectarse al servicio.
router.post(
'/',
passport.authenticate('jwt', { session: false }),
checkAdminRole,
validatorHandler(createCategorySchema, 'body'),
async (req, res, next) => {
try {
const body = req.body;
const newCategory = await service.create(body);
res.status(201).json(newCategory);
} catch (error) {
next(error);
}
}
);
Cuando se desea escalar y tener más roles, la función checkAdminRole
se vuelve poco mantenible, por ello creamos la función checkRoles
que recibirá los roles que tendrán acceso a ese endpoint. Si en los roles se encuentra el rol del usuario, devolverá true
y tendrá acceso al endpoint, de lo contrario devolverá false y arrojará error unauthorized.
En resumen, la función checkRoles
recibe un array de roles, verifica que user.role
se encuentre en ese array, y si todo bien procede al siguiente middleware.
function checkRoles(...roles) {
return (req, res, next) => {
const user = req.user;
if (roles.includes(user.role)) {
next();
} else {
next(boom.unauthorized());
}
};
}
Haciendo la implementación del middleware en la ruta queda de la siguiente manera, agregando los roles a los que tendrá acceso ese endpoint.
router.post(
'/',
passport.authenticate('jwt', { session: false }),
checkRoles('admin', 'seller'),
validatorHandler(createCategorySchema, 'body'),
async (req, res, next) => {
try {
const body = req.body;
const newCategory = await service.create(body);
res.status(201).json(newCategory);
} catch (error) {
next(error);
}
}
);
Se recomienda utilizar la librería accesscontrol donde realmente y de forma explicita se gestionan permisos de una forma más profunda y avanzada.
To create a role control, a middleware will be our best friend
First of all, let’s think about what we need, we need a middleware able to check what kind of users is authenticated, for this, let’s create a single function, which will receive a list of roles and return a middleware.
//@param roles array of roles that can access the route
function checkRole(roles) {
return (req, res, next) => {
const user = req.user;
//Check if rol is allowed to access
if (!roles.includes(user.role)) {
next(boom.unauthorized("Unauthorized!You cannot do this, Admins have been notified "));
} else {
//If everything is right, go to next middleware
next();
}
}
}
Finally just use it wherever you need to check role, por example:
router.post("/",
passport.authenticate('jwt', { session: false }),
checkRole(['admin', 'Captain']),
validatorHandler(getOrderSchema, "body"),
async(req, res, next) => {
try {
const body = req.body;
res.status(201).json(await service.create(body));
} catch (error) {
next(error);
}
}
);
Recommendation
Haciendo que esto sea un poco más escalable y mejores prácticas:
Creé un archivo index.js dentro de una carpeta ‘roles’ y simplemente es esto:
const ROLES = {
ADMIN: 'admin',
USER: 'user',
CUSTOMER: 'customer',
SELLER: 'seller'
}
module.exports = ROLES
luego usamos esos valores en los archivos que querramos:
const ROLES = require('../roles/');
router.get('/',
protectRoute,
checkRoles(ROLES.ADMIN, ROLES.CUSTOMER, ROLES.SELLER),
async (req, res, next) => {
try {
const categories = await service.find();
res.json(categories);
} catch (error) {
next(error);
}
});
Si estan usando insomia para hacer la pruebas de la API, pueden guardar el token del usuario al hacer login. Pega la siguiente linea, dentro del archivo de variables de entorno
{
"TOKEN_ADMIN": "{% response 'body', 'req_1e7f7e044bd5407bbff202ad27392f6e', 'b64::JC50b2tlbg==::46b', no-history, 60 %}",
}
Nota: después de pegar la línea de código aparecerá un recuadro en rojo, no se asusten, deben configurar los parámetros según sus request disponible (esto se hace a traves de una ventana gráfica)
No se si alguien ya lo puso antes, pero hice un pequeño cambio al código para no poner un rol que de por si tendrá acceso a todo ( al menos en este caso) para no repetir “admin” cada que se llama a la función hice lo siguiente:
function checkRoles(...roles){
roles.push('admin');
return (req, res, next) => {
const user = req.user;
if(roles.includes(user.role)){
next()
} else {
next(boom.unauthorized());
}
}
}
checkAdminRole
y checkRoles
quedan://Ésta función solo evalúa para el role ‘admin’
function checkAdminRole(req,res,next){
console.log(req.user);
//Se encuentra el payload
const user=req.user;
if(user.role==='admin'){
next();
}else{
next(boom.unauthorized());
}
}
//Los 3 puntos transorma todo argumento en array
function checkRoles(...roles){
return(req,res,next)=>{
const user=req.user;
//Compara el role del usuario con los roles permitidos enviados
if(roles.includes(user.role)){
next();
}else{
next(boom.unauthorized());
}
}
}
module.exports={checkApiKey,checkAdminRole,checkRoles}
Guardamos, vamos a la carpeta routes y abrimos el archivo categories.router.js, se implementa la lógica en cada petición, el código queda:
const express = require('express');
const passport = require('passport');
const CategoryService = require('./../services/category.service');
const validatorHandler = require('./../middlewares/validator.handler');
//Para veriicar si tiene autorización:
const { checkRoles } = require('./../middlewares/auth.handler');
//const { checkAdminRole } = require('./../middlewares/auth.handler');
const { createCategorySchema, updateCategorySchema, getCategorySchema } = require('./../schemas/category.schema');
const router = express.Router();
const service = new CategoryService();
router.get('/',
passport.authenticate('jwt', {session: false}),
checkRoles('admin', 'seller', 'customer'),
async (req, res, next) => {
try {
const categories = await service.find();
res.json(categories);
} catch (error) {
next(error);
}
});
router.get('/:id',
passport.authenticate('jwt', {session: false}),
checkRoles('admin', 'seller', 'customer'),
validatorHandler(getCategorySchema, 'params'),
async (req, res, next) => {
try {
const { id } = req.params;
const category = await service.findOne(id);
res.json(category);
} catch (error) {
next(error);
}
}
);
router.post('/',
//Proteger este endpoint con passport
passport.authenticate('jwt', {session: false}),
//Verificar si está autorizado con el role de 'admin'
//checkAdminRole,
checkRoles('admin'),
validatorHandler(createCategorySchema, 'body'),
async (req, res, next) => {
try {
const body = req.body;
const newCategory = await service.create(body);
res.status(201).json(newCategory);
} catch (error) {
next(error);
}
}
);
router.patch('/:id',
passport.authenticate('jwt', {session: false}),
checkRoles('admin', 'seller'),
validatorHandler(getCategorySchema, 'params'),
validatorHandler(updateCategorySchema, 'body'),
async (req, res, next) => {
try {
const { id } = req.params;
const body = req.body;
const category = await service.update(id, body);
res.json(category);
} catch (error) {
next(error);
}
}
);
router.delete('/:id',
passport.authenticate('jwt', {session: false}),
checkRoles('admin', 'seller'),
validatorHandler(getCategorySchema, 'params'),
async (req, res, next) => {
try {
const { id } = req.params;
await service.delete(id);
res.status(201).json({id});
} catch (error) {
next(error);
}
}
);
module.exports = router;
Guardamos, si no se ha compilado con: npm run dev
en la terminal, esperar a Mi port 3000
{
"email": "[email protected]",
"password": "23 23 23"
}
En la salida se obtiene el código 200 OK
:
{
"user": {
"id": 4,
"email": "[email protected]",
"role": "customer",
"createdAt": "2023-03-28T21:32:16.237Z"
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjQsInJvbGUiOiJjdXN0b21lciIsImlhdCI6MTY4MDAzOTE4M30.d49BvWGrgGtVcYHQEKTHS7CJeruE4mjTealUYgSwwS8"
}
Con ese token lo guardamos en Manage Environment, vamos ala carpeta Categories, luego a Get Categories, en la pestaña Auth, se selecciona Bearer y en TOKEN se coloca el token generado recientemente, al dar a Send, debe aparecer el código 200 OK
con la lista de las Categorías almacenadas, en caso de que el cliente no tenga el role con privilegio, sale el código 401 Unauthorized
.
Ni idea del por qué postman se cada cargando eternamente a pesar de que el servidor resuelva que es un 403 u.u
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?