Autenticación con JWT en JSON Server
Introducción
En este artículo te explico como crear un API Rest completa junto a faker.js para que puedas hacer tus pruebas sin muchas complicaciones.
Como ampliación me parece muy interesante añadir a nuestra API Rest seguridad, así que en este nuevo artículo vamos a usar JWT para agregar un sistema de autenticación por token.
Voy a dar por sentado que tanto json-server (imprescindible) como faker.js (opcional) está(n) instalado(s).
Ampliación JSON server
Quiero aprovechar para hacer una pequeña ampliación sobre algunas funciones avanzadas que ofrece json-server, porque en el artículo anterior solo probamos el CRUD.
Para no extenderme mucho voy añadir el código que genera los datos falsos y el indice con las funciones avanzadas que podemos hacer en JSON Server:
let faker = require('faker'); let generateData = () => { let data = [] for (let id = 0; id < 1000; id++) { data.push({ "id": id, "address": faker.address.streetAddress(), "latitude": faker.address.latitude(), "longitude": faker.address.longitude(), "first_name": faker.name.firstName() }); } return { "data": data } } module.exports = generateData /* ------------------------ FILTER [ { "id": 4, "address": "866 Mohr Light", "latitude": "-55.7908", "longitude": "170.3427", "first_name": "Tyree" } ] CURL: http://localhost:3000/data?id=4 ------------------------ ------------------------ PAGINATE [ { "id": 10, "address": "612 Fahey Locks", "latitude": "57.2960", "longitude": "-54.5350", "first_name": "Antone" }, { "id": 11, "address": "09094 Tillman Brooks", "latitude": "69.1174", "longitude": "-134.4193", "first_name": "Tito" }, { "id": 12, "address": "8161 Kelton Shoal", "latitude": "86.5114", "longitude": "65.8792", "first_name": "Freda" }, { "id": 13, "address": "21785 Jasmin Trail", "latitude": "-33.6972", "longitude": "16.9923", "first_name": "Bettye" }, { "id": 14, "address": "620 Abernathy Ports", "latitude": "-9.2774", "longitude": "-47.5246", "first_name": "Braden" }, { "id": 15, "address": "24260 Wilfred Parks", "latitude": "54.5527", "longitude": "111.0772", "first_name": "Roselyn" }, { "id": 16, "address": "2826 Oral Trafficway", "latitude": "-61.6411", "longitude": "-124.6945", "first_name": "Maude" }, { "id": 17, "address": "0318 Hessel Junction", "latitude": "-87.0497", "longitude": "147.6963", "first_name": "Allison" }, { "id": 18, "address": "93964 Darion Lights", "latitude": "-82.7756", "longitude": "176.7674", "first_name": "Quincy" }, { "id": 19, "address": "341 Akeem Way", "latitude": "11.6085", "longitude": "67.5816", "first_name": "Theresa" } ] CURL: http://localhost:3000/data?_page=2 Por defecto son paginaciones de 10 en 10 registros ------------------------ ------------------------ SORT [ ........ { "id": 605, "address": "688 Shanna Via", "latitude": "55.7688", "longitude": "168.6224", "first_name": "Zoila" } ] CURL: http://localhost:3000/data?_sort=first_name&order=asc ------------------------ */
Para obtener mas información puedes ir a la siguiente url
Añadiendo la autenticación JWT
Lo primero que tenemos que hacer es instalar el paquete jsonwebtoken
npm i jsonwebtoken –save
Una vez terminada la instalación crearemos un fichero que usaremos configurar nuestra API REST junto a JWT. En el siguiente enlace puedes acceder al repositorio.
No obstante voy añadir el código completo con una pequeña explicación.
servidor.js
// Añadimos los módulos necesarios const FS = require('fs'); const bodyParser = require('body-parser'); const jsonServer = require('json-server'); const JWT = require('jsonwebtoken'); const middlewares = jsonServer.defaults() // Servidor Express const server = jsonServer.create(); // Enrutador Express const router = jsonServer.router('./db.json'); // Creamos un JSON con los usuarios (03f996214fba4a1d05a68b18fece8e71 == MD5 Hash 'usuarios' ) const userdb = JSON.parse(FS.readFileSync('./03f996214fba4a1d05a68b18fece8e71.json', 'UTF-8')); // Middlewares predeterminados (logger, static, cors y no-cache) server.use(middlewares) // Parseo del body server.use(jsonServer.bodyParser); // Configuración TOKEN y duración const SECRET_KEY = 'zxcasdqwe098765'; const expiresIn = '1h'; // Crear un TOKEN desde un payload createToken = (payload) => JWT.sign(payload, SECRET_KEY, { expiresIn }); // Verificar el TOKEN verifyToken = (token) => JWT.verify(token, SECRET_KEY, (err, decode) => decode !== undefined ? decode : err); // Comprobamos si el usuario existe en nuestra 'base de datos' isAuthenticated = ({ email, password }) => userdb.users.findIndex(user => user.email === email && user.password === password) !== - 1; // Creamos un ENDPOINT para comprobar si el usuario existe y poder crear y enviar un TOKEN server.post('/auth/login', (req, res) => { const { email, password } = req.body; if (isAuthenticated({ email, password }) === false) { const status = 401; const message = 'Contraseña y/o password incorrectos'; res.status(status).json({ status, message }) console.log(message); return; } const access_token = createToken({ email, password }); res.status(200).json({ access_token }) }); // Añadir un middleware Express que verifique si el encabezado de autorización. // Luego verificara si el token es válido para todas las rutas, excepto la ruta anterior, // ya que esta es la que usamos para iniciar sesión en los usuarios. server.use(/^(?!\/auth).*$/, (req, res, next) => { if (req.headers.authorization === undefined || req.headers.authorization.split(' ')[0] !== 'Bearer') { const status = 401; const message = 'Header con autorización incorrecta'; res.status(status).json({ status, message }); console.log(message); return; } try { verifyToken(req.headers.authorization.split(' ')[1]); next(); } catch (err) { const status = 401; const message = 'Error: TOKEN de acceso no válido'; res.status(status).json({ status, message }); console.log(message); } }) server.use(router); server.listen(3000, () => { console.log('API REST FUNCIONANDO') });
El fichero para la base de datos lo he generado con el siguiente script en NodeJS (db.js) :
let faker = require('faker'); let data = { data: [] }; for (let id = 0; id < 1000; id++) { data.data.push({ "id": id, "address": faker.address.streetAddress(), "latitude": faker.address.latitude(), "longitude": faker.address.longitude(), "first_name": faker.name.firstName() }); } console.log(JSON.stringify(data));
Para generar nuestra base de datos usaremos el siguiente comando:
node db.js > db.json
Y para finalizar, la tabla de los usuarios es un fichero JSON con unos mínimos datos:
{ "users": [ { "id": 1, "name": "ivan", "email": "ivan@dominio.com", "password": "qwerty1234" }, { "id": 2, "name": "rafael", "email": "rafael@dominio.es", "password": "asdfgh56789" } ] }
Cuando tengamos todo el proyecto preparado usaremos el siguiente comando para iniciar nuestro servidor:
node servidor.js
A continuación unas pruebas del funcionamiento de JWT:
Ahora con el usuario y la contraseña validos: