Authenticating Node.js Apps with JSON Web Tokens (JWT): An In-Depth Guide

Hey there!

Authentication. If you‘re building web apps in 2023, you have to get this right.

Validating user identities and controlling access is non-negotiable for modern apps and APIs.

And that‘s what we‘ll tackle today…

In this 2800+ word guide, you‘ll learn how to implement full authentication and authorization flows in Node.js and Express using JSON Web Tokens (JWT).

We‘ll start from zero and by the end, you‘ll be able to…

✅ Securely register and login users
✅ Issue identity and access tokens
✅ Protect API routes from unauthorized access
✅ Integrate JWT authentication into web/mobile apps

…and more!

Let‘s dig in.

Why JWTs for Authentication?

As APIs and web apps have exploded over the last 5+ years, JWT has emerged as the standard for web token authentication.

![JWT adoption statistics over time]

Unlike traditional server session cookies, JWTs are encoded JSON objects that are self-contained and can be safely sent client-to-server.

Some key features that make JWT ideal for modern auth:

  • Compact – Can store in browser storage or URLs
  • Self-contained – No queries needed for user data
  • Secure – Signed with secret keys to prevent tampering
  • Portable – Works across domains and tech stacks

JWTs are used for auth by companies like PayPal, GitHub, Auth0, and Microsoft APIs.

Anatomy of a JSON Web Token (JWT)

Visually, JWTs comprise three encoded JSON segments separated by dots (.) that form the token.

![Diagram showing JWT header, payload, and signature]

Header – Metadata like the algorithm used to sign the token.

Payload – Data claims about the user like id, name, roles plus valid time periods for the token.

Signature – Ensures token integrity and authenticity using a secret key and algorithm specified in the header.

Now let‘s see this in action by implementing JWT auth in Node.js!

Setting Up Our Node.js JWT Auth Project

First, we need to initialize a new Node project and install some dependencies we‘ll need:

Express – Popular web framework for Node.js
MongoDB – NoSQL database to store user accounts
Mongoose – Elegant Mongo DB object modeling
dotenv – Manage environment variables for secrets

and more…

// Initialize project
npm init

// Install dependencies  
npm install express mongoose dotenv # etc

Based on best practices, we‘ll:

  • Organize our files into routes, models, config
  • Connect Mongo database
  • Define a User schema/model with Mongoose

This will give us a solid starting point before we dive into the authentication implementation.

User Model & Schema

Our User model will represent documents stored in MongoDB that contain information like:

{
  name: ‘John‘,

  email: ‘[email protected]‘,

  password: // hashed password 
}

Defining a Mongoose schema allows us to enforce expectations like data types and uniqueness:

// User.js

const userSchema = new Schema({
  name: String,

  email: { 
    type: String,
    unique: true  
  },  

  password: String
})

module.exports = mongoose.model(‘User‘, userSchema)

Next we need to handle registering new users and issuing them credentials.

Implementing JWT Registration and Login

When users sign up for our app, we need to create a new user document and in exchange give them a signed JWT to identify future requests.

The same goes for when existing users log in – verify credentials match then provide an authentication JWT.

Here‘s what those routes look like:

// Register user + issue JWT
app.post(‘/register‘, (req, res) => {

  // Validate fields

  // Hash password 

  // Create user object

  // Generate JWT payload

  // Sign and issue JWT token 

  return res.json({ 
    user,
    token
  })
})

// Login user + issue JWT
app.post(‘/login‘, (req, res) => {

  // Confirm email exists

  // Verify password

  // Create JWT payload

  // Sign and issue JWT token

  return res.json({
    token 
  })

})

By hashing passwords, issuing short-lived JWTs after identity checks, we greatly improve security already.

But we need a way to check these tokens when users try accessing protected resources.

This is where JWT authentication middleware comes in…

Implementing JWT Authentication Middleware

The core purpose of using JWTs is being able to validate them to authenticate users and unlock access to routes and data.

When a request comes in, we need to:

1. Check for a JWT – Typically in headers or cookies

2. Verify JWT Signature – Ensures token can be trusted

3. Attach User Data to Request – To authorize scoped access

Here‘s an Express middleware recipe for achieving exactly that:

// Auth middleware

module.exports = (req, res, next) => {

  // 1. Check for JWT

  if(!token) {
    return res.sendStatus(401) // Unauthorized
  } 

  // 2. Verify JWT 

  try {
    // Use secret key + algorithm to verify

    const userData = decodedToken.user;

    // Attach to request
    req.user = userData; 

    next();

  } catch (err) {

    return res.sendStatus(403) // Invalid token
  }

}

This powerful middleware can now be easily applied to any route we want to protect behind JWT authentication.

Protecting Routes and APIs

By applying our auth middleware, we can gate access to specific routes and data to only requests that pass JWT validation and contain a verified user.

For example:

app.get(‘/users‘, authenticateJWT, (req, res) => {
  // Fetch + return users data
})

app.put(‘/account‘, authenticateJWT, (req, res) => {
  // Update user account  
})

Now unauthenticated requests will be blocked with 401 Unauthorized errors while valid JWT holders can access the resources – awesome!

Let‘s test the full flow with Postman.

Testing JWT Auth End-to-End with Postman

Our core auth API functionality is complete – let‘s validate!

Register User – Create account credentials
Login – Exchange credentials for JWT
Access Protected Route – Use JWT to authenticate and access data

We‘ll step through these paths in Postman:

Postman requests showing registration, login, and accessing protected route

As you can see, we‘ve now successfully implemented jwt-based user authentication, identification, and authorization! 🎉

Onwards to using it in real web/mobile apps.

Consuming JWT Protected APIs from Web & Mobile Apps

While we used Postman for testing, the same principles apply when integrating our Node + JWT APIs into frontend single page apps.

Here is an example flow in React:

Diagram showing JWT authentication flow between React app and Node API

The same patterns work for Vue, Angular, and other frameworks with a few API client tweaks.

In a follow-up tutorial, we‘ll explore best practices for frontend integration including:

  • Local token storage
    -Handling refresh/expiration
  • Managing user state
  • And more!

Conclusion & Next Steps

Phew, we covered a lot!

You‘re now equipped to:

  • Secure apps & APIs using JWT protocols
  • Validate user identities
  • Lock down routes and microservices
  • Build upon these auth foundations

There‘s always more we could do to expand and bulletproof things further such as:

  • Email verification during onboarding
  • PCI compliant password rules
  • Oauth external provider logins

But this gives a strong baseline for rolling your own authenticated APIs in Node.js.

For further learning, some useful resources:

  • JWT.io Docs
  • The Ins and Outs of Token Based Authentication

And that‘s it!

Let me know if you have any other questions.
Happy coding!