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

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.

Related

Redirect to original URL after login in NodeJS

I have a NodeJS server where some pages require logging in. At the moment, I have this code for login:
router.post("/login",
passport.authenticate("user-otp", {
successRedirect: "/dashboard",
failureRedirect: "back",
})
);
When I send a link to a user who is not logged in, they are redirected to a login page, enter credentials, go to a generic page, and have to go back to my message to find the original URL. I would like to condense the last two steps so users go to the original URL right after login.
Following Send data back with the Passport js failureRedirect method , I tried the following:
router.post("/login", function(req, res, next) {
let success = "/dashboard";
if (req.query.redir) {
success = req.query.redir;
}
return passport.authenticate("user-otp", {
successRedirect: success,
failureRedirect: "back",
})
});
But submitting the login information does not get a response from the server, probably because I am new to NodeJS and the asynchronous logic.
Update: My passport function is:
// Define the login strategy for users based on email and one-time-password.
passport.use('user-otp', new CustomStrategy(
async function(req, done) {
// logic...
let user, email = req.body.email, is_email = false;
try {
user = await User.findOne({email});
if (!user) {
return done(null, false, {message: "failed"});
}
} catch (err) {
console.error(err);
return done(err);
}
let check = await user.validateOtp(req.body.otp);
if ("valid" === check) {
return done(null, user);
} else {
return done(null, false, {});
}
}));
How can I redirect users to the original URL after login?
First idea is good but I think you should manually call passport, thats why you don't get any response from server.
router.post("/login", function(req, res, next) {
let success = "/dashboard";
if (req.query.redir) {
success = req.query.redir;
}
return passport.authenticate("some-strategy", {
successRedirect: success,
failureRedirect: "back",
})(req, res, next)
});
try this:
https://github.com/ne-cyber/login
see src/index.ts
app.get('/task', (req,res) => {
if(login('GET', res, '/task', req.query, req.body)) {
res.render('main.ejs', {
queryObj: req.query,
postObj: req.body
})
}
})
there is a simple function: login() if it returns true(token is valid or username&password is valid) and you may render your page. It saves query params and body(post) params
First things first
Following Send data back with the Passport js failureRedirect method , I tried the following:
router.post("/login", function(req, res, next) {
let success = "/dashboard";
if (req.query.redir) {
success = req.query.redir;
}
return passport.authenticate("some-strategy", {
successRedirect: success,
failureRedirect: "back",
})
});
the (passport)strategy in your code example in the question is "some-strategy",
whereas your defined strategy name is user-otp.
If the real implementation is different than my description above, please update the question to reflect the correct code.

Passport serializeUser() is not called with this authenticate() callback

Using passport.js, I write the route this way so I have access to the MongoDb document userDoc. But, when doing it this way... passport.serializeUser() will never be called and the req object will be missing user.
auth.route('/auth/facebook/callback')
.get(function(req, res, next) {
passport.authenticate('facebook', function(err, userDoc, info) {
if (err) { return next(err); }
// I don't think !userDoc will ever happen because of mongo upsert
if (!userDoc) { return res.redirect('/login'); }
res.cookie('facebookPicUrl', userDoc.value.facebook.picture, {maxAge : 9999999,
httpOnly: false,
secure: false,
signed: false
});
res.redirect('http://localhost:9000/users')
})(req, res, next);
});
But if I write it this way, the req.user is there as it should be:
auth.route('/auth/facebook/callback')
.get(passport.authenticate('facebook', { failureRedirect: '/login' }),
function(req, res) {
res.redirect('http://localhost:9000/users')
});
How can I make this to where passport.serializeUser is called and user exists on req and I also have access to the mongoDb object?
Since you are using the custom authentication callback you are responsible for establishing the session.
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.
req.login() assigns the user object to the request object req as req.user once the login operation completes.
You can see for example that in the documentation req.login() is explicitly called in the 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('/users/' + user.username);
});
})(req, res, next);
});

Node.js passport custom callback

I am using passport for my node.js app.
When I want to authenticate users local, I can simply do it
function local(req, res) {
req._passport.instance.authenticate('local', function(err, user, info) {
if(err) {
return workflow.emit('exception', err);
}
// and so on
res.end('some data');
}
}
But when I want to use facebook strategy, I must use redirectUrls like this.
function signinFacebook(req, res, next) {
req._passport.instance.authenticate('facebook')(req, res, next);
}
function facebookCallback(req, res, next) {
req._passport.instance.authenticate('facebook', {
successRedirect: '/',
failureRedirect: '/'
})(req, res, next);
}
This way I cant send with response data, that I am sending on local strategy.
Can anyone help me to fix it. I want not give success and failure Redirects, I want to call some function if all goes well like on local strategy.
I've found this in Passport's documentation, it may help.
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('/users/' + user.username);
});
})(req, res, next);
});
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.

How to use res.send in passport strategy?

