I made a silly mistake running my code today and I had to share it

I’m creating a sample to-do list application using the Jira API for an article I’m writing and I have been getting a very frustrating bug since yesterday.

The Jira API requires you to attach an access token to the Authorization header when making requests. Since the HTTP protocol doesn't remember anything between requests (it's ✨stateless✨), I'm using the session storage to keep track of this token.

Persisting access tokens in the session

I have this configuration in my middleware to make the session data accessible to incoming requests using the express-session npm package:

app.use(session({
  secret: 'my-secret',
  resave: false,
  saveUninitialized: true,
  cookie: { 
    secure: false, // For http websites
    maxAge:60 * 60 * 1000 // Expire after 3600 sec
   }
}))

When Jira returns an access token, I store it in the session like this:

app.get("/auth/callback", async (req, res) => {
  try {
    const code = req.query.code;
    // Perform the OAuth token exchange
    const accessToken = await performOAuthExchange(code, clientId, clientSecret, redirectUri);
    // Save the access token in the session
    req.session.accessToken = accessToken;

    // Redirect to your frontend page
    return res.redirect(FRONTEND_REDIRECT_URL); 
  } catch (error) {
    return res.json({ error: error.message });
  }
});

Incoming requests can then access and use the token until it expires.

const accessToken = req.session.accessToken;

The mystery: The "User is not authenticated" error

My access token was working in some requests but failing in others and the pattern was not predictable.

I was getting a “User is not authenticated” error, an error message I was throwing when the session did not have an access token.

const accessToken = req.session.accessToken;
if (!accessToken) {
   throw Error("User is not authenticated.");
}

This error was puzzling because I was confident that the session had an expiration time of 3600 seconds, and it shouldn't have gone invalid that fast.

What was going wrong?

I searched the internet like mad thinking I had configured the session wrong or I wasn't saving it correctly but couldn't find anything.

Earlier today, I hopped back into it, thinking that maybe with a less tired brain, I'd see things differently. And you know what? I was running the wrong npm script!

See, in my package.json file, I have these two scripts:

"scripts": {
  "dev": "node index",
  "watch": "nodemon index"
}

I was running the watch script which uses Nodemon to start the server.

Nodemon watches for changes in your code and automatically restarts the server when you save these changes.

So, what does this have to do with the missing access token?

By default, express-session stores session data in a new MemoryStore instance, which essentially means it saves this data on your computer's disk. Unfortunately, this data is volatile and gets wiped clean when the server restarts or shuts down.

So every time I was resaving my file, the session data including the access token was getting deleted.

The simple fix

The solution was simple: I needed to run the server using the node command.

npm run dev

This way, the server wouldn't restart unless I told it to, and my session data would stay intact.

A better fix would be storing the session in a memory-based caching application like Redis.

Comic template from Balloon Comics