¿Comó crear sesiones con webtokens, sails y redis?


Hola a todos en este post vamos a explicar como utilizar web tokens en nuestra API y almacenar los tokens en redis y mantener las sesiones de nuestros usuarios ya sea a travez de un sitio web o una aplicación movil.

Puedes descargar el repositorio en https://github.com/herel/sails-web-tokens, como primer paso debemos tener instalado en nuestro equipo:

  • sails.js
  • node.js
  • dotenv con npm
  • redis con npm
  • flat con npm

debemos configurar nuestras variables de entorno  que contienen el puerto en el que nuestra app corre, el puerto de redis y el host de redis.

Archivo .env debe ir en nuestra carpeta root

PORT=1337 ##puerto donde corre nuestra aplicación
TOKEN_KEY="A0oPHg;muUyHmnAE:O}F" ##key de nuestro token
REDIS_PORT=6379 ##puerto donde corre redis
REDIS_HOST="localhost" ##host donde corre redis

1.- Configurar el servicio para conectar con redis, insertar, eliminar o verificar si existe en redis nuestro token para eso vamos a crear el archivo RedisService.js en /api/services

2.- Cada vez que inicie nuestra aplicación debemos conectar con redis y seleccionar la base de datos 1 para esto vamos al archivo config/bootstrap.js donde replazamos por el siguiente codigo:

module.exports.bootstrap = function(cb) {
 
  var client = RedisService.prepareConnect().createClient(process.env.REDIS_PORT, process.env.REDIS_HOST);
  client.on('connect',function(){
  		sails.log.debug('Redis connected');
  		client.select(1);
  		RedisService.setConnection(client);
  	return cb();
  });
};

3.- Vamos a configurar el servicio TokenService.js que se encargara de crear o validar si el token es correcto  para esto creamos el archivo  TokenService.js en la carpeta api/service/TokenService.js

4.- Creamos el archivo sessionAuth.js en la ruta /api/policies que se encargara de recibir el token en cada request y validar si existe en redis

module.exports = function(req, res, next) {
	var token =  req.headers.authorization;
	if(token){
		TokenService.decode(token).then(function(decoded){
			RedisService.get('TOKEN::'+decoded.userId).then(function(result){
				if(!result)
					return res.send(403,{ error : true, message : "El token no es valido", status : 403 });
				if(result.create == decoded.create && result.expire == decoded.expire && decoded.userId == result.userId){
					req.userId  = result.userId.toString();
					return next();
				}else{
					return res.send(403,{ error : true, message : "El token no es valido", status : 403 });
				}
			}).catch(function(e){
				return res.send(500, { error : true, message : "Internal server error", status : 500 });
			});
		}).catch(function(e){
			return res.send(500, { error : true, message : "Internal server error", status : 500 });
		});
	}else{
		return res.send(500, { error : true, message : "Internal server error", status : 500 });
	}
};

Como podemos observar cada vez que se hace un request a travez del sitio web o app se debe enviar en los headers el token con el key authorization donde primero verificamos que el token se pueda decodificar con  el key posteriormente verificamos que exista el token en redis  para esto el token se creo con el KEY “TOKEN::ID” donde ID es el id del usuario de nuestra base de datos por ejemplo mongodb si el token es el mismo que se encuentra en redis esto quiere decir que es valido y añadimos a nuestro requests el userId para usarlo posteriomente en nuestro controller si vamos a realizar alguna otra  acción  más, asi en cada controller sabemos que usuario esta haciendo la peticion,  si por ejemplo el mismo usuario inicia sesión en otro equipo o aplicacion , el token antiguo  no tendra los mismo valores en create y expire por lo cual  mandaria el error 403 para indicar que la session expiro

5.- para activar nuestro policie debemos ir al archivo policie.js y añadir el controller y la funcion que pasara por el policie.

module.exports.policies = {
 
  'LoginController'   : {
    'login' : true,
  },
  'AccountController' : {
    'updateprofile'   : ['sessionAuth']
  }
}

Donde indicamos que el controller AccountController en la función updateprofile pasara antes por el policie sessionAuth para validar si la session es correcta. si todo est correcto se ejecutara la funcion de controller donde ya sabemos cual es el usuario logueado usando req.userId y poder actualizar el perfil.

Tambien si  observas el controller LoginController en la funcion Login  tiene como valor true  esto quiere decir que se ejecutara directo al hacer el request ya que este resquest es para iniciar sesion y aun  no tenemos un token para validar.

6.- por ultimo como configurar nuestra rutas con sails  vamos al archivo routes.js  que se encuentra en la carpeta  /config

module.exports.routes = {
  '/': {
    view: 'homepage'
  },
  'POST /api/v1.0/account/profile' : 'AccountController.updateprofile'
}