Passport user handlebars template - node.js

app.use(passport.initialize());
app.use(passport.session());
app.use(function(req, res, next) {
res.locals.login = req.isAuthenticated();
res.locals.user = req.user;
console.log(res.locals.user);
next();
});
app.use('/', indexRouter);
Here above is the code i used to set my user value in the res.locals.user field.
The console gives me:
{
_id: 5fc3e49c0bfce754c8f923c9,
email: 'myemailadres#hotmail.com',
name: 'Jarne',
password: 'passwordhash......',
__v: 0
}
In my handle bars i'm now trying to use this user variable.
{{#if login}}
welkom
{{user.name}}
TESTER
{{tester}}
This doesn't work. This stays empty..
I can use {{user}} but this give me back the Json format.
How can i access the fields like name, email,... saw some examples in EJS with user.name, user._id but this doesn't seem to work in my handlebars.
Also tried to do this via the router. but same result with tester (tester.name doesn't show anything).
router.get('/', function (req, res, next) {
res.render('index', {
title: '---- Dashboard---- HOME',
tester: req.user
});
});
I also tried to set a res.locals.username = req.user.name
But this resulted in an error.

I found following solution:
{{#each user}}
{{name}}
{{_id}}
{{email}}
{{/each}}
Looks like i needed to loop through the user object.

In my case when I printed out {{user}} in navigation.hbs it showed the JSON string instead of object
This happened because I used a mongo version that requires lean() function after searching the user
add .lean function
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user);
}).lean()
})
hope it helps!

Related

How to show logged in user info in all routes NodeJS

So this is my code that shows if user session exists and if it exists then it renders user info with the file so I can display the logged in user info there.
app.get('/', async(req, res) => {
if(req.session.user && req.cookies.user_sid){
let user = await User.findOne({username: req.session.user.username});
res.render('index', {user});
} else {
res.render('index');
}
});
But now I want to display user info and in another routes. So in my opinion it would be stupid to repeat again that if statement. What alternative could be there?
It's best to repeat that statement and make the DB call again. In the time between the first request and any subsequent requests, the user may have been logged out (such as by cookies expiring) or user data might have been changed in the database, so it's best to not cache the data and check every time.
Method 1
Add a user details middleware ,which checks if user details is available in session or not and then updates the session object if not available.This way you will avoid redundant calls to db across routes.
app.use(function (req, res, next) {
if(req.session && req.session.userDetails === undefined){
const userDetails = await User.findOne({username: req.session.user.username});
req.session.userDetails = userDetails || undefined;
//optional
req.user = {};
Object.assign(req.user, req.session.userDetails);
}
next()
})
You can pass the userDetails in all your routes with reference to req.user or req.session.userDetails, something like
app.get('/profile', async(req, res) => {
res.render('profile', {user : req.user});
})
Method 2
You can also save the user details in session when the user successfully logs in and use the session reference in all routes, something like
app.post('/authenticate', async (req, res) => {
const userDetails = await User.findOne({ username: req.body.username, password: req.body.password });
if (userDetails.length > 0) {
//when authentication is successsful
req.session.user = userDetails;
}
});

Pug Express req.user Interpolation

I am having a really hard time accessing a variable in Pug from Express.
My route looks like:
router.get('/', ensureAuthenticated, function(req, res, next) {
res.render('profile/profile', {user: req.user});
});
My template looks like:
.card
.card-body
h4.card-title Local Profile
if (user)
p.card-text
strong.pr-2 ID: !{user.userEmail}
br
br
strong.pr-2 Name:
= user
br
br
strong.pr-2 Email:
= user
br
br
strong.pr-2 Password:
span.text-muted= user
a.btn.btn-default(href="/profile/edit") Edit
p.card-text
small.text-muted Last login
= user
The user object looks like:
{UID: 5, userEmail: "rtester#testing.com", userPassword: "bd4eb56b41fc3663dfe2761ff34621a544ecfe27", userLastLogin: "2017-11-20T22:18:13.000Z", userToken: "cae45ae7e68ef8024d4ad5b56c68f263"}
If I include just user without stringifying, then I get Object object. If I stringify I can output the object, but trying to access a property in the object gives me nothing.
But if I
console.log(x.userEmail)
after
var x = !{JSON.stringify(user)}
then I get the property.
Any help would be fantastic!!
use user.userEmail instead of !{user.userEmail}
this worked fine for me,
app.get('/', function(req, res, next) {
var u = {UID: 5, userEmail: "rtester#testing.com", userPassword: "bd4eb56b41fc3663dfe2761ff34621a544ecfe27", userLastLogin: "2017-11-20T22:18:13.000Z", userToken: "cae45ae7e68ef8024d4ad5b56c68f263"}
res.render('index', { user: u });
});
,
html
head
title= user.userEmail
body
h1= user.userEmail
The issue actually had nothing to do with Pug or Express. It was with the way that Passport was setting the req.user variable after finding the user using Bookshelf.js.
There were some additional nested attributes in the object. This separate answer led me to a solution to isolate the information I needed. Cannot get properties of req.user
The way to access the information in Pug is the same as laid out in the docs.

