PassportJS rename "user" object - node.js

I am using PassportJS for my login functionalIs there a way to change the "user" object name in the request?
This is how it works now, by default (req.user):
function (req, res, next) {
if (!req.user) {
req.flash('error', 'Please sign in to access this page.');
res.redirect('/login');
} else {
next();
}
}
I would like to use the following instead (req.candidate):
function (req, res, next) {
if (!req.candidate) {
req.flash('error', 'Please sign in to access this page.');
res.redirect('/login');
} else {
next();
}
}

You can change it in initialization:
passport.initialize( {
userProperty: 'student' // defaults to 'user' if omitted
})
This code set: req.student
What you will do next depends on you and the used framework.
Look at source code:
Passport source code

Related

Passport.js authenticate failureRedirect with parameters

Working on Passport.js authentication and want to display messages to client when login is not successful.
My current logic:
app.route('/login').post(passport.authenticate('local', { failureRedirect: '/' }),
(req, res) => {
res.redirect('/profile')
});
Also using similar thing in registeration which is working as I intended using connect-flash to display my message.
app.route('/register')
.post((req, res, next) => {
myDataBase.findOne({ username: req.body.username }, function (err, user) {
if (err) {
next(err);
} else if (user) {
req.flash('message', 'Username is already registered, please try different username');
res.redirect('/');
} else {........
How can I apply similar solution to my login logic? I suppose I need to create a custom callback function but could not figure it out.

Getting Information from Req.User() into Req.params() in ExpressJS

After people log into my Express app, I'd like to redirect them to a url that reads:
www.mysite.com/:user
What I'm having trouble with is that my req.params object is coming up as an empty object literal when people get re-directed after logging in.
Ideally what I'd like to do is to somehow take the information stored in req.user and pass some of that information into the URL.
So basically I'd like to get from here:
router.post('/login',
passport.authenticate('local', {successRedirect:'/', failureRedirect: '/login', failureFlash: true}));
To here:
router.get('/:user', function(req, res) {
//stuff
}
After logging in the req.user object reads like this:
{
username: joesmith#mail.com,
name: Joe Smith,
password: dkei348#$kDL^583L,
formulas: []
}
And the code I'm attempting to use is as follows:
router.get('/:user', function(req, res) {
var user = req.user.name;
req.params = {
"user": user
}
But this doesn't work because initially req.user.name is undefined if someone isn't already logged in, so I can't get past the console error.
Apparently I don't understand how req.params is generated in enough detail to get around this problem.
Thank you.
If you want to have username in parameters you shoud redirect to '/' + req.user.name); because '/' doesn't have any params. That's why it is undefined. (additionaly you shoud check if parameter is defined and handle the error, so instead of console error you get error 404 or get to next, proper routing path).
Passport Documentation privides examples of doiung it:
app.post('/login',
passport.authenticate('local'),
function(req, res) {
// If this function gets called, authentication was successful.
// `req.user` contains the authenticated user.
res.redirect('/' + req.username);
});
or more complex custom callback :
app.get('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/' + user.name);
});
})(req, res, next);
});
But I think it is not a good practice and safer would be to redirect to the proper page/content by req.user value without putting it to URL, for example setting req.auth. and then using next(); instead of res.redirect('/' + user.name); to get to the next middleware. There username would be taken from req.auth.username, not from /:username.
How does the structure of your routing looks? You shoud make sure router.get('/:user' .. is not before router.get('/login'...
Looks like you are wanting to access whatever /:user value is. e.g. www.mysite.com/stretch0. Therefore you would access it like var user = req.params.user;
You can provide a custom handler, instead of letting Passport do all the redirecting:
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) {
req.flash('error', 'Unable to authenticate user');
return res.redirect('/login');
}
if (! user) {
return res.redirect('/login');
}
req.logIn(user, function(err) {
if (err) {
req.flash('error', 'Unable to log in user');
return res.redirect('/login');
}
return res.redirect('/' + user.username);
});
})(req, res, next);
});
This does mean that each logged-in user gets their "personal" URL, because you're redirecting to /USERNAME. I'm not sure from your question if that's actually your intention.

Calling and Saving User Account Custom Data

I'm making a user account system for my new website using node.sdk,stormpath,express.js and passport.js . So I've set up an account with a custom data slot. I would like to know how can I post new data to this custom data slot when they log out and retrieve it when they log in.I'm new to using node and I don't know where to put my code or how to access the 'user' account info when they have logged in. From what I can tell passport.js is handling authentication so I probably can't see the users email to search for their user account url on the stormpath api... maybe I'm missing something here??
router.post('/register', function(req, res) {
var username = req.body.username; var password = req.body.password;
// Grab user fields. if (!username || !password) { return res.render('register', { title: 'Register', error: 'Email and password required.' }); }
// Initialize our Stormpath client. var apiKey = new stormpath.ApiKey( process.env['STORMPATH_API_KEY_ID'], process.env['STORMPATH_API_KEY_SECRET'] ); var spClient = new stormpath.Client({ apiKey: apiKey });
var app = spClient.getApplication(process.env['STORMPATH_APP_HREF'], function(err, app) { if (err) throw err;
account = {
givenName: 'John',
surname: 'Smith',
username: username,
email: username,
password: password,
customData:{
favList:'',
},
};
app.createAccount(account, function (err, createdAccount) {
if (err) {
return res.render('register', {'title': 'Register', error: err.userMessage });
} else {
passport.authenticate('stormpath')(req, res, function () {
return res.redirect('/home');
});
}
});
});
});
// Render the login page. router.get('/login', function(req, res) {
res.render('login', { title: 'Login', error: req.flash('error')[0] }); });
// Authenticate a user. router.post( '/login',
passport.authenticate( 'stormpath', { successRedirect: '/home', failureRedirect: '/login', failureFlash: 'Oops I guess you need an account to get in here..Soz', } ) );
// Render the dashboard page. router.get('/home', function (req, res) { if (!req.user || req.user.status !== 'ENABLED') { return res.redirect('/login'); }
res.render('home', { title: 'Home', user: req.user, } ); });
This is a great question. Thankfully the Passport API has you covered. You want to use a "Custom Callback" function, then you can get access to the user inside of that function. In the case of the Stormpath strategy the user object will be a Stormpath Account instance. However you will need to re-implement some of the redirection logic that you're currently passing in as options. Here is an example of how that would look with the Stormpath strategy:
app.post('/login', function(req, res, next) {
passport.authenticate('stormpath', function(err, user, info) {
if (err) {
return next(err);
}
else if (user) {
console.log('The account is: ', user);
req.logIn(user, function(err) {
if (err) {
next(err);
}else{
res.redirect('/dashboard');
}
});
}else{
req.flash('error',info.message);
res.redirect('/login');
}
})(req, res, next);
});
The docs for this custom strategy can be found here: http://passportjs.org/guide/authenticate/
Another note: I'd suggest creating your spClient outside of the route handler. The Stormpath Client can be used for multiple requests and only needs to be created once per process.

