Cómo autenticar y autorizar usuarios usando JWT en NodeJS

La autenticación y autorización es el concepto básico de la seguridad informática. Utiliza sus credenciales (como un nombre de usuario y contraseña) para probar su identidad e identificarse como un usuario registrado y luego obtener privilegios adicionales.

Esto también se aplica cuando inicia sesión en servicios en línea utilizando sus cuentas de Facebook o Google.

En este artículo, vamos a construir una API de Nodejs con autenticación JWT (JSON Web Tokens). Las herramientas que vamos a utilizar en este tutorial son:

  • Expressjs
  • Base de datos MongoDB
  • Mangosta
  • Dotenv
  • Bcryptjs
  • Jsonwebtoken

Autenticación vs. Autorización

¿Qué es la autenticación?

La autenticación es el proceso de identificación de usuarios mediante la adquisición de credenciales como correo electrónico, contraseña y tokens. Las credenciales proporcionadas se comparan con las credenciales del usuario registrado, que está disponible en el archivo del sistema informático local o en cualquier base de datos. Si las credenciales proporcionadas coinciden con los datos disponibles en la base de datos, el proceso de autenticación se completa y el usuario puede acceder a los recursos.

¿Qué es la Autorización?

La autorización ocurre después de la autenticación. Toda autorización debe tener un proceso de autenticación. Es el proceso de permitir a los usuarios acceder a los recursos de los sistemas o un sitio web. En este tutorial, autorizaremos a los usuarios registrados a acceder a los datos del usuario. Si el usuario no ha iniciado sesión, no podrá acceder a los datos.

Los mejores ejemplos de autorización son las plataformas de redes sociales como Facebook y Twitter. No puede acceder al contenido de las redes sociales sin tener una cuenta.

Otro ejemplo de autorización es el contenido basado en suscripción, su autenticación se puede realizar iniciando sesión en el sitio web, pero no estará autorizado para acceder al contenido hasta que no se haya suscrito.

Requisito previo

Antes de continuar, supongo que tiene un conocimiento básico de Javascript y MongoDB y un buen conocimiento de Nodejs.

Asegúrese de haber instalado node y npm en su máquina local. Para verificar si node y npm están instalados en su computadora, abra el símbolo del sistema y escriba node -v y npm -v. Esto debería mostrar el siguiente resultado.

Sus versiones pueden diferir de la mía. NPM se descarga automáticamente con el nodo. Si aún no lo ha descargado, descárguelo de la Sitio web de NodeJS.

Necesitará un IDE (entorno de desarrollo integrado) para escribir código. En este tutorial, estoy usando el editor de código VS. Si tienes otro, también puedes usarlo. Si no tiene ningún IDE instalado en su computadora, puede descargarlo desde el Sitio web de Visual Studio. Descárguelo según su sistema local.

Configuración del proyecto

Cree una carpeta con el nombre nodeapi en cualquier lugar de su computadora local y luego ábrala con vs-code. Abra el terminal vs-code y luego inicialice el administrador de paquetes de nodos escribiendo.

npm init -y

Asegúrate de estar en el directorio nodeapi.

El comando anterior creará un archivo package.json que contiene todas las dependencias que vamos a utilizar en este proyecto.

Ahora vamos a descargar todos los paquetes mencionados anteriormente, ahora escríbelos e ingrésalos en la terminal.

npm install express dotenv jsonwebtoken mongoose bcryptjs

Ahora, tendrá archivos y carpetas, como se muestra a continuación.

Creación de servidor y base de datos de conexión

Ahora cree un archivo llamado index.js y una carpeta llamada config. Dentro de la configuración, cree dos archivos llamados conn.js para conectarse a la base de datos y config.env para declarar las variables de entorno. Escriba el código dado a continuación en los archivos respectivos.

índice.js

const express = require('express');
const dotenv = require('dotenv');

//Configure dotenv files above using any other library and files
dotenv.config({path:'./config/config.env'}); 

