Skip to content

Express Authentication Overview

Authentication confirms who a user is; Express doesn't ship with an opinionated way to do it, giving you a choice between several well-established strategies. This lesson maps out the landscape before diving into each in detail.

The Main Authentication Approaches

Session-based authentication stores a session identifier in a cookie, with the actual user data kept server-side (in memory, Redis, or a database). Token-based authentication (typically JWT) issues a signed token to the client, which sends it back on every request, no server-side session storage needed.

Passport.js is a middleware framework that standardizes many authentication strategies (local username/password, Google, GitHub, and more) behind one consistent API, rather than being a strategy itself.

// Session-based (server remembers the session)
app.post('/login', (req, res) => {
  req.session.userId = user.id; // requires express-session
  res.json({ loggedIn: true });
});

// Token-based (client remembers the token)
app.post('/login', (req, res) => {
  const token = jwt.sign({ sub: user.id }, process.env.JWT_SECRET);
  res.json({ token });
});

Sessions rely on server-side storage keyed by a cookie; JWTs are self-contained and verified without any server-side lookup.

Choosing an Approach

Traditional server-rendered site  -> sessions (express-session)
Stateless REST/mobile API          -> JWT
Multiple auth providers (Google, GitHub) -> Passport.js strategies
Third-party login without password  -> OAuth 2.0
  • Sessions fit naturally with server-rendered apps that already use cookies for everything else.
  • JWTs fit stateless REST APIs, especially ones consumed by mobile apps or SPAs across different domains.
  • Passport.js is a strategy-agnostic toolkit, it can implement either sessions or tokens under the hood.
  • OAuth is specifically for delegating authentication to a third party (Google, GitHub, etc.), not a general auth mechanism on its own.

Authentication Strategy Cheatsheet

A quick comparison of the main approaches covered in this section.

Strategy State Best Fit
Sessions Server-side (memory/Redis/DB) Server-rendered apps, traditional websites
JWT Stateless (self-contained token) REST APIs, mobile apps, SPAs
Passport.js Depends on strategy used Apps needing multiple login providers
OAuth 2.0 Delegated to a third party "Sign in with Google/GitHub" flows

Authentication vs Authorization

Authentication answers "who are you?", verifying identity, typically via credentials, a token, or a session. Authorization answers "what are you allowed to do?", checking permissions or roles after identity is already established. Middleware for each is usually separate: an authenticate middleware first, then an authorize(role) middleware after.

app.delete('/posts/:id', authenticate, authorize('admin'), deletePost);

Password Storage Basics

Regardless of which session/token strategy you choose, passwords must never be stored in plain text. Use a slow, purpose-built hashing algorithm like bcrypt, never a fast general-purpose hash like MD5 or SHA-256 alone.

const bcrypt = require('bcrypt');

const passwordHash = await bcrypt.hash(plainPassword, 12);
const isValid = await bcrypt.compare(plainPassword, passwordHash);

Common Mistakes

  • Storing passwords in plain text or with a fast, non-purpose-built hash.
  • Confusing authentication (identity) with authorization (permissions).
  • Choosing JWTs for a traditional server-rendered site just because they're popular, when sessions fit more naturally.
  • Implementing OAuth manually instead of using a mature library like Passport.js.

Key Takeaways

  • Sessions and JWTs are the two dominant authentication state strategies in Express apps.
  • Passport.js standardizes many strategies behind one consistent middleware API.
  • OAuth delegates identity verification to a third-party provider like Google or GitHub.
  • Authentication (who you are) and authorization (what you can do) are separate concerns with separate middleware.

Pro Tip

Pick session-based auth by default for classic server-rendered apps and JWTs by default for pure APIs, switching strategies mid-project is far more work than choosing the right one up front based on how the client actually consumes your API.