User not recognized by Express Passport with my strategy - angularjs

I have a strange behavior with my Angular - Express + Passport behavior.
On my application, a user is authenticated this way: the AngularJS application calls an external service, linked to NTML, which returns several information such as the email of the current user.
Then, the AngularJS application calls a REST service on my Express backend (/rest/userProfile) to get the full profile of the user. That's when I authenticate the user on the back-end.
However, at the same time, the AngularJS application (during its initialization) calls several others REST api on my back-end (such as retrieving the application configuration in /rest/config, some other stuffs to display on the homepage). These calls are not protected and are generally handled before the call to /rest/userProfile.
In others words, the Express session is created by the first request received on the back-end, which is generally /rest/config and not /rest/userProfile (I think this point is really important).
After all that initialization, the user can call some protected REST api, like /rest/foo/.
But it appears that sometimes, the user is then not recognized on the back-end, despite the fact that the request contains the Express cookie (connect.sid). The logs show that the request object does not contain the passport property.
If I refresh one or two times the browser, everything start to work correctly, i.e. the user is finally recognized. After that, during some times, I can restart my browser and it will work directly without problem.
It seems that if I delay all the requests (like /rest/config) to ensure that /rest/userProfile is the first request received on the server, everything works correctly (i.e. the user will be recognized correctly on protected routes).
Is my understanding correct, in the sense that with my current implementation I have to make the call to /rest/userProfile be the first request received on the backend, so the session created by Passport will contains the user information?
If yes, what I have to change in my implementation (cf. below) to make it work, regardless when the call to /rest/userProfile is received (of course before any protected route)?
If no, what is wrong on my site? Any clue?
The code
On my index.js, I set the following Express middlewares:
app.use(session({
secret: 'xxx',
resave: true,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
On my main route JS file:
passport.serializeUser(function(user, done) {
console.log('[serialize user]', user);
done(null, user.username);
});
passport.deserializeUser(function(username, done) {
console.log('[deserialize username]', username);
User.findOne({username: username}, function(err, user) {
done(err, user);
});
});
passport.use('ntml-strategy', new LocalStrategy({
usernameField: 'mail',
passwordField: 'password',
passReqToCallback: true
},
function(req, username, password, done) {
console.log('[ntml strategy]', username);
User.findOne({username: username}, function(err, user) {
if (err) {
return done(err);
} else if (user && user !== null) {
return done(null, user);
}
// User does not exist in DB.
// We should create a new user with data received from client.
var newUser = new User({
username: username,
roles: ['user']
});
User.create(newUser, function(err, userSaved) {
if (err) {
return done(err);
}
return done(null, userSaved);
});
});
}));
// The REST api to get user profile
app.post('/rest/userProfile', passport.authenticate('ntml-strategy'), getUserProfile);
function getUserProfile(req, res) {
var user = req.user;
console.log('[getUserProfile]', user);
if (!user) {
return res.status(401).end();
}
res.json({
username: user.username,
roles: user.roles
});
}
// In other file, another route:
app.get('/rest/foo/', isAuthenticated, getUserStuff);
function isAuthenticated(req, res, next) {
if (!req.isAuthenticated()) {
return res.status(401).end();
}
next();
}
function getUserStuff(req, res, next) {
// Do stuff
}
On the User model, I've set the passport-local-mongoose:
User.plugin(passportLocalMongoose, {
usernameField: 'username',
usernameUnique: true
});
Note that removing that plugin does not change anything in the behavior.
Thanks.
For versions: node 5.5, express 4.10.8, passport 0.3.2, passport-local 1.0.0 and Angular 1.5.

Related

Angularjs : server side templating

I am very new to MEAN. And I have made an application using node.js, express and mongodb. I want to send username to dashboard after user is logged in. How can I do it using Angularjs. I don't want to use ejs templating engine. If any Middle ware is required....plzz tell me.
thank you...
this is my usercrud.js
var User = require("../../schema/user/userschema");
var bcrypt = require('bcrypt');
const userOperation={
login(userObject,response){
var username=userObject.userid;
var psw = userObject.password;
User.find({ userid:username}, function (err, user) {
if (err) {
return done(err); }
if (!user) {
return done(null, false, { message: 'no user found' });
}
if(user){
console.log("user's true password is: "+user[0].password);
console.log("password"+psw);
bcrypt.compare(psw, user[0].password, function(err, res) {
if (err){
throw err;}
if(!res) {
console.log('Ooops!. Wrong Pass!');
return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.')); // create the loginMessage and save it to session as flashdata
}
if(res){
response.redirect('dashboard');
}
});
}
});
},
}
how can I send username to the dashboard.html
this is userroute.js
router.get('/dashboard',(req,response)=>{
});
As you are using MEAN stack for your application, what you can do is check login via async call and return user object in response. Then you can store that user into localstorage.
Once user is successful in logIn and you get this "user" object in response, redirect user from login to dashboard using angular redirection not from back-end server.
After redirection use localstorge for getting user information.

Passport session disappears after oauth callback

Making a hybrid app with Ionic, Angular, nodejs, etc.
User logs in with email and password and then wants to add 3rd party authentication to their account.
They are serialized into session.
We check, using passport, if they are authorized with 3rd party and if not send them to do so.
When the user comes back to the callback url we don't know who they are anymore because req.session is undefined.
Edit: I've been trying to simplify the code to get to the route of the problem.
// require everything and app.use them
// this is what I'm using for the session config
app.use(session({
secret: 'thisIsASecret',
resave: false,
saveUninitialized: false,
cookie: {secure: true, maxAge: (4*60*60*1000)}
}));
var user = { // simple user model for testing
id: 1,
username: 'username',
password: 'password',
oauthId: null
};
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
done(err, user);
});
// Local Passport
passport.use(new LocalStrategy(function(username, password, done) {
return done(null, user);
}));
app.post('/login', passport.authenticate('local'), function(req, res) {
console.log(req.session); // Prints out session object with passport.user = 1
res.end();
});
// oauth Passport
passport.use(new oauthStrategy({
clientID: ****,
clientSecret: ****,
callbackURL: 'http://localhost:3000/auth/oauth/callback',
passReqToCallback: true
}, function(req, accessToken, refreshToken, profile, done) {
console.log(req.session); // no passport object in session anymore
return done(null, profile);
}));
app.get('/auth/oauth', passport.authorize('oauth'));
app.get('/auth/oauth/callback', passport.authorize('oauth'), function(req, res) {
console.log(req.session); // no passport object in session here either
res.end();
});
On the client side after logging in I use this because the regular http request method doesn't work.
window.location.href = 'http://localhost:3000/auth/oauth';
Edit 2: Ionic doesn't allow sessions apparently. So I found that you can use the state parameter to send a token with the oauth request which comes back to the callback and use that to link the oauth details to the user's account.
app.get('auth/oauth/:token', function(req, res) {
passport.authorize('oauth', {state: req.params.token});
});
The only problem is now it won't redirect to the 3rd party to authorize with them. Just times out...
The solution was to use the route like this, where token is used to identify the user.
app.get('auth/oauth/:token', function(req, res, next) {
passport.authorize('oauth', {state: req.params.token})(req, res, next);
});
Then the token was available in the callback (req.query.state) and we can add the new details to our existing user details.

NodeJS Passport Custom Callback on Local Strategy doesn't trigger serialize/deserialize functions

I am trying to get Passport working with a custom callback on a local strategy.
I am using AngularJS in the frontend and nodejs, express on the backend.
So far I have been able to get the whole workflow done. My problem is that apparently the strategy does not invoke the deserialize / serialize functions in the verify callback when using custom callbacks. The curious thing is when I let passport handle the rest without giving a custom callback the functions are being called and I got 'foo' or 'bar' printed in the console.
Or maybe I just missed something important, but I cannot find anything in the docs or from other sources which could fix my problem.
var sessionOpts = {
saveUninitialized: true, // saved new sessions
resave: false, // do not automatically write to the session store
store: sessionStore,
secret: sessionSecret,
cookie : { httpOnly: true, maxAge: config.session.maxAge } // configure when sessions expires
};
var server = express();
// log all requests to the console
server.use(morgan('dev'));
server.use(bodyParser.json());
server.use(bodyParser.urlencoded({
extended: true
}));
server.use(session(sessionOpts));
server.use(passport.initialize());
server.use(passport.session());
// used to serialize the user for the session
passport.serializeUser(function(user, done) {
console.log('foo')
var sessionUser = {
id: user._id,
email: user.email
};
done(null, sessionUser)
});
// used to deserialize the user
passport.deserializeUser(function(sessionUser, done) {
console.log('bar')
done(null, sessionUser);
});
passport.use('local-signin', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
},
function (req, email, password, done) { // callback with email and password from our form
// attempt to authenticate user
User.getAuthenticated(email, password, function (err, user, reason) {
if (err) return done(err);
// login was successful if we have a user
if (user) {
// handle login success
return done(null, user);
}
// otherwise send back the reason why it failed
return done(null, false, reason)
});
}));
And this is my route defined in express (error handling are just dummy error codes for now):
server.get('/signin', function(req, res, next) {
passport.authenticate('local-signin', function(err, user, reason) {
if (err) {
return res.sendStatus(500);
}
// login was successful if we have a user
if (user) {
// handle login success
return res.send(user);
}
// otherwise we can determine why we failed
var reasons = User.failedLogin;
switch (reason) {
case reasons.NOT_FOUND:
return res.sendStatus(401);
break;
case reasons.PASSWORD_INCORRECT:
// note: these cases are usually treated the same - don't tell
// the user *why* the login failed, only that it did
return res.sendStatus(402);
break;
case reasons.MAX_ATTEMPTS:
// send email or otherwise notify user that account is
// temporarily locked
return res.sendStatus(403);
break;
default:
return res.sendStatus(500)
}
})(req, res, next);
});
Thank you in advance!
After searching the internet for about 2 days with absolutely no result or any example which could help me with my problem I posted the question here. Now right after posting it I found the solution in the docs. How embarrassing...
The solution is in this sentence: "Note that when using a custom callback, it becomes the application's responsibility to establish a session (by calling req.login()) and send a response."
Whatever... I hope this helps someone else to save some time
edited code from above:
server.get('/signin', function(req, res, next) {
passport.authenticate('local-signin', function(err, user, reason) {
if (err) {
return res.sendStatus(500);
}
// login was successful if we have a user
if (user) {
// handle login success
return req.login(user, function(err) {
if (err) return res.sendStatus(500);
return res.send(user)
});
}
// otherwise we can determine why we failed

How to secure feature for authenticated users only in Meanjs when using angularjs

I am working on my first app, and have started with the front-end and angularjs. In general I have found it very intuitive, but the relationship between backend and frontend is where things start to blur for me.
I have now gotten to the point where I want to provide slightly different functionality on some pages depending on whether the user is authenticated or not (in this case the ability to edit some form fields in a form).
From the public angularjs side it seems easy enough to write a basic if statement to provide different functionality to authenticated users (see basic attempt below) but as this is a client side function, how do I prevent a user spoofing authentication to edit things I don't want them to (save to database).
angular.module('core').controller('myCtrl', ['$scope', 'Authentication', 'Menus',
function($scope, Authentication, Menus) {
$scope.authentication = Authentication;
if(typeof $scope.authentication.user == "object"){
// behaviour for authenticated
}else{
// for unauthenticated
}
}
I am new to mean, meanjs and node.js in general, being primarily a php guy, so please be gentle if my question is way off base.
I suggest using passport a npm module for user authentication. Here's some code to get you started. Also take a look at this scotch.io tutorial
// load all the things we need
var LocalStrategy = require('passport-local').Strategy;
// load up the user model
var User = require('../app/models/user');
// expose this function to our app using module.exports
module.exports = function(passport) {
passport.serializeUser(function(user, done) {
done(null, user.id);
});
// used to deserialize the user
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
passport.use('local-signup', new LocalStrategy({
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) {
// asynchronous
// User.findOne wont fire unless data is sent back
process.nextTick(function() {
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ 'local.email' : email }, function(err, user) {
// if there are any errors, return the error
if (err)
return done(err);
// check to see if theres already a user with that email
if (user) {
return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
} else {
// if there is no user with that email
// create the user
var newUser = new User();
// set the user's local credentials
newUser.local.email = email;
newUser.local.password = newUser.generateHash(password);
// save the user
newUser.save(function(err) {
if (err)
throw err;
return done(null, newUser);
});
}
});
});
}));
passport.use('local-login', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) { // callback with email and password from our form
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ 'local.email' : email }, function(err, user) {
// if there are any errors, return the error before anything else
if (err)
return done(err);
// if the user is found but the password is wrong
if (!user || !user.validPassword(password))
return done(null, false, req.flash('loginMessage', 'Oops! Wrong username or password.')); // create the loginMessage and save it to session as flashdata
// all is well, return successful user
return done(null, user);
});
}));
};

