9. Authorization Middleware
Follow along with code examples here!
Authentication answers "who are you?" Authorization answers "what are you allowed to do?" In this lesson, we use middleware to enforce authorization — preventing unauthenticated or unauthorized users from accessing protected endpoints.
Table of Contents:
Essential Questions
By the end of this lesson, you should be able to answer these questions:
What is the difference between authentication and authorization?
What problem does authorization middleware solve? Why not just put the check inside every controller?
How does middleware use
next()to either continue the request or short-circuit it?How do you protect a single route vs. an entire router?
What is ownership-based authorization? How do you check if a user owns a resource?
Terms
Authentication — verifying who you are (proving your identity, typically through login).
Authorization — determining what you're allowed to do (checking permissions after identity is established).
Protected Route / Endpoint — an endpoint that requires a valid session (or additional permissions) to access.
checkAuthenticationmiddleware — a custom middleware function that checks for a valid session and either allows the request to continue (next()) or short-circuits it with a401response.Ownership — a resource (post, comment, profile) that belongs to a specific user. Authorization checks that the currently logged-in user owns the resource before allowing modifications.
Authentication vs. Authorization
These two terms are related but distinct — and it's important to understand the difference:
Question
Who are you?
What are you allowed to do?
How
Login (username + password)
Session check + permission check
Status on failure
401 Unauthorized
403 Forbidden
Comes first?
Yes
After authentication
Example
Logging into Instagram
Editing your own post, not someone else's
Authentication always comes first. You can't authorize someone whose identity you haven't verified.
Q: A logged-in user tries to delete another user's post. Is this an authentication failure or an authorization failure?
Authorization failure. The user is authenticated (we know who they are — they have a valid session), but they don't have permission to delete someone else's post. The server should return 403 Forbidden, not 401 Unauthorized.
401— "I don't know who you are. Log in."403— "I know who you are, but you can't do this."
The Problem: Unprotected Endpoints
Without authorization middleware, every endpoint on your server is public:
A user who knows the URL can delete posts even if they're not logged in, or delete someone else's posts. We need to protect endpoints that require login.
You might be tempted to handle this inside each controller:
This works, but it's repetitive. If you have 10 protected endpoints, you'd write the same check 10 times. Middleware solves this by extracting the check into a reusable function.
Writing Authorization Middleware
The checkAuthentication Middleware
checkAuthentication MiddlewareAuthorization middleware is just a regular Express middleware function — it receives req, res, and next. The key behavior:
If the session is valid: call
next()to pass control to the next middleware or controllerIf the session is missing: send a
401response and stop
That's it. The middleware either stops the request by sending a response, or lets it continue by calling next().
Q: What happens if you forget to call next() in middleware when the session is valid?
The request hangs. The middleware function returns without sending a response or calling next(), so Express doesn't know what to do next. The client's request will eventually time out. Always make sure every code path through a middleware either calls next(), calls next(err), or sends a response.
Applying It to Routes
Add the middleware as an argument between the path and the controller:
When a request hits DELETE /posts/5:
Express calls
checkAuthenticationIf session is missing →
401, stops hereIf session is valid → calls
next(), Express callsdeletePost
The controller never even runs if the user isn't logged in.
Q: You have a blog app where anyone can read posts, but only logged-in users can create, edit, or delete. Which routes get the middleware and which don't?
Only the write operations (create, update, delete) require authentication. Read-only endpoints remain public.
Applying It to an Entire Router
If every endpoint on a router requires authentication, use router.use() to apply the middleware to all routes at once:
You can also do this when mounting the router in index.js:
Every route under /api/admin requires a valid session. Routes under other prefixes are unaffected.
Ownership-Based Authorization
Being logged in is the first check. But some actions require that you own the resource. Anyone logged in shouldn't be able to edit anyone else's post.
Ownership authorization looks like this:
checkAuthenticationmiddleware already confirmedreq.session.userIdexistsThe controller fetches the post and compares
post.user_idtoreq.session.userIdIf they don't match →
403 ForbiddenIf they match → proceed with the update
You could also extract ownership checking into its own middleware, but keeping it in the controller is common when the check requires a database lookup.
Q: Why does ownership authorization return 403 instead of 401?
Because the user is authenticated — we know who they are. The 401 ("Unauthorized") response specifically means "I don't know who you are, please authenticate." Since we do know who they are, and we're denying them based on permissions, the correct code is 403 Forbidden: "I know who you are, but you're not allowed to do this."
Q: Could you write a reusable middleware for ownership checking? What would it look like?
You could, but it's tricky because ownership middleware needs to know which model to look up and which field represents the owner. A common pattern is a factory function that generates middleware:
This approach is clean for large applications. For a smaller app, handling ownership inline in the controller is simpler and perfectly fine.
Testing Protected Routes with Postman
When testing protected routes in Postman, you need an active session cookie. The workflow:
Register or login — send a
POST /api/loginrequest with valid credentialsPostman stores the cookie automatically — look in the "Cookies" tab to confirm
Make requests to protected endpoints — Postman sends the cookie automatically, just like a browser would
Test the unauthorized case — clear the cookies in Postman and try the protected endpoint again. You should get a
401.
This mirrors exactly how the browser and your frontend would behave.
The Complete Auth Picture
Here's the full authentication and authorization system we've built:
The flow for a protected request:
And for an unauthorized request:
Q: You're building a notes app. Every user can only see and edit their own notes — no note is public. Design the middleware strategy for the /api/notes router.
Since no route on this router is public, apply checkAuthentication to the entire router:
In getUserNotes, you'd filter by user_id:
Model method:
This design ensures users can only ever see and modify their own data.
Last updated