Can I use res.redirect and res.send simultaneously in Node.js?

I want to print user's info on the landing page after they get logged in. I am using res.redirect to redirect to landing page i.e index1.html after a user get successfully logged in.
Now I want to get user info. Here is my node.js code:
app.post('/login-feedback', function (req, res) {
dbConn.then(function(db) {
delete req.body._id; // for safety reasons
var dd = db.db("customermanagement");
dd.collection('admin').findOne({username: req.body.username, password: req.body.password}, function(err, user){
if(err) {
console.log(err);
}
else if(user){
req.session.user = user;
console.log( req.session.user.username);
res.status(200);
res.redirect('/index1.html');
}
else {
res.redirect('/login1.html');
}
});
});
});
I would love if you will tell me explicitly everything step by step.
If you want to send data to a static file, like an html file, you should use res.render :
res.render('/login1.html', {user: req.session.user.username})

Express rendering in/after post

Web app routing novice here. I've got a relatively simple app working based on Node/Express.
The main index of the app is a list of user names, and IDs.
localhost:242
Every user ID is a link to a page with a form to enter additional metadata about that particular user.
localhost:242/user/1398
Everything is working correctly. When I enter some metadata about the user, and submit the form, a POST route is executed, and then I'm redirected back to the original page I was on. Instead of using a redirect, I'd like to be able to re-render that same page, so I can pass some confirmation messages indicating what was just changed.
Here's a simplified version of my code.
// Module imports
var express = require('express');
var validator = require('express-validator');
var router = express.Router();
router.get('/', function(req, res, next) {
db.dataTalk(queryUsers, null, config.connection, function(err, result) {
var listUsers = result;
res.render('index', {
// Index with list of users
title: 'Page Title',
listUsers: listUsers
});
});
});
// GET /user/:id
router.get('/user/:id', function(req, res, next) {
db.dataTalk(queryUserDeets, [req.params.id], config.connection, function(err, result) {
// Details for a single user
var userDetails = result;
res.render('user', {
title: req.params.id,
userDetails: userDetails
});
});
});
// POST /user-update
router.post('/user-update', function(req, res) {
// Here goes a lot of logic to validate the form contents, and update the appropriate databases
// Redirect back to the user page, which should display the updated metadata
res.redirect('/user/' + req.body.userInitID);
});
module.exports = router;
Extract a helper function you can call from both places. Here's one that sticks very close to your original code.
function renderUserPage (userId, res) {
db.dataTalk(queryUserDeets, [userId], config.connection, function(err, result) {
// Details for a single user
var userDetails = result;
res.render('user', {
title: userId,
userDetails: userDetails
});
});
});
// GET /user/:id
router.get('/user/:id', function (req, res) {
renderUserPage(req.params.id, res)
});
// POST /user-update
router.post('/user-update', function(req, res) {
// Here goes a lot of logic to validate the form contents, and update the appropriate databases
// Redirect back to the user page, which should display the updated metadata
renderUserPage(req.body.userInitID, res);
});
Aside: You are ignoring errors from database calls. If you don't at the very least log something for each and every error passed to an async callback, you are going to be blind to problems that would otherwise be straightforward to debug.

Express redirect and locals

I'm using passportjs in order to log user and I try to redirect them after the password verify is complete with angularjs.
But I keep getting "Cannot read property 'name' of undefined" when I try to get user data on another page
Snippet:
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, usr, info) {
res.locals.user = req.usr;
res.json({msg:true});
return next();
})(req, res, next);
});
And somewhere else I try to do something like this:
user.find({name: req.user.name },function(err,q){
Which fire the error "Cannot read property 'name' of undefined"
You have to provide passport with serializeUser and deserializeUser functions in order for passport to store the user in the request object. For more info, check the guide:
http://passportjs.org/guide/configure/
Specifically, look at the bottom section on Sessions. Also, consult this similar question:
Do I implement serialize and deserialize NodesJS + Passport + RedisStore?
In your case, it looks like you're using name instead of id to identify users, so wherever you configure passport, you will probably want to do something like:
passport.serializeUser(function(user, done) {
done(null, user.name);
});
passport.deserializeUser(function(name, done) {
user.find({name: req.user.name }, function(err, user){
done(err, user);
});
});

Resources