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
You don't have access to this class
Keep learning! Join and start boosting your career
To protect our routes in a more secure way, we have come to the critical point of implementing JWT (JSON Web Tokens) with Passport in our system. This process ensures that only authenticated users have access to certain parts of our backend. Below, I'll walk you through the steps necessary to implement this strategy.
Passport JWT Installation: From the terminal, run the Passport JWT library installation. With this package you will have what you need to implement the strategy.
npm install passport-jwt
Strategy setup: Create a new strategy called JWT by importing Strategy
and ExtractJwt
from passport-jwt
.
const { Strategy, ExtractJwt } = require('passport-jwt');
Options definition: Configures the options required for the strategy. Defines where the token will be extracted from (usually from the header as Bearer token) and specifies the secret to validate the token.
const options = { jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), secretOrKey: config.jwtSecret // Get this from your configuration};
Creating the JWT strategy: Initialize the JWT strategy using the above options. Passport will verify the token and provide you with the payload if it is valid.
const jwtStrategy = new Strategy(options, (payload, done) => { // Token verification if (isValid(payload)) { return done(null, payload); } return done(null, false);});
System integration: Once the strategy is configured, make sure to import it and use it where you need to protect routes.
passport.use(jwtStrategy);
To protect an endpoint, such as the category creation endpoint, follow the steps below:
Define the protected path: use the jwt
strategy to protect the desired endpoint. Passport is used here to verify the authenticity of the JWT.
router.post('/categories', passport.authenticate('jwt', { session: false }), (req, res) => { // Logic to create a category});
Verification in Insomnia or similar environment: Make sure to send the token in the request headers. For example, in Insomnia, set the authentication type as Bearer token to send the JWT.
Testing and validation: Make sure that when sending a valid JWT, the system allows you to perform specific actions, such as creating a category. If the JWT is invalid or corrupted, access should be denied.
A Bearer token is simply a type of token that is sent in the Authorization
header of an HTTP request. This is used to authenticate requests and validate that they are made by a legitimate user. Important:
Bearer <token>.
authorization
token must be included correctly in the headers.Congratulations! You now have a route protected with JWT. Continue securing the rest of your routes. Make sure that not only the creation operations are protected, but also the modification and deletion operations. This will prevent unauthorized users from performing critical actions on your system.
Learning and experimenting with API security is extremely important. Protect your application effectively and keep researching in this fascinating field of technology. Success in your implementation and keep on developing!
Contributions 18
Questions 8
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.
Want to see more contributions, questions and answers from the community?