Verify access/group in Passport.js

I would like to use passport.js to verify that when users hit certain endpoints that they not only have the correct password but are a member of a specific group or have a certain access.
For simplicity sake if I have access levels of USER and ADMIN.
I can use passport to authenticate a password:
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);
});
}
));
Then with a route I can make sure the user passes auth:
app.get('/api/users',
passport.authenticate('local'),
function(req, res) {
res.json({ ... });
});
But lets say you need to have ADMIN acess to hit /api/users'. Do I need to write my own Strategies? IE do I need to have a local-user, and local-admin strategy, and in each verify the proper access levels?
I think I can do this pretty easily, but the problem arises when I need my site to have different auth methods (maybe sometimes use oauth), I would need to write custom *-user, *-admin strategies for each. Seems like overkill.
Other option is to just verify access/group in each route after the user has been authenticated. But I would prefer to do this in the middle-ware if possible.
Thanks
You could create a simple middleware that checks the group:
var needsGroup = function(group) {
return function(req, res, next) {
if (req.user && req.user.group === group)
next();
else
res.send(401, 'Unauthorized');
};
};
app.get('/api/users',
passport.authenticate('local'),
needsGroup('admin'),
function(req, res) {
...
});
This assumes that the object stored in req.user has a property group. This object is the one passed along from the strategy implementation and deserializeUser.
An alternative could be connect-roles, but I don't know how well that integrates with Passport.
EDIT: you could also combine Passport and the group-checking middleware:
var needsGroup = function(group) {
return [
passport.authenticate('local'),
function(req, res, next) {
if (req.user && req.user.group === group)
next();
else
res.send(401, 'Unauthorized');
}
];
};
app.get('/api/users', needsGroup('admin'), function(req, res) {
});

Restricting Login Access - Passport.js, Google Authentication

Okay, so using passport.js works, and works well, from what I've seen. However, I'm not sure how to properly exclude certain users. If the application is intended to have restricted access, rather than just providing the user a method for logging in, how can I restrict the login through passport.js? As it stands, users can just visit /login and log in with their Google account, thereby getting access to the internals.
Here is one way to do this, with comments throughout. The main thing is understanding this page from the author: http://passportjs.org/guide/authenticate/, which I explain a little more in this example ...
It might be easier to read bottom to top:
var authenticate = function(req, success, failure) {
// Use the Google strategy with passport.js, but with a custom callback.
// passport.authenticate returns Connect middleware that we will use below.
//
// For reference: http://passportjs.org/guide/authenticate/
return passport.authenticate('google',
// This is the 'custom callback' part
function (err, user, info) {
if (err) {
failure(err);
}
else if (!user) {
failure("Invalid login data");
}
else {
// Here, you can do what you want to control
// access. For example, you asked to deny users
// with a specific email address:
if (user.emails[0].value === "no#emails.com") {
failure("User not allowed");
}
else {
// req.login is added by the passport.initialize()
// middleware to manage login state. We need
// to call it directly, as we're overriding
// the default passport behavior.
req.login(user, function(err) {
if (err) {
failure(err);
}
success();
});
}
}
}
);
};
One idea is to wrap the above code in some more middleware, to make it easier to read:
// This defines what we send back to clients that want to authenticate
// with the system.
var authMiddleware = function(req, res, next) {
var success = function() {
res.send(200, "Login successul");
};
var failure = function(error) {
console.log(error);
res.send(401, "Unauthorized");
};
var middleware = authenticate(req, success, failure);
middleware(req, res, next);
};
// GET /auth/google/return
// Use custom middleware to handle the return from Google.
// The first /auth/google call can remain the same.
app.get('/auth/google/return', authMiddleware);
(This all assumes we're using Express.)
Try this.
googleLogin: function(req, res) {
passport.authenticate('google', { failureRedirect: '/login', scope: ['https://www.googleapis.com/auth/plus.login', 'https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/userinfo.email'] }, function(err, user) {
req.logIn(user, function(err) {
if (err) {
console.log(err);
res.view('500');
return;
}
var usrEmail = user['email'];
if(usrEmail.indexOf("#something.com") !== -1)
{
console.log('successful');
res.redirect('/');
return;
}
else
{
console.log('Invalid access');
req.logout();
res.view('403');
return;
}
});
})(req, res);
}
*

Resources