Tutorial: Autenticación basada en Token utilizando Node.js + Express.js + MongoDB

| 2016-07-4 | No hay comentarios »

La autenticación es una de las grandes partes de cada aplicación y la seguridad es siempre algo que está cambiando y evolucionando. En la actualidad, una de las formas más utilizada para añadir seguridad a un API es utilizando JSON Web Tokens.

maxresdefault

El objetivo de este tutorial es implementar la autenticación por token para una aplicación web basada en: MongoDB + Express + Node.

Antes de empezar con este tutorial te recomiendo que primeramente empieces con este: Tutorial: Crear API RESTful utilizando Node.js + Express.js + MongoDB

Estructura del proyecto

 ---- models/
 -------- user.js
 - config.js
 - package.json
 - server.js
 - middleware.js
 - service.js

Primeros pasos

Lo primero que debemos de hacer es crear el archivo package.json y ejecutar npm install para instalar todas las dependencias:

{
 "name": "node-token-jwt",
 "version": "1.0.0",
 "dependencies": {
 "body-parser": "~1.13.2",
 "express": "~4.13.1",
 "jwt-simple": "^0.5.0",
 "method-override": "^2.1.2",
 "mongoose": "~3.6.11"
 }
}

Codificando el JSON Web Token

Antes de continuar con ésta sección recomiendo que lean éste artículo: ¿Qué es Json Web Token (JWT)?

Ahora crearemos el Token que identifique a nuestro usuario en cada petición HTTP que realice.

Para codificar el token utilizamos una clave secreta. Es importante que esta clave permanezca lo más oculta posible. Una opción es almacenarla en un fichero config.js y ese fichero no subirlo al repositorio con .gitignore o la opción mejor es utilizar una variable de entorno (con process.env) que esté en nuestro servidor, y otra para nuestro entorno de desarrollo.

El archivo config.js quedaría así:

module.exports = {
TOKEN_SECRET: process.env.TOKEN_SECRET || "tokenprogramacioncompy"
};

Creamos el servicio (service.js) que utilizaremos para codificar el token. Para ello vamos utilizar la librería jwt-simple que nos facilita la vida a la hora de codificar el payload.

var jwt = require('jwt-simple');  
var moment = require('moment');  
var config = require('./config');

exports.createToken = function(user) {  
  var payload = {
    sub: user._id,
    iat: moment().unix(),
    exp: moment().add(14, "days").unix(),
  };
  return jwt.encode(payload, config.TOKEN_SECRET);
};

Creamos un objeto payload en el que ponemos tres atributos: sub, iat y exp.  En sub almacenamos el ID del usuario que pasamos por parámetro.

También usamos la librería moment para ayudarnos en el manejo de fechas. Con moment().unix() conseguimos el tiempo actual en formato UNIX, y con moment().add(14, "days").unix() le estamos añadiendo 14 días al momento actual.

Por último devolvemos el JSON Web Token, codificando el payload con nuestra clave secreta.

Acesso a rutas con autenticación.

Cada vez que accedamos a una ruta privada, sólo es accesible si estamos autenticados, como por ejemplo/private, le pasamos el middleware ensureAuthenticated que a continuación programaremos en el archivo middleware.js:

var jwt = require('jwt-simple'); 
var moment = require('moment'); 
var config = require('./config');

exports.ensureAuthenticated = function(req, res, next) { 
 if(!req.headers.authorization) {
 return res
 .status(403)
 .send({message: "Error"});
 }

 var token = req.headers.authorization.split(" ")[1];
 var payload = jwt.decode(token, config.TOKEN_SECRET);

 if(payload.exp <= moment().unix()) {
 return res
 .status(401)
 .send({message: "The token expires"});
 }

 req.user = payload.sub;
 next();
}

Lo primero que hacemos en la función es comprobar que la petición, req lleva la cabecera de autorización req.headers.authorization. Si la petición no envía una autorización, envíamos el código de error 403 de acesso denegado. y si no, tomamos el token.

La cabecera quedaría así:

Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbsciOiJIUzI1NiJ9.eyJzdWIiOiIWeRtU2ZWMyYjUyNjgxNzE2YmXiNzAxMzIiLCJpYXQiOjE0Mj10MjA0OTEsImV4cCI6MTQy67YzMDA5MX0.IH7ek7Rp_WQJvXeOd8zrBIpeFi4W6kUi_6htmaxv7Ow  

Sólo tenemos que obtener el token de ese String y lo hacemos con el método split de JavaScript:

var token = req.headers.authorization.split(" ")[1];  

Decodificamos ese token con la función decode y la clave secreta y ya podemos identificar al usuario, con el atributo sub del objeto payload, que según este ejemplo serán un ObjectID de Mongo.

Ahora seguimos con nuestro servidor (server.js) que iremos creando paso a paso:

var express = require('express');
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var methodOverride = require("method-override");
var app = express();
var User = require('./models/user'); 
var middleware = require('./middleware');
var service = require('./service');

// Connection to DB and configurations
 mongoose.connect('mongodb://localhost/token', function(err, res) {
 if(err) throw err;
 console.log('Connected to Database');
 });
 app.set('superSecret', config.secret); // secret variable

