Overview

28 Adding API security

This lesson explains how to add practical security to API routes when clients may not use browsers or cookies. It introduces a progression of strategies—starting with a single shared token, then moving to per-user API keys, and culminating with JSON Web Tokens (JWTs)—to verify identity, control access, and reduce risk. The overarching goal is to protect data while keeping the API usable for scripts, mobile apps, and other non-browser consumers.

The first approach applies a simple middleware that checks a shared token supplied as a query parameter, ideally stored in an environment variable. While quick to implement, it becomes risky as more consumers share the same secret. The lesson then strengthens this by generating a unique API token for each user, adding an apiToken field to the user model and creating it automatically on save. Middleware is updated to validate incoming requests by looking up users by their token, enabling per-user auditing and rate insights, yet still exposing tokens in URLs and requiring careful distribution.

The most robust option presented is JWT-based authentication. An API login endpoint authenticates credentials (e.g., with Passport’s local strategy) and returns a signed, time-limited token embedding the user’s ID. Subsequent requests send this token in headers, and verification middleware decodes it, confirms integrity, and ensures the user exists before allowing access. This approach removes reliance on cookies, but requires clients to store tokens and may require adjustments to existing client code. The lesson emphasizes choosing the right trade-off: start simple when appropriate, adopt per-user tokens for better accountability, and use JWTs for stronger, stateless authentication when clients can manage token storage.

Displaying the API token on the user’s show page
Displaying the API token on the user's show page

FAQ

Why add extra security to API routes when we already have login pages and sessions?Many API clients aren’t browsers and can’t use cookies/sessions. Adding token-based checks lets you verify a caller’s identity and protect data even when there’s no browser session.
How do I implement a simple shared API token?Create a middleware that compares a query parameter (for example, apiToken) against a server-side secret (preferably from process.env). If it matches, call next(); otherwise, return an error. Mount it with router.use before your API routes.
What are the downsides of using a single token for everyone?Any user who learns the token can share it, it’s hard to revoke per user, and it offers no per-user metrics or rate-limiting signals. It’s fine for quick prototypes but weak for production.
How can I give each user their own API token?Add an apiToken field to the User schema and generate one in a pre('save') hook using a random token generator. Store it with the user so each account has a unique token you can audit and revoke.
How do I validate a per-user API token?In middleware, read the apiToken query param, look up a user by that token, and call next() only if a matching user exists. Otherwise, return an error (401/403) to block access.
What are JSON Web Tokens (JWTs), and how do they differ from simple tokens?JWTs are signed, compact tokens with a header, payload, and signature. They’re issued on login and sent with each request. Unlike static API tokens, JWTs expire, are easy to verify server-side without session storage, and typically carry just the user ID in the payload.
How do I issue a JWT to a user?Add a POST /api/login route that authenticates credentials (for example, via Passport local). On success, sign a JWT with the user’s ID and an expiration (e.g., 24 hours) using a server secret, then return the token in JSON.
How do I protect routes with JWTs?Add verify middleware that reads the token from request headers, verifies it with your secret, extracts the user ID from the payload, fetches the user, and calls next() if valid. Mount it with router.use after the login route so all other endpoints require a valid JWT.
Where should clients send the JWT on each request?Put it in a request header (for example, token or Authorization) so it works for GET, POST, and others. Avoid putting it in query strings.
What best practices should I follow for tokens and JWTs?- Store secrets in environment variables, not source code. - Keep JWT payloads small and set reasonable expiration. - Ensure the client can store the token securely. - Don’t stack conflicting schemes (remove old verifyToken if switching to JWT-only), or clearly define how multiple schemes coexist.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Get Programming with Node.js ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Get Programming with Node.js ebook for free