Sails.js : JWT API Authentification


4 min read
Sails.js : JWT API Authentification

Ce post est destiné aux développeurs d'API's REST avec le framework Sails.js. L'objectif est de créer une authentification avec un Json Web Token pour une API REST.
Cet article est réalisé avec la version 1.0 de Sails.

Vous  devez au préalable avoir une app Sails connecté à votre base de  données. Dans mon exemple la BDD contient une table user comme  ci-dessous :

| name      | type                   |
|-----------|------------------------|
| id        | int(11) Auto Increment |
| email     | varchar(200)           |
| password  | varchar(200)           |
| createdAt | updatedAt              |

Pour hasher les mots de passe nous allons utiliser bcrypt.

npm install bcryptjs -s

Pour créer et gérer en toute transparence notre token (JWT), nous allons utiliser le paquet du même nom :

npm install jsonwebtoken -s

Si  vous n'avez pas encore de Model et Controller pour votre table  d'utilisateur, vous pouvez utiliser la commande suivante pour générer  automatiquement les deux fichiers (Model et Controller).

sails generate api user

Cette commande vous permet de créer le Model dans /app/models/User.js et le Controller dans /app/controllers/UserController.js.

Model

Rendez-vous dans votre fichier User.js dans /api/models et ajouter son contenu :

/** 
  * User.js 
  * 
  * @description :: A model definition represents a database table/collection. 
  * @docs        :: https://sailsjs.com/docs/concepts/models-and-orm/models 
  */

var bcrypt = require("bcryptjs");

module.exports = {
	attributes: {

	//  ╔═╗╦═╗╦╔╦╗╦╔╦╗╦╦  ╦╔═╗╔═╗    
	//  ╠═╝╠╦╝║║║║║ ║ ║╚╗╔╝║╣ ╚═╗    
	//  ╩  ╩╚═╩╩ ╩╩ ╩ ╩ ╚╝ ╚═╝╚═╝    
	password: 'string',    
	email: 'string',    

	//  ╔═╗╔╦╗╔╗ ╔═╗╔╦╗╔═╗    
	//  ║╣ ║║║╠╩╗║╣  ║║╚═╗    
	//  ╚═╝╩ ╩╚═╝╚═╝═╩╝╚═╝    

	//  ╔═╗╔═╗╔═╗╔═╗╔═╗╦╔═╗╔╦╗╦╔═╗╔╗╔╔═╗    
	//  ╠═╣╚═╗╚═╗║ ║║  ║╠═╣ ║ ║║ ║║║║╚═╗    
	//  ╩ ╩╚═╝╚═╝╚═╝╚═╝╩╩ ╩ ╩ ╩╚═╝╝╚╝╚═╝  
},  
customToJSON: function() {    
	// Retourne une copie du résultat sans le mot de passe    
	return _.omit(this, ['password'])  
},  
beforeCreate: function(values, cb) {    
	// Hash le password avant chaque création    
	bcrypt.hash(values.password, 10, function(err, hash) {      
		if (err) return cb(err);      
		values.password = hash;      
		cb();    
	});  
}};

Maintenant que notre User Model est créé vous pouvez déjà tester la création d'un utilisateur et vérifier que le mot de passe est bien haché.

Controller

Nous allons maintenant créer les méthodes de notre API.

  • Login : Récupère l'email et le password en POST et connecte l'utilisateur
  • Token : Rafraîchit le dernier token envoyé et met à jour la date d'expiration

Passons maintenant au UserController.js dans /api/controllers pour créer ces fonctions :

/** 
  * UserController 
  *
  * @description :: Server-side actions for handling incoming requests. 
  * @help        :: See https://sailsjs.com/docs/concepts/actions 
  */

var jwt = require("jsonwebtoken");
var bcrypt = require("bcryptjs");

