Error encountered in accessing object property with Handlebars

Upon successful authentication using the passport module, a template was rendered with the req.user object as shown below,

app.get('/', (req, res) => {
  console.log(`Router get user: ${req.user}`);
  console.log("Router get user of type: " + (typeof req.user));
  res.render('index', {
    layout: false,
    user: req.user,
  });
});

I verified the contents of req.user through console.log, and the user object appeared as expected,

Router get user: {
  _id: new ObjectId("629e3821bfb2869c42ac3c4b"),
  username: 'me',
  password: '123'
}

The second console.log indicated that the type of req.user is indeed an object,

Router get user of type: object

After converting the req.user to a string,

app.get('/', (req, res) => {
  console.log(JSON.stringify(req.user));
  console.log("Router get user of type: " + (typeof req.user));
  res.render('index', {
    layout: false,
    user: req.user,
  });
});

the output transformed into,

{"_id":"629e3821bfb2869c42ac3c4b","username":"me","password":"123"}
Router get user of type: object

When I attempted to log req.user.username like this,

app.get('/', (req, res) => {
  console.log(req.user.username);
  console.log("Router get user of type: " + (typeof req.user));
  res.render('index', {
    layout: false,
    user: req.user,
  });
});

I encountered the following error,

TypeError: Cannot read properties of undefined (reading 'username')
    at /Users/Wei/github/play-js/express/authentication/src/app.js:87:24

