📜 ⬆️ ⬇️

Passport integration into SailsJS 0.9

Good afternoon, Habr!

Recently, I ran into an authorization problem with Sails.js. Initially, user authentication via crypto and req.session was implemented. But I wanted, sooner or later, to add authorization through social networks.

In order not to waste time on each social network ( meaning OAuth and callback processing ), two excellent libraries were found: Passport and Everyauth .
')
There is a good quick start in their documentation. With one small reservation. Quick Start is described using Express in "pure form." We have Sails (roughly speaking, a wrapper over Express). And all the requests that I “Google” did not give a detailed answer to the question “How to integrate Passport into Sails.js”.

This post is intended to close the question of the availability of the tutorial of Passport integration in Sails.js.

Prologue


Why Passport? There is a detailed answer to this question, which introduced the last word.

Sails installation


I will not go into details, it is assumed that the reader already knows the basics of working with Sails.
npm install -g sails sails new passport-sails cd passport-sails sails lift 

PS For live-coding I advise you to use nodemon.
 npm install -g nodemon 
Then just run nodemon instead of sails lift.

Passport installation


Everything is simple here.
 npm install passport npm install passport-local npm install passport-remember-me 

We set Passport, Local Strategy to log in with username / password and Remembe Me Strategy, to remember the user by token in the cookie.

Passport Setup


This is the most interesting item. The documentation describes the process of setting up Middleware without using frameworks ( link ). We have the same framework, so it’s not clear where to write the Passport setting. Well, let's do a search.

The documentation states that you need to use passport.use () and app.use ().
 //   var passport = require('passport'), LocalStrategy = require('passport-local').Strategy; passport.use(new LocalStrategy( function(username, password, done) { User.findOne({ username: username }, function (err, user) { if (err) { return done(err); } if (!user) { return done(null, false, { message: 'Incorrect username.' }); } if (!user.validPassword(password)) { return done(null, false, { message: 'Incorrect password.' }); } return done(null, user); }); } )); app.configure(function() { app.use(express.static('public')); app.use(express.cookieParser()); app.use(express.bodyParser()); app.use(express.session({ secret: 'keyboard cat' })); app.use(passport.initialize()); app.use(passport.session()); app.use(app.router); }); 

From the example it is clear that we need access to the app variable. Also, you need to put passport.use somewhere, not in the action actions.

After a brief search, one detail is found out that is not stated in the Sails.js documentation. You can access the Middleware layer in Sails. To do this, create a file in config / middleware.js with the following content:
 module.exports = { express: { customMiddleware: function(app) {} } }; 

We have access to the app, so we can proceed to the Passport configuration.
 //config/middleware.js var bcrypt = require('bcrypt'), // bcrypt crypto = require('crypto'), //crypto    token' passport = require('passport'), //passport LocalStrategy = require('passport-local').Strategy, //  RememberMeStrategy = require('passport-remember-me').Strategy; //Remember Me  //   "login sessions" //   serialize\deserialize. passport.serializeUser(function(user, next) { next(null, user.id); }); passport.deserializeUser(function(id, next) { User .findOne(id) .done(function(error, user) { next(error, user); }); }); module.exports = { express: { customMiddleware: function(app) { //   passport.use(new LocalStrategy({ usernameField: 'username', passwordField: 'password' }, function(username, password, next) { //      email' User .findOne() .where({ or: [{ username: username }, { email: username }] }) .done(function(error, user) { // next-callback': //next(error, user, info); if (error) { next(error); } else if (!user) { next(false, false, 'This user not exists'); } else if (!bcrypt.compareSync(password, user.password)) { next(false, false, 'Wrong password'); } else { next(false, user); } }); } )); // RememberMe  passport.use(new RememberMeStrategy({ key: 'token' //  cookie,    token }, function(token, done) { //    token' User .findOne() .where({ autoLoginHash: token }) .done(function(error, user) { if (error) { done(error); } else if (!user) { done(null, false); } else { //  token    delete user.autoLoginHash; user.save(function() {}); done(null, user); } }); }, function(user, done) { //   token var token = crypto.randomBytes(32).toString('hex'); user.autoLoginHash = token; user.save(function() {}); done(null, token); })); app.use(passport.initialize()); app.use(passport.session()); app.use(passport.authenticate('remember-me')); } } }; 

In config / middleware.js, we configure all the strategies required for authentication. After that, you can proceed directly to the use of the route'ah.

Use Passport


Suppose you have a controller that is responsible for authentication. It has login and logout routes.
 //api/controllers/AuthController.js module.exports = { login: function(req, res, next) { //  authenticate  LocalStrategy passport.authenticate('local', function(error, user, info) { if (error) { next(new Error('Some error was occured')); } else if (!user) { next(info); } else { //     //    login //     req.login(user, function(error) { if (error) { next(new Error(error)); } else { res.redirect('/my-profile/' + req.user.username); } }); } })(req, res); //IMPORTANT:    ,    authenticate('login', ...)(req,res); //Passport'    \  req.body }, logout: function(req, res, next) { //    token' res.clearCookie('token'); req.logout(); res.redirect('/'); } }; 

You can use your own error handling. Use req.flash, for example, etc. We will leave it outside the post.

In the req.user object, after a successful login, there will be a model of your user. In the same way, you can access it in templates via req.user.

That's the end of the tutorial, though no ... wait. I found another error. So…

The user is determined by HTTP requests, but not by sockets. Not good, you need to decide.


If you send \ receive information on sockets, then the policies in Sails work. The error is that in policy I use the Passport req.isAuthenticated () method. And just it is not present in req, when the request goes by sockets. To add support for req.login, req.isAuthenticated (), and others, you need to “skip” Sails a bit.

Open the file config / bootstrap.js and write the following code:
 //config/bootstrap.js module.exports.bootstrap = function(cb) { var passport = require('passport'), // passport http = require('http'), // http initialize = passport.initialize(), session = passport.session(), //  :) methods = ['login', 'logIn', 'logout', 'logOut', 'isAuthenticated', 'isUnauthenticated']; sails.removeAllListeners('router:request'); //  listeners  request' sails.on('router:request', function(req, res) { //   event-listener initialize(req, res, function() { session(req, res, function(error) { if (error) { return sails.config[500](500, req, res); } for (var i = 0; i < methods.length; i++) { //Bind'    req- req[methods[i]] = http.IncomingMessage.prototype[methods[i]].bind(req); } //  sails    route sails.router.route(req, res); }); }); }); //IMPORTANT:    cb() // Sails    cb(); }; 

Thus, before each request processing, all missing Passport methods will be injected into the req-object.

I hope this post will help novice developers on Node.js + Sails to solve the problem of user authorization. Thank you for reading to the end :)

Sails
Passport
Passport Local Strategy
Passport Remember Me Strategy
An example of social network integration - sails-social-auth-example

Source: https://habr.com/ru/post/211925/


All Articles