// Middlewares
 app.use(bodyParser.urlencoded({ extended: false }));
 app.use(bodyParser.json());
 app.use(methodOverride());

 // =======================
 // routes ================
 // =======================
 app.get('/', function(req, res) {
 res.send('Hola! API: http://localhost:3000/api');
 });

// Start server
 app.listen(3000, function() {
 console.log("Node server running on http://localhost:3000");
 });

Antes de continuar vamos a probar nuestro servidor (node server.js) e igresamos a: http://localhost:3000 , el resultado debe ser el siguiente:

apitoken

Creamos un usuario de ejemplo

Esto es muy sencillo, vamos a agregar las siguientes líneas al archivo server.js:

app.get('/setup', function(req, res) {
 // create a sample user
 var nick = new User({ 
 name: 'Rodrigo', 
 password: 'pro',
 admin: true 
 });

// save the sample user
 nick.save(function(err) {
 if (err) throw err;

console.log('User saved successfully');
 res.json({ success: true });
 });
});

Luego ingresamos a http://localhost:3000/setup y el resultado debería de ser:

setuptoken

Mostramos el usuario creado

Debemos de agregar las siguientes lineas al archivo server.js:

// API ROUTES -------------------
var apiRoutes = express.Router();

apiRoutes.get('/', function(req, res) {
 res.json({ message: 'Bienvenido al api de programacion.com.py :)' });
});

apiRoutes.get('/users', function(req, res) {
 User.find({}, function(err, users) {
 res.json(users);
 });
});

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

En este caso vamos a utilizar la herramienta Postman, por lo tanto haremos un request GET a http://localhost:3000/api/users y el resultado debe de ser:

apiexampleusers

Autenticación y creación del TOKEN

Ahora crearemos un método POST que servira para autenticar un nombre y una contraseña. Si el nombre y la contraseña son correctos, a continuación, vamos a crear un token de una forma muy sencilla 🙂

// Route to authenticate a user (POST http://localhost:3000/api/authenticate)
apiRoutes.post('/authenticate', function(req, res) {
 //find the user
 User.findOne({
 name: req.body.name
 }, function(err, user) {

if (err) throw err;

if (!user) {
 res.json({ success: false, message: 'Authentication failed. User not found.' });
 } else if (user) {

// check if password matches
 if (user.password != req.body.password) {
 res.json({ success: false, message: 'Authentication failed. Wrong password.' });
 } else {
 // return the information including token as JSON
 res.json({
 success: true,
 message: 'Enjoy your token!',
 token: service.createToken(user)
 });
 }
 }
 });
});

Posteriormente crearemos un método que solamente podrán acceder los usuarios autenticados:

apiRoutes.get(‘/private’,middleware.ensureAuthenticated, function(req, res){
var token = req.headers.authorization.split(» «)[1];
res.json({ message: ‘Estás autenticado correctamente y tu _id es:’+req.user });
});

Prueba

Ahora vamos a probar que todo este funcionando correctamente, primeramente hacemos correr el servidor con node server.js, posteriormente haremos un request POST a http://localhost:3000/api/authenticate utiliozando «x-www-form-urlencoded» para enviar el name y el password:

apiexampleaut

si todo está correcto el servidor nos debería de devolver:

{
 "success": true,
 "message": "Enjoy your token!",
 "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI1Nzc3ZGY0ZmFmOTc5ZmQwMDkwMDAwMDEiLCJpYXQiOjE0Njc0OTk1MzIsImV4cCI6MTQ2ODcwOTEzMn0.VXX4VZtwhRzKnAY6Siy4s4UyE56c6MXm1hpSQq_9Kk0"
}

A continuación probaremos que la autenticación funcione correctamente pero esta vez ya enviaremos el token generado en la cabecera del request (en éste caso sería así: Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI1Nzc3ZGY0ZmFmOTc5ZmQwMDkwMDAwMDEiLCJpYXQiOjE0Njc0OTMwMDAsImV4cCI6MTQ2ODcwMjYwMH0.gW-HPE5VvpC_gB6C23OXE_-WgS7nJSyjgLuNeCn5egI ya que ese es el token que se nos generó anteriormente) con un método GET al url http://localhost:3000/api/private:

apiexampleautprivate

Como pueden observar en el anterior screen, el servidor nos devolvió el mensaje con el id del usuario Rodrigo que es el que utilizamos en éste ejemplo.

Espero que les sea de mucha utilidad éste artículo!!

Descargar proyecto completo:

github-logo

Acerca del autor: Rodrigo Paszniuk

Ingeniero Informático, amante de la tecnología, la música, el ciclismo y aprender cosas nuevas.

Posts Relacionados

  • Tutorial: Autenticación de usuarios en Node.js con Facebook utilizando Passport
  • Tutorial: Crear una aplicación web utilizando MEAN
  • Tutorial: Crear API RESTful utilizando Node.js + Express.js + MongoDB
  • Archivos estáticos en Express.js



SEGUÍNOS EN FACEBOOK


GITHUB