module.exports = {  
	login: function(req, res) {    
		if (!req.param('email') || !req.param('password')) {      
			return res.serverError("No field should be empty.");    
		}    

		User.findOne({      
			email: req.param('email')    
		}).exec(function callback(err, user) {      
			if (err) return res.serverError(err);      
			if (!user) return res.serverError("User not found, please sign up."); 

			// check password      
			bcrypt.compare(req.param('password'), user.password, function(error, matched) {        
				if (error) return res.serverError(error);        
				if (!matched) return res.serverError("Invalid password.");        
		
				user.token = jwt.sign(user.toJSON(), "votre clé secrète ici", {          
					expiresIn: '7d'        
				});        
				res.ok(user);      
			});    
		});  
	},  
	token: function(req, res) {    
		User.findOne(req.user.id).exec(function callback(error, user) {      
			if (error) return res.serverError(error);      
			if (!user) return res.serverError("User not found");      

			user.token = jwt.sign(user.toJSON(), "votre clé secrète ici", {        
				expiresIn: '7d'      
			});      
			res.ok(user);    
		});  
	},
};

Policies

Nous  allons maintenant créer une règle de policies (autrement dit un  middleware). Ce fichier sera executé pour chaques requètes le demandant  et sera la barrière de connexion.
Créez un fichier isAuth.js dans /app/policies. Ajoutez-y la logique de notre règle :

var jwt = require("jsonwebtoken");

module.exports = function(req, res, next) {  
	var bearerToken;  
	var bearerHeader = req.headers["authorization"];  
	
	if (typeof bearerHeader !== 'undefined') {    
		var bearer = bearerHeader.split(" ");    
		bearerToken = bearer[1];    
		if (bearer[0] !== "Bearer") {      
			return res.forbidden("bearer not understood");    
		}    
		
		// on vérifie si le token est bon    
		jwt.verify(bearerToken, "votre clé secrète ici", function(err, decoded) {      
			if (err) {        
				sails.log("verification error", err);        
				if (err.name === "TokenExpiredError")          
					return res.forbidden("Session timed out, please login again");        
				else          
					return res.forbidden("Error authenticating, please login again");      
			}      

			User.findOne(decoded.id).exec(function callback(error, user) {        
				if (error) return res.serverError(err);        
				if (!user) return res.serverError("User not found");        
				req.user = user;        
				next();      
			});    
		});  
	} else {    
		return res.forbidden("No token provided");  
	}
};

Maintenant  que notre policie est crée, nous allons configurer les méthodes qui  devront la consommer. Pour cela rendez-vous dans le fichier /config/policies.js.

/** 
  * Policy Mappings 
  * (sails.config.policies) 
  * 
  * Policies are simple functions which run **before** your actions. 
  * 
  * For more information on configuring policies, check out: 
  * https://sailsjs.com/docs/concepts/policies 
  */
  
module.exports.policies = {  
    UserController: {    
        update: 'isAuth',    
        destroy: 'isAuth',    
        token: 'isAuth'  
    }
};

Cette règle rend les méthodes update, destroy et token inaccessibles sans token.

Routes

Enfin il ne nous reste plus qu'à créer les routes correspondantes pour nos deux méthodes. Rendez-vous dans /config/routes.js.

/** 
  * Route Mappings 
  * (sails.config.routes) 
  * 
  * Your routes tell Sails what to do each time it receives a request. 
  * 
  * For more information on configuring custom routes, check out: 
  * https://sailsjs.com/anatomy/config/routes-js 
  */
  
module.exports.routes = {  
    'post /user/login': 'UserController.login',  
    'get /user/token': 'UserController.token',
};

Test

Et voilà ! Vous pouvez maintenant tester le bon fonctionnement avec Postman par exemple.

  • Test de la connexion après avoir créé un utilisateur test
  • Test de la mise à jour d'un token

Merci d'avoir suivi cet article jusqu'au bout ! ❤️


Mon retour d'expérience sur Kimsufi
Previous article

Mon retour d'expérience sur Kimsufi

Cela fait maintenant 6 mois que je suis l'heureux (ou pas) propriétaire d'un serveur Dédié chez Kimsufi. Pour ceux qui ne connaissement pas Kimsufi est

Internet plus rapide et privé
Next article

Internet plus rapide et privé

1.1.1.1, c'est l'adresse du résolveur DNS orienté grand public et vie privé le plus rapide. Le 1er avril 2018 (je suis en


GO TOP