I'm trying to make Node.js ajax authentication with passport.js, I want to show messages in /login page. I should use res.send in my passport strategy, then ajax call ended successfully will display success data to its page. But I can't guess how can I use res. in strategy. Please see below code,
login.ejs
<div id="messages"></div>
<!-- and there is a form, when form submitted, the ajax call executed.-->
<!-- ...ajax method : POST, url : /login, data: {}, success:... -->
<!-- If ajax call success, get 'result' data and display it here -->
app.js
// and here is ajax handler
// authentication with received username, password by ajax call
app.post('/login', passport.authenticate('local'),
function(req, res, next){
res.redirect('/');
});
// and here is passport strategy
passport.use(new passportLocal.Strategy(function(userid, password, done) {
Members.findOne({'user_id' : userid}, function(err, user){
// if user is not exist
if(!user){
// *** I want to use 'res.send' here.
// *** Like this :
// *** res.send('user is not exist');
// *** If it is possible, the login.ejs display above message.
// *** That's what I'm trying to it. How can I do it?
return done(null, null);
}
// if everything OK,
else {
return done(null, {id : userid});
}
})
}));
I searched some document on google, people usually use 'flash()' in connect-flash module, but I thought this module needed to reload page, this is not what I want to, So please help me and let me know if there is better way. Thanks.
Instead of directly inserting the Passport middleware, you can use a custom callback in order to pass the req, res, next objects to the Passport function.
You could do something similar in your route handler/controller (this is taken directly from the Passport docs):
app.post('/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('/users/' + user.username);
});
})(req, res, next);
});

Nodejs and PassportJs: Redirect middleware after passport.authenticate not being called if authentication fails

I have no login page but rather I have a login form that appears on every page. I want to redirect user back to the same page they were on regardless of whether authentication was successful (with appropriate flash messages)
Take the following code:
app.post('/login', validateLogin, passport.authenticate('local-login'), function(req, res) {
var redirectUrl = '/';
if(req.body.to.length > 0){
redirectUrl = req.body.to;
}
console.log("THIS IS ONLY CALLED IF passport.authenticate() IS SUCCESSFUL");
res.redirect(redirectUrl);
});
I only see the final middleware above being called if authentication is passed. If it fails then passport appears to be redirecting me to /login in the form of a get request. In my app this page doesn't exist.
If I pass an additional options object as a parameter in the passport authenticate function then this works:
app.post('/login', validateLogin, passport.authenticate('local-login', {
successRedirect : '/', // redirect to the secure profile section
failureRedirect : '/signup', // redirect back to the signup page. THIS IS JUST FOR TESTING TO SEE IF THE REDIRECT ON FAIL WORKS.
failureFlash : true, // allow flash messages
}
));
But in doing this I lose the ability to choose where to redirect the user to. It seems that passport takes control over where the user is redirected to if authentication fails. How can I fix this? Or is it a bug? Must passport authenticate be the last middleware in the chain if authentication fails?
This is my local strategy function call:
//LOCAL LOGIN
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
console.log("IN PASSPORT");
if(email.length == 0 || password.length == 0){
console.log("FIELDS ARE EMPTY");
return done(null, false, req.flash('loginMessage', 'Fill in all values.'));
}
// 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);
console.log("db err");
}
// if no user is found, return the message
if (!user){
console.log("not user");
return done(null, false, req.flash('loginMessage', 'Incorrect details.')); // req.flash is the way to set flashdata using connect-flash
}
// if the user is found but the password is wrong
if (!user.validPassword(password)){
console.log("invalid pw");
return done(null, false, req.flash('loginMessage', 'Incorrect details.')); // create the loginMessage and save it to session as flashdata
}
// all is well, return successful user
console.log("All OK");
return done(null, user);
});
}));
You could use a custom authentication callback as described in the last paragraph there http://passportjs.org/guide/authenticate/.
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
// Redirect if it fails
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
// Redirect if it succeeds
return res.redirect('/users/' + user.username);
});
})(req, res, next);
});
I was running into the same issue where the redirect-calls , that follow successful Facebook Auth
passport.authenticate('facebook', ..)
.. were not being honored.
Based on 'local' passportJS Strategy - and a nice reminder of that from #ploutch's answer here .. I realized the key to getting it to work seems to be in this call:
req.logIn(user, function(err) {
...
}
For Facebook, this route setup worked for me:
app.get(
'/auth/facebook/callback',
passport.authenticate
(
'facebook',
{ failureRedirect: '/fbFailed' }
),
function(req, res)
{
var user = myGetUserFunc(); // Get user object from DB or etc
req.logIn(user, function(err) {
if (err) {
req.flash('error', 'SOMETHING BAD HAPPEND');
return res.redirect('/login');
}
req.session.user = user;
// Redirect if it succeeds
req.flash('success', 'Fb Auth successful');
return res.redirect('/user/home');
});
}
);
Full answer, including:
Middleware to set redirectUrl
Flash messages
Not returning values that won't be used
Just create a redirectTo value in your loginRequired middleware:
var loginRequired = function(req, res, next) {
if ( req.isAuthenticated() ) {
next();
return
}
// Redirect here if logged in successfully
req.session.redirectTo = req.path;
res.redirect('/login')
}
And then in your login POST:
router.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if ( err ) {
next(err);
return
}
// User does not exist
if ( ! user ) {
req.flash('error', 'Invalid email or password');
res.redirect('/login');
return
}
req.logIn(user, function(err) {
// Invalid password
if ( err ) {
req.flash('error', 'Invalid email or password');
next(err);
return
}
res.redirect(req.session.redirectTo || '/orders');
return
});
})(req, res, next);
});
I know this might still be a problem for some people like me who tried all the suggested options without any success.
In my case, as it turned out, I was getting the error because my req.body object was always empty. I had my body parsing middleware set up correctly so it didn't make sense why this was happening.
After more research I found out that the enctype I was using for my forms(multipart/form-data) isn't supported by body-parser - see their read me - after switching to a different middleware, multer, everything worked smoothly.

Resources