However, when I used user.username in the template file, it did not display the username.

  <body>
    {{#if user}}
      <h1>WELCOME BACK {{user.username}}</h1>
    {{/if}}
  </body>

But when I replaced {{user.username}} with {{user}}, the user object displayed correctly,

  <body>
    {{#if user}}
      <h1>WELCOME BACK {{user}}</h1>
    {{/if}}
  </body>
WELCOME BACK { "_id": "629e3821bfb2869c42ac3c4b", "username": "me", "password": "123" }

As per the Handlebars Documentation, dot-separated paths are allowed in Handlebars expressions.

Hence, what could be causing this issue?

Below is the complete code illustrating how I set up the express server and implemented passport authentication,

// connect to MongoDB
const mongoDB = process.env.DB_URI;
mongoose.connect(mongoDB);
const db = mongoose.connection;
db.on('error', console.error.bind(console), 'MongoDB connection error');

// Schema & Model
const userSchema = new Schema({
  username: {
    type: String,
    required: true,
  },
  password: {
    type: String,
    required: true,
  }
});
const User = mongoose.model('User', userSchema);

// Express server
const app = express();
app.set('views', path.join(__dirname, 'views'));
const eh = handlebars.create(); // ExpressHandlebars instance
app.engine('handlebars', eh.engine); // register the engine() function
app.set('view engine', 'handlebars');

// Middleware
app.use(morgan('dev')); // logger
app.use(session({
  secret: 'cats',
  resave: false,
  saveUninitialized: true,
}));
app.use(passport.initialize());
app.use(passport.session()); // this middleware will set cookie in client computer for each session.
app.use(express.urlencoded({
  extended: false,
}));

// Verify username & password in our database
// Register the LocalStrategy to the passport.
passport.use(
  new LocalStrategy(function verify(username, password, done) {
    User.findOne({username: username}, (err, user) => {
      if (err) return done(err);
      if (!user) return done(null, false, {message: 'Incorrect username'});
      if (user.password !== password) return done(null, false, {message: 'Incorrect password'});
      return done(null, user);
    });
  })
);

// Only store user._id in the cookie.
passport.serializeUser(function(user, done) {
  console.log(`serialize: ${user._id}`);
  done(null, user._id);
});

// Get the user object from database by searching user._id.
passport.deserializeUser(function(_id, done) {
  console.log(`deserialize search for: ${_id}`);
  User.findById(_id, function(err, user) {
    console.log(`deserialization find user: ${user}`);
    done(err, user);
  });
});

// router
app.get('/', (req, res) => {
  console.log(JSON.stringify(req.user));
  console.log("Router get user of type: " + (typeof req.user));
  res.render('index', {
    layout: false,
    user: req.user,
  });
});

app.post('/log-in', passport.authenticate('local', {
  successRedirect: '/',
  failureRedirect: '/',
}));

Answer №1

Collaborating with @Phil, we successfully identified the bug within the deserializeUser() function.

// Retrieve the user object from the database by searching for user._id.
passport.deserializeUser(function(_id, done) {
  User.findById(_id, function(err, user) {
    console.log(`Located user during deserialization: ${user}`);
    done(err, user);
  });
});

passport.deserializeUser() essentially retrieves the user._id stored in cookies and queries the complete user object from MongoDB, which will subsequently become our req.user.

The issue arises because User.findById() returns a mongoose Document object lacking its own username property.

What is required here is a plain JavaScript object, which can be achieved by enabling the lean option immediately after calling the findById() function.

// Retrieve the user object from the database by searching for user._id.
passport.deserializeUser(function(_id, done) {
  User.findById(_id, function(err, user) {
    console.log(`Located user during deserialization: ${user}`);
    done(err, user);
  }).lean(); // now it returns a plain JavaScript object
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Obtaining IP Address with Jquery or Phonegap: A Guide for Cross Platform Development

Is there a way to retrieve the IP address using Jquery or Phonegap? I am currently working on an application that is compatible with Android, iPhone, and Windows platforms. I'm in need of a solution to locate the IP address of a request. ...

Modifying the file name during the download process using AngularJS

Looking for a solution to download a file from an ajax GET request in angularjs? Currently, I am using an invisible iframe to trigger the "Save as" popup for the downloaded file. However, I need to change the name of the file before the popup appears. If ...

Using the keyboard to access the close button is not an option for me

Despite adding tabindex=0 to the close image button with <img src= "close img link" tabindex="0" alt= "close" title="close" aria-labelledby="close" />, I am unable to use the keyboard to access the bu ...

Why does socket.io have trouble connecting when clients are using different IP addresses on separate wifi networks?

I've encountered an issue where socket.io won't connect when clients are on different wifi networks (ip address) using my self-configured Ubuntu Nginx server. Strangely enough, it works perfectly fine on a pre-configured Heroku server. Here is a ...

Problems with select tag change events

I encountered an issue with select tag onChange events. When I select a value from the select tag, it should display in a textbox that is declared in the script. It works perfectly when I remove the class "input" from the select tag, but I prefer not to re ...

Concurrent Openlayers maps in Rails - A Viable Option

I've been playing around with Openlayers maps on my website. The map functionality is working perfectly, but I'm having trouble getting the maps to display correctly on my page. My goal is to show a map in each search result on my screen - one m ...

What is the best way to style nodes in a Force Directed Graph using CSS in D3?

Currently, I am developing a Force Directed Graph using D3 and managed to make it work with svg circles for the nodes. However, when attempting to use div elements along with CSS left and top properties, the positioning of the nodes seems to be incorrect. ...

List of sortable components

I have successfully implemented a filters section using vue.js to display dynamic responses based on user-selected filters. The components, in this case, represent cars with attributes such as price and brand. Now, I want to add two more filters that will ...

Attempting to send an object to an external route file through Node.js and Express

I have a nodeapp with an external route file and I'm attempting to pass the object clientMap to the external router. Whenever I try to pass it using syntax similar to examples, I always encounter the following error: Error: Cannot call method &apo ...

How to send a function from a parent component to a child component in Vue.js

I'm trying to figure out how to pass the parent component's method to the child component. Here's the scenario: when I click the confirm button on my modal (child component), it needs to send the data from the parent form (parent component). ...

Dynamically adjusting the background color of table elements using JavaScript

Snippet code copied from this link In my app, clicking on the Add New Item button dynamically adds rows. Clicking on any number in the table populates it in the corresponding row. Hovering over the 1st row changes its background color to green along with ...

Vue.js known as javascript hooks

I've been working on connecting Vue.js with velocity.js. Following a guide, I found an example that doesn't use a named transition. Currently, my transition code looks like this: <transition name="collapse"> https://v2.vuejs.org/ ...

What is preventing Angular's $scope from being modified?

Recently, I decided to delve into the world of AngularJS. During my first tutorial sessions, I attempted to rename the $scope variable and quickly realized that it caused everything to break. phonecatApp.controller('PhoneListCtrl', function($sco ...

Display chosen preferences in an Angularjs dropdown alongside additional options

I'm currently developing a blogging software and have implemented an AngularJS dropdown for selecting post terms. Here's the code: <select multiple="multiple" name="terms" ng-model="post.data.attributes.term_ids" required> ...

Using `texture.needsUpdate = true` in Three.js can cause significant performance issues due to its slow execution

I am currently working on a project involving a Three.js scene where I need to update textures after a certain period of time. However, I have noticed that updating the textures is causing a significant slowdown in the FPS, dropping it to 1-2 FPS for sever ...

Tips for creating line breaks in Google Chart tooltips

I'm having trouble breaking a line in the code snippet below. Here is the complete code: <html> <head> <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script> <script ...

Is it possible to access variables within functions from outside of them?

async function checkPlayersOnline() { const response = await fetch("http://ip:port/dynamic.json"); const playersData = await response.json(); console.log(playersData.clients); } Is it possible to access the playersData inside another func ...

Ways to integrate search features into the SearchAppBar within Material UI?

Currently, I have a simple react application that enables users to search for and view movies from the Movie Database API. However, I am facing an issue: I want to connect my custom React Function SearchMovies() to a specific material ui component known a ...

Unable to successfully append a unique character using jQuery

I need help with displaying special characters, specifically the degree symbol (°), in JavaScript/jQuery. Despite my efforts, it is not showing up correctly on the webpage. How can I fix this issue and ensure that the degree sign appears as desired? I ...

Encountering an "undefined" error while implementing a registration system in Node.js and attempting to retrieve

Having recently delved into the world of javascript and nodejs, I am currently working on developing a registration system. The issue I'm facing is related to an error message indicating that "isEmail" is undefined. I have included my form validator a ...