Sessions versus Tokens


Uncategorized

Updated Aug 19th, 2023

Two broad but radially different paradigms.

When I first started out I was mistakenly confusing the debate of sessions versus JWTS for cookies versus JWTs. And that’s because, not only do session ids get automatically stored in cookies by the browser, you can use cookies to store JWTs. Cookies can be used in both. Storing tokens in cookies may be preferred over Local Storage.

Big Picture: These concepts help a server remember or trust a visitor.

Cookies

Form MDN: “An HTTP cookie (web cookie, browser cookie) is a small piece of data that a server sends to a user’s web browser. The browser may store the cookie and send it back to the same server with later requests. Typically, an HTTP cookie is used to tell if two requests come from the same browser—keeping a user logged in, for example.”

Cookies are set in the request header. Persist on page refresh and are sent on every request.

The most common way cookies are used in the real world is with sessions. A library like express-session can help manage although this lacks persistence on page refresh so you will often store session cookies in a database.

When setting a cookie you can make the cookie inaccessible from client-side JavaScript (which means someone cannot read or overwrite) which makes things a little more secure with the following:

{httpOnly: true}
// There is also for https only
{secure: true}
// and to set a time limit
{maxAge: number} // in milliseconds

Set a cookie in an express-app with res.cookie

res.cookie('honey', 'yummy')

You can set browser cookies all day long but to read a cookie you’ll want to reach for a library like cookie-parser

The biggest security threat for cookies is a cross-site-request-forgery attack, csrf, and you can install a library like “csurf” to help implement some protection.

You can see cookies in the browser in the chrome dev tools in the “applications” tab.

Sessions

Sessions are essentially “sessions-ids” (long strings generated by the browser) stored as cookies and sent to the browser on each request. There is no sensitive data stored in these ids but an application will save them either in memory or to a database with sensitive or non-sensitive data tied to that user.

The example in Brad’s video is an application shows a user’s name when they’ve logged in. If they close and reopen the browser tab the name is still there because, on the server-side code of the application there is logic saying, if see if there is an existing session id for this user, if so then show the data tied that user, if not then show the username as guest.

The stytch article mentions this in regard to sessions: While session-based authentication is very reliable, at scale it can begin to introduce latency and performance issues. 

Tokens

JSON Web Tokens

Alternative to sessions that accomplishes the same thing, trust requests and remember users.

These are platform-agnostic. The server doesn’t store data in memory or database. The client stores independently, this token, typically in local storage.

How can we trust this? It’s cryptographically secure. Uses a secret variable to generate the

The popular library here is “jwt” where you can leverage the “sign” and “verify” methods.

What a token is made up of. It is important to understand the server creates/signs a token and this leverages a secret environment variable which:

If you paste a token in a site like jwt.io, the 1.) header, 2.) payload and 3.) signature. It is important to note that the payload is not encrypted. Only the server can create and verify token but anyone can read the payload if they have the token.

Sending the token

It it common to send the token in the request header with Authorization: bearer<token>

const requestOptions = {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    // Set the Authorization header with the JWT
    'Authorization': `Bearer ${jwt}`,
  },
  body: JSON.stringify(postData),
};

Extracting the token from the Header in Node/Express

const express = require('express');
const app = express();
const jwt = require('jsonwebtoken');
app.post('/postEndpoint', (req, res) => {
  const authHeader = req.headers['authorization'];
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    // Handle unauthorized access
    return res.status(401).json({ error: 'Unauthorized' });
  }
  const token = authHeader.split(' ')[1]; // Extract the token from the "Bearer <token>" format
  // Verify and decode the JWT token
  jwt.verify(token, 'your_secret_key', (err, decodedToken) => {
    if (err) {
      // Handle invalid or expired token
      return res.status(403).json({ error: 'Forbidden' });
    }
    // Token is valid, and you can access its contents through `decodedToken`
    // For example, decodedToken.userId, decodedToken.username, etc.
    // Process the POST request here
    // Return a response (if needed)
    res.json({ message: 'Request successful' });
  });
});
app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

As a Test

The downside of using the browser’s local storage to store JWTs: The data is not sent automatically on every request. So we need to send with JavaScript and cannot lock down the ability to access these values like with “cookies/httpOnly-only”. If a malicious code gets into your application they can just use localStorage.getItem(“yourExtremelyValuableToken”) and see the payload (wth jwt.io) and make requests on your behalf.

Open a browser tab go to the google homepage and open the chrome dev tools looking at the application tab. You’re likely to see a list of cookies with names that don’t mean much. Not that some, but not all, are httpOnly and secure. Expiration dates vary from a few days to next year. Pasting them into jwt.io they all say invalid signature? The reason is not all cookies are json web tokens. So I’ll try another site, keybr.com. Notice the cookies update to only show the current tab and website you are on. Holy moly they store a lot of cookies. Still none valid JWTs. So I went to one of my own demo projects that uses NextAuth.js and I see a “session-token” cookie that is valid. By default there is nothing in these token payloads but on one of my sites I am sending the username on the payload and this shows on jwt.io

The downside of using cookies with JWTs is that this is browser-specific and it now your endpoints are not super platform-agnostic. They would not work with iOS or Android native app.

Sources

Brad Schiff Full Stack from Stack Course and Cookies, Session, JWT video.

Mozilla (here)

W3 here

Web Dev Cody Video is pretty good

Stytch article here