I am setting up an authentication functionality for my first time and am getting some unexpected results after a user has been logged in. A colleague has given me an application with working authentication to model my application after and it seems like everything I have done is correct.
I am using AngularJS on the front-end, SailsJS back-end framework, and PassportJS authentication middleware.
My source is (for now) stored publicly... the backend API is on Github here (API Github) and the front-end code is found here (Front-End Github)
What basically happens is that the user
Hits the login button, which triggers this function
login: function (credentials) { return baseAuth.customPOST(credentials, 'login').then(function (user) { $log.debug('User logged in: ', user); isAuthenticated = true; return user; }); },
The controller logic for that customPOST is this
login: function (req, res, next) { passport.authenticate('local', function (err, user, info) { if (err) { return res.json(err); } else if (user) { req.logIn(user, function (err) { if (err) { return res.json(err); } return res.json(user); }); } else { return res.json(400, info); } })(req, res); },
In that step, passport should do its thing and login the user
passport.use(new LocalStrategy({ usernameField: 'email', passwordField: 'password' }, function(email, password, next) { User.findOne({ email: email }) .exec(function (err, user) { if (err) { return next(err); } if (user) { if (user.activated) { bcrypt.compare(password, user.password, function (err, valid) { if (err) { next(err); } if (valid) { return next(null, user, { message: 'Logged In' }); } else { return next(null, false, { message: 'Incorrect password'}); } }); } else { next(null, false, { message: 'User is not activated. Please contact admins.'}); } } else { next(null, false, { message: 'Could not find user with email ' + email }); } }); } ));
In the console, I always get that this is successful.
Console reads:
User logged in: Object {firstName: "John", lastName: "Doe", email: "john_doe@work.com", activated: true, isUser: true…}
Then, my understanding of what happens gets kind of fuzzy. I know the application then tries to see if the user is authenticated. IsAuthenticated gets triggered, which looks like this in AngularJS:
isAuthenticated: function (force) { var deferred = $q.defer(); if (isAuthenticated && currentUser) { deferred.resolve(currentUser); } else { return baseAuth.customGET('authenticated').then(function (user) { deferred.resolve(currentUser); isAuthenticated = true; currentUser = user; return user; }); } return deferred.promise; }
It hits this action in the backend UserController
isAuthenticated: function (req, res) { if (req.isAuthenticated() && req.user.isUser) { return res.json(req.user); } else { return res.send(401); } },
Then it fails :( GET http://localhost:1337/user/authenticated 401 (Unauthorized)
Neither req.isAuthenticated nor req.user.isUser pass. I have separated them out to individually test and neither of them are true. "isUser" is a value I default to true and is so it should be true for every single user in the db. req.isAuthenticated also fails, which I don't entirely understand.
Anyone have some insight into my problem? What I have I done wrong here?
Since
passport.js
works using the underlying concept of strategies, you need to make sure that you authenticate using the right one. I don't see anywhere a strategy called 'user'should be changed to
Also make sure your user's
isUser
flag is set to true.If your frontend application has as a different origin than your backend application the AJAX requests will not include the session cookie and
req.isAuthenticated()
will never returns true.Use the withCredentials options to force it.
It seems that Restangular uses the same options:
https://github.com/mgonto/restangular#setdefaulthttpfields
https://docs.angularjs.org/api/ng/service/$http#usage
Edit: You should also check the server side configuration as pointed out by idbehold
As suggested by Matan, you should really give a try to the sails-generate-auth generator on the server side.
https://github.com/sails101/even-more-passport
https://github.com/kasperisager/sails-generate-auth
Usualy a 401 'Unauthorized' error actually refers to a credentials error rather than 403 error which refers to a real Unauthorized error(forbidden) so you should check your client with Postman first to test It and then move on to the client.
although, I would highly recommend that you will drop down from that github project and use this tutorial to implmenet your passport.js authentication as you only need to execute 2 lines of code and some configurations rather then get messy with the code: https://www.bearfruit.org/2014/07/21/tutorial-easy-authentication-for-sails-js-apps/