//Creating an app from express
const app = express();

//Using express.json to get request of json data
app.use(express.json());



//listening to the server
app.listen(process.env.PORT,()=>{
    console.log(`Server is listening at ${process.env.PORT}`);
})

Si está utilizando dotenv, configúrelo en su archivo index.js antes de llamar a otros archivos que usan variables de entorno.

conn.js

const mongoose = require('mongoose');

mongoose.connect(process.env.URI, 
    { useNewUrlParser: true,
     useUnifiedTopology: true })
    .then((data) => {
        console.log(`Database connected to ${data.connection.host}`)
})

config.env

URI = 'mongodb+srv://ghulamrabbani883:[email protected]/?retryWrites=true&w=majority'
PORT = 5000

Estoy usando mongo-DB Atlas URI, también puede usar localhost.

Creación de modelos y rutas.

El modelo es un diseño de sus datos en la base de datos Mongo-DB y se almacenará como un documento JSON. Para crear un modelo, vamos a utilizar el esquema de mangosta.

El enrutamiento se refiere a cómo una aplicación responde a las solicitudes de los clientes. Usaremos la función de enrutador express para crear rutas.

Los métodos de enrutamiento generalmente toman dos argumentos. La primera es la ruta, y la segunda es la función de devolución de llamada para definir qué haría esta ruta a pedido del cliente.

También toma un tercer argumento como función de middleware cuando es necesario, como en el proceso de autenticación. Como estamos construyendo una API autenticada, también usaremos la función de middleware para autorizar y autenticar a los usuarios.

Ahora crearemos dos carpetas llamadas rutas y modelos. Dentro de las rutas, cree un nombre de archivo userRoute.js y dentro de la carpeta de modelos, cree un nombre de archivo userModel.js. Después de crear los archivos, escriba el siguiente código en los archivos respectivos.

modelo de usuario.js

const mongoose = require('mongoose');

//Creating Schema using mongoose
const userSchema = new mongoose.Schema({
    name: {
        type:String,
        required:true,
        minLength:[4,'Name should be minimum of 4 characters']
    },
    email:{
        type:String,
        required:true,
        unique:true,
    },
    password:{
        type:String,
        required:true,
        minLength:[8,'Password should be minimum of 8 characters']
    },
    token:{
        type:String
    }
})

//Creating models
const userModel = mongoose.model('user',userSchema);
module.exports = userModel;

rutausuario.js

const express = require('express');
//Creating express router
const route = express.Router();
//Importing userModel
const userModel = require('../models/userModel');

//Creating register route
route.post('/register',(req,res)=>{

})
//Creating login routes
route.post('/login',(req,res)=>{

})

//Creating user routes to fetch users data
route.get('/user',(req,res)=>{

})

Implementación de la funcionalidad de ruta y creación de tokens JWT

¿Qué es JWT?

Los tokens web JSON (JWT) son una biblioteca de JavaScript que crea y verifica tokens. Es un estándar abierto que se utiliza para compartir información entre dos partes: un cliente y un servidor. Usaremos dos funciones de JWT. La primera función es firmar para crear un nuevo token y la segunda función es verificar para verificar el token.

¿Qué es bcryptjs?

Bcryptjs es una función hash creada por Niels Provos y David Mazières. Utiliza un algoritmo hash para cifrar la contraseña. Tiene dos funciones más comunes que usaremos en este proyecto. La primera función bcryptjs es hash para generar un valor hash y la segunda función es la función de comparación para comparar contraseñas.

Implementar la funcionalidad de ruta

La función de devolución de llamada en el enrutamiento toma tres argumentos, solicitud, respuesta y función siguiente. El siguiente argumento es opcional; pasa esto solo cuando lo necesites. Estos argumentos deben estar en la solicitud, la respuesta y el siguiente pedido. Ahora modifique los archivos userRoute.js, config.env e index.js con los siguientes códigos.

rutausuario.js

