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
Convierte tus certificados en títulos universitarios en USA
Antes: $249
Paga en 4 cuotas sin intereses
Termina en:
Aportes 17
Preguntas 7
Encontré una forma para proteger todas las rutas de /categories
sin agregar el middleware en cada uno de los métodos.
router.use('/categories', passport.authenticate('jwt', { session: false }), categoriesRouter);
Para proteger las rutas se instala la estrategia passport-jwt, esta estrategia va a capturar el token que viene del header, si el token está firmado con nuestra firma entonces lo va a autorizar, de otro modo no tendrá acceso a la ruta.
Comando de instalación: npm install passport-jwt
.
–
Se crea la nueva estrategia jwt.strategy.js
:
Se requiere la Strategy
y ExtractJwt
(dónde el token está para que extraiga el token).
Se crean las options
, estas contienen:
jwtFromRequest
→ Indica de dónde se saca el token, en este caso del header como bearer tokensecretOrKey
→ Cuál es el secreto, necesario para poder verificar si la firma es válida o no.La nueva estrategia (JwtStrategy
) recibe las options
y una función callback que recibe el payload
del JWT y la función done
que retorna el payload
que ya verificó.
const { Strategy, ExtractJwt } = require('passport-jwt');
const { config } = require('../../../config/config');
const options = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: config.jwtSecret,
};
const JwtStrategy = new Strategy(options, (payload, done) => {
return done(null, payload);
});
module.exports = JwtStrategy;
La nueva estrategia se agrega a utils/auth/index.js
:
const passport = require('passport');
const LocalStrategy = require('./strategies/local.strategy');
const JwtStrategy = require('./strategies/jwt.strategy');
passport.use(LocalStrategy);
passport.use(JwtStrategy);
Implementando la nueva estrategia a la ruta para crear categorías será como decir “los clientes que tengan un JWT válido van a poder crear categorías”.
Se requiere passport
, después se utiliza el método authenticate
que recibe el nombre de la estrategia jwt
sin un manejo de sesión (session: false
).
categories.router.js
:
const passport = require('passport');
router.post(
'/',
passport.authenticate('jwt', { session: false }),
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);
}
}
);
En resumen, primero se identifica al usuario (passport.authenticate
), después se validan los datos (validatorHandler
) y si todo bien, se conecta a la capa de servicios para poder crear la categoría.
En Insomnia se envía el token a través de los headers o usando la opción Bearer de la pestaña Auth.
Un pequeño aporte para proteger rutas agrupadas por el recurso, sirve para ahorrar un poco de trabajo al no tener que ir al archivo de las rutas para proteger una por una.
const express = require('express');
const passport = require('passport');
const { checkRoles } = require('./../middlewares/auth.handler');
const usersRouter = require('./users.router');
const profilesRouter = require('./profiles.router');
const coursesRouter = require('./courses.router');
const skillsRouter = require('./skills.router');
const authRouter = require('./auth.router');
function routerApi(app) {
const router = express.Router();
app.use('/api/v1', router);
router.use(
'/users',
passport.authenticate('jwt', {session: false}),
checkRoles('admin'),
usersRouter
);
router.use(
'/profiles',
passport.authenticate('jwt', {session: false}),
profilesRouter
);
router.use('/courses', coursesRouter);
router.use('/skills', skillsRouter);
router.use('/auth', authRouter);
}
module.exports = routerApi;
Cuando hacemos el login, el backend nos envía un token, ese token podemos usarlo para poder navegar diversas rutas, de modo que el backend nos solicita el token generado al hacer el login para acceder a la ruta protegida.
- Vamos a la página de passport, en passport-jwt indica que se debe instalar el paquete en la terminal:
npm install passport-jwt
Cuando haya instalado bien al arrojar:
found 0 vulnerabilities
//Llamar la librería passport-jwt
const { Strategy, ExtractJwt } = require('passport-jwt');
//Necesitamos a config para el secreto
const { config } = require('../../../config/config');
const options = {
//De dónde va a sacar el token:
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
//Secreto para ver si la firma es válida o no:
secretOrKey: config.jwtSecret
}
//Estrategia:
const JwtStrategy = new Strategy(options, (payload, done) => {
//Retorna cuando todo está bien y devuelve el payload que verificó:
return done(null, payload);
});
module.exports = JwtStrategy;
Guardamos, abrimos el archivo index.js de la ruta utils/auth, agregamos la nueva estrategia creada de jwt, el código queda:
const passport = require('passport');
//Aqui se definen qué estrategias se van a usar
const LocalStrategy = require('./strategies/local.strategy');
const JwtStrategy = require('./strategies/jwt.strategy');
passport.use(LocalStrategy);
passport.use(JwtStrategy);
Guardamos, vamos a la carpeta categories y abrimos el archivo categories.routes.js, agregamos en la librería (en la cabecera) a passport:
const passport = require('passport');
Luego agregamos en la petición del POST la autenticación del jwt, el código queda:
router.post('/',
//Proteger este endpoint con passport
passport.authenticate('jwt', {session: false}),
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);
}
}
);
Guardamos, si en la terminal no se ha compilado, ejecutar: npm run dev
cuando salga Mi port 3000
, podemos abrir Insomnia, vamos a la carpeta de Auth, en la petición del POST (login) y como tenemos en la salida el token, lo copiamos, vamos a la carpeta de Categories, luego a Create Category con la petición POST y la dirección:
_. API_URL/api/v1/categories
con la opción de desarrollador (dev), si no enviamos el token, en la salida tenemos un código 401 Unauthorized
(que no estamos autorizados para acceder).
Como ya tenemos el token, hay una pestaña que dice “Auth”, le damos click y en la lista desplegable elegimos a “Bearer” y en la casilla donde sale TOKEN pegamos el token que copiamos de Auth cuando hicimos login, en el JSON en el body tenemos:
{
"name": "new category",
"image": "dirección página"
}
Al dar Send, obtenemos un código 201 Created
:
{
"createdAt": "2023-03-27T20:40:12.397Z",
"id": 3,
"name": "new category",
"image": "dirección página"
}
Si seleccionamos la pestaña “Timeline” aparece el tipo de “Authorization” que en nuestro caso es de tipo “Bearer”:
Otra forma de enviar el token es que en “Header” antes de enviar la petición POST se coloque en la primera casilla: Authorization
y en la segunda casilla: Bearer
seguido del token:
En Insomnia, al igual que postman, se puede añdir una variable al entorno para no tener que estar metiendo el token a cada ruta.
BEARER[portador] AUTHENTICATION o autenticación de portador (también llamada autenticación de token) es un esquema de autenticación HTTP que implica tokens de seguridad llamados tokens de portador. El nombre “autenticación de portador” puede entenderse como “dar acceso al portador de este token”.
Guarde el
passport.authenticate('jwt', {session: false})
En una const para simplificar todo un poco y las fui agregando para proteger las demas routers
Reto hecho:
const express = require('express');
const passport = require('passport');
const CategoryService = require('./../services/category.service');
const validatorHandler = require('./../middlewares/validator.handler');
const { createCategorySchema, updateCategorySchema, getCategorySchema } = require('./../schemas/category.schema');
const router = express.Router();
const service = new CategoryService();
const protectRoute = passport.authenticate('jwt', { session: false });
router.get('/', async (req, res, next) => {
try {
const categories = await service.find();
res.json(categories);
} catch (error) {
next(error);
}
});
router.get('/:id',
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('/',
protectRoute,
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',
protectRoute,
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',
protectRoute,
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;
Por si quieren proteger todas las rutas excepto la de login
app.all(/^\/api\/v1\/(?!login).*$/, passport.authenticate('jwt', {session: false}))
Si alguien esta utilizando cookies para guardar el token, para que funcione la estrategia de passport-jwt deben usar cookieParser() en su servidor.
Aqui les dejo un ejemplo de la configuracio nen el index.js principal.
creacion de mi estrategia en el archivo archivo jwt.strategy.js
El vídeo tiene un error con el audio en el minuto 6.
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?