When building secure APIs, developers often put a lot of effort into authentication—verifying the identity of a user. But what about authorization—deciding what that user is allowed to do?

Ignoring or misconfiguring authorization is one of the most common security oversights in backend development. Even with a strong authentication system (like JWTs), attackers can inject API calls and access data or perform actions they shouldn't.

Let’s break down the difference, then look at a real example of what can go wrong—and how to fix it.


✅ Authentication vs 🚫 Authorization

Concept What it Does Example
Authentication Confirms who you are Login with email and password
Authorization Confirms what you can do Check if the user is allowed to edit a post

In short:

  • Authentication answers: “Is this user really John?”
  • Authorization answers: “Is John allowed to do this?”

🔑 Common Setup: JWT Authentication

A common practice is to use JWT (JSON Web Token) to authenticate users. When a user logs in, the backend generates a JWT that includes some payload—usually the user’s ID or role.

Here’s an example payload:

{
  "user_id": "12345",
  "role": "user",
  "iat": 1713308160,
  "exp": 1713344160
}

This token is signed and sent to the frontend. The client includes it in API requests:

GET /api/orders/98765
Authorization: Bearer eyJhbGciOi...

On the backend, you might decode the token, extract user_id, and trust it blindly:

const userId = decodedToken.user_id;
const orderId = req.params.order_id;

const order = await db.orders.findById(orderId);
return res.json(order);

❌ What’s Wrong Here?

The backend checks that the user is authenticated—but not whether they own the order.

Any user with a valid token can try this:

GET /api/orders/any-other-user-id
Authorization: Bearer eyJhbGciOi...

This is an authorization failure. You're trusting the JWT, but you're not checking whether the user is authorized to access that specific record.

This opens the door to API injection attacks—where attackers manipulate API endpoints and access or modify other users’ data.


🛡️ How to Fix It

You must verify both:

  1. The user is authenticated (JWT is valid)
  2. The user is authorized (owns this resource or has permission)

✅ Example with Authorization Check:

const userId = decodedToken.user_id;
const orderId = req.params.order_id;

const order = await db.orders.findById(orderId);

if (!order || order.user_id !== userId) {
  return res.status(403).json({ error: "Unauthorized" });
}

return res.json(order);

This way, even if a user tries to inject a different order_id, they’ll be denied access unless the order belongs to them.


🔐 Pro Tip: Don’t Trust the Client

Even if the frontend is “locked down,” never rely on the client to enforce rules like:

  • Hiding buttons for admin-only actions
  • Sending only their own user_id in the request

The client can be modified, spoofed, or reverse-engineered. All critical access rules must be validated on the backend.


Final Thoughts

  • Authentication gets you in the door.
  • Authorization decides what you’re allowed to do once inside.

If you only implement authentication, you’re trusting every user with a key to the building. Without proper authorization, they might still wander into rooms they shouldn’t be in.

Audit your routes. Build role-based or resource-based access checks. Assume nothing from the frontend.

And always remember: a valid JWT does not mean unlimited access.