//Requiring all the necessary files and libraries
const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');

//Creating express router
const route = express.Router();
//Importing userModel
const userModel = require('../models/userModel');

//Creating register route
route.post("/register", async (req, res) => {

    try {
        const { name, email, password } = req.body;
        //Check emptyness of the incoming data
        if (!name || !email || !password) {
            return res.json({ message: 'Please enter all the details' })
        }

        //Check if the user already exist or not
        const userExist = await userModel.findOne({ email: req.body.email });
        if (userExist) {
            return res.json({ message: 'User already exist with the given emailId' })
        }
        //Hash the password
        const salt = await bcrypt.genSalt(10);
        const hashPassword = await bcrypt.hash(req.body.password, salt);
        req.body.password = hashPassword;
        const user = new userModel(req.body);
        await user.save();
        const token = await jwt.sign({ id: user._id }, process.env.SECRET_KEY, {
            expiresIn: process.env.JWT_EXPIRE,
        });
        return res.cookie({ 'token': token }).json({ success: true, message: 'User registered successfully', data: user })
    } catch (error) {
        return res.json({ error: error });
    }

})
//Creating login routes
route.post('/login', async (req, res) => {
    try {
        const { email, password } = req.body;
        //Check emptyness of the incoming data
        if (!email || !password) {
            return res.json({ message: 'Please enter all the details' })
        }
        //Check if the user already exist or not
        const userExist = await userModel.findOne({email:req.body.email});
        if(!userExist){
            return res.json({message:'Wrong credentials'})
        }
        //Check password match
        const isPasswordMatched = await bcrypt.compare(password,userExist.password);
        if(!isPasswordMatched){
            return res.json({message:'Wrong credentials pass'});
        }
        const token = await jwt.sign({ id: userExist._id }, process.env.SECRET_KEY, {
            expiresIn: process.env.JWT_EXPIRE,
        });
        return res.cookie({"token":token}).json({success:true,message:'LoggedIn Successfully'})
    } catch (error) {
        return res.json({ error: error });
    }

})

//Creating user routes to fetch users data
route.get('/user', async (req, res) => {
    try {
        const user  = await userModel.find();
        if(!user){
            return res.json({message:'No user found'})
        }
        return res.json({user:user})
    } catch (error) {
        return res.json({ error: error });  
    }
})

module.exports = route;

Si está utilizando la función Async, use el bloque try-catch; de lo contrario, arrojará un error de rechazo de promesa no controlado.

config.env

URI = 'mongodb+srv://ghulamrabbani883:[email protected]/?retryWrites=true&w=majority'
PORT = 5000
SECRET_KEY = KGGK>HKHVHJVKBKJKJBKBKHKBMKHB
JWT_EXPIRE = 2d

índice.js

const express = require('express');
const dotenv = require('dotenv');

//Configure dotenv files above using any other library and files
dotenv.config({path:'./config/config.env'}); 
require('./config/conn');
//Creating an app from express
const app = express();
const route = require('./routes/userRoute');

//Using express.json to get request of json data
app.use(express.json());
//Using routes

app.use('/api', route);

//listening to the server
app.listen(process.env.PORT,()=>{
    console.log(`Server is listening at ${process.env.PORT}`);
})

Creación de middleware para autenticar al usuario

¿Qué es el software intermedio?

El middleware es una función que tiene acceso a la solicitud, al objeto de respuesta y a la siguiente función en el ciclo de solicitud-respuesta. La siguiente función se invoca cuando se completa la ejecución de la función. Como mencioné anteriormente, use next() cuando tenga que ejecutar otra función de devolución de llamada o función de middleware.

Ahora cree una carpeta llamada middleware, y dentro de ella, cree un nombre de archivo como auth.js y escriba el siguiente código.

autenticación.js

const userModel = require('../models/userModel');
const jwt = require('jsonwebtoken');
const isAuthenticated = async (req,res,next)=>{
    try {
        const {token} = req.cookies;
        if(!token){
            return next('Please login to access the data');
        }
        const verify = await jwt.verify(token,process.env.SECRET_KEY);
        req.user = await userModel.findById(verify.id);
        next();
    } catch (error) {
       return next(error); 
    }
}

module.exports = isAuthenticated;

Ahora instale la biblioteca del analizador de cookies para configurar el analizador de cookies en su aplicación. cookieParser le ayuda a acceder al token almacenado en la cookie. Si no tiene configurado cookieParser en su aplicación nodejs, no podrá acceder a las cookies desde los encabezados del objeto de solicitud. Ahora, escriba en la terminal para descargar el analizador de cookies.

npm i cookie-parser

Ahora, tiene instalado un analizador de cookies. Configure su aplicación modificando el archivo index.js y agregue middleware a la ruta «/usuario/».

archivo index.js

const cookieParser = require('cookie-parser');
const express = require('express');
const dotenv = require('dotenv');

//Configure dotenv files above using any other library and files
dotenv.config({path:'./config/config.env'}); 
require('./config/conn');
//Creating an app from express
const app = express();
const route = require('./routes/userRoute');

//Using express.json to get request of json data
app.use(express.json());
//Configuring cookie-parser
app.use(cookieParser()); 

//Using routes
app.use('/api', route);

//listening to the server
app.listen(process.env.PORT,()=>{
    console.log(`Server is listening at ${process.env.PORT}`);
})

rutausuario.js

//Requiring all the necessary files and libraries
const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const isAuthenticated = require('../middleware/auth');

//Creating express router
const route = express.Router();
//Importing userModel
const userModel = require('../models/userModel');

//Creating user routes to fetch users data
route.get('/user', isAuthenticated, async (req, res) => {
    try {
        const user = await userModel.find();
        if (!user) {
            return res.json({ message: 'No user found' })
        }
        return res.json({ user: user })
    } catch (error) {
        return res.json({ error: error });
    }
})

module.exports = route;

La ruta «/usuario» solo será accesible cuando el usuario haya iniciado sesión.

Comprobación de las API en POSTMAN

Antes de verificar las API, debe modificar el archivo package.json. Agregue las siguientes líneas de código.

"scripts": {
    "test": "echo "Error: no test specified" && exit 1",
    "start": "node index.js",
    "dev": "nodemon index.js"
  },

Puede iniciar el servidor escribiendo npm start, pero solo se ejecutará una vez. Para mantener su servidor funcionando mientras cambia archivos, necesitará nodemon. Descárgala tecleando en la terminal

npm install -g nodemon

-g flag descargará el nodemon globalmente en su sistema local. No tienes que descargarlo una y otra vez para cada nuevo proyecto.

Para ejecutar el servidor, escriba npm run dev en la terminal. Obtendrá el siguiente resultado.

Finalmente, su código está completo y el servidor está funcionando correctamente, vaya al cartero y verifique si está funcionando.

¿Qué es POSTMAN?

POSTMAN es una herramienta de software para diseñar, construir, desarrollar y probar API.

Si no ha descargado el cartero en su computadora, descárguelo de la sitio web del cartero.

Ahora abra el cartero y cree un nombre de colección nodeAPItest, y dentro de él, cree tres solicitudes: registro, inicio de sesión y usuario. Debes tener los siguientes archivos.

Cuando envíe datos JSON a «localhost: 5000/api/register», obtendrá el siguiente resultado.

Como también estamos creando y guardando tokens en cookies durante el registro, puede obtener los detalles del usuario cuando solicita la ruta «localhost: 5000/api/user». Puedes consultar el resto de solicitudes en POSTMAN.

Si desea el código completo, puede obtenerlo de mi cuenta github.

Conclusión

En este tutorial, hemos aprendido cómo aplicar la autenticación a la API de NodeJS usando tokens JWT. También autorizamos a los usuarios a acceder a los datos del usuario.

¡FELIZ CODIFICACIÓN!