passportjs and backbone: authenticating users

Currently I been using a chrome app called Postman to test my services from nodejs/express/passportjs.
Currently I'm having trouble wrapping my head around how I should grab the user info and authenticate it with backbone.
I would try to authenticate the user like so:
$.post("http://localhost:3000/login", { username: "joe", password: "pass" })
.done(function(data) {
console.log(data)
//try to pull a service that's protected by passport
})
.fail(function(data) {
console.log(data)
})
Which is not working at all when it's successful. Its giving the 500 error I set for when someone isn't logged in.
Any particular direction I should be going in to manage authentication with passportjs in backbone?
The 500 error means some part of the code in the server is not working properly.
You can send the logged in user from express using passport. You can follow the following example.
var app = express();
var login = require('./routes/login');
app.post('/login',
passport.authenticate('local', { successRedirect: '/',
failureRedirect: '/login',
failureFlash: true }),
login.login);
where your login.js file may look like this
exports.login = function (req, res) {
res.json(req.user);
}
the authenticate process of passport populates user variable in request (req) with the logged in user.
Please note, you have to use cookie parser and session of express to make the passport session working. e.g.,
app.use(express.cookieParser());
app.use(express.session({ secret: 'keyboard cat' }));
your local authentication may look like the following (say you have a function that finds user by username (findByUsername)).
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password'
},
function(username, password, done) {
// asynchronous verification, for effect...
process.nextTick(function () {
// Find the user by username. If there is no user with the given
// username, or the password is not correct, set the user to `false` to
// indicate failure and set a flash message. Otherwise, return the
// authenticated `user`.
findByUsername(username, function(err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false, { message: 'Unknown user ' + username }); }
if (user.password != password) { return done(null, false, { message: 'Invalid password' }); }
return done(null, user);
})
});
}
));

Resources