A generated API route that reads or writes data but never checks who is calling it will happily serve any request, including one for another user's records. The fix is to verify the caller is authenticated and is allowed to act on the specific resource, on every route that touches data.
Why it's a problem
Without an ownership check, changing an id in the URL is enough to read or modify someone else's data (a pattern known as broken object level authorization). The endpoint works perfectly in your own testing because you are logged in as yourself, which is why the missing check is so easy to ship.
The pattern
// returns the order for ANY id, no auth, no ownership check
app.get("/api/orders/:id", async (req, res) => {
const order = await db.order.findUnique({ where: { id: req.params.id } });
res.json(order);
});The fix
app.get("/api/orders/:id", requireAuth, async (req, res) => {
const order = await db.order.findUnique({ where: { id: req.params.id } });
// enforce that the caller owns the resource
if (!order || order.userId !== req.user.id) return res.sendStatus(404);
res.json(order);
});Why AI tools write this
When you ask an assistant to 'add an endpoint to fetch an order,' it generates the data access that fulfills the request, and authentication was not part of what you asked for. The route works end to end in a happy-path test, so the absence of an auth check does not surface until someone requests an id that is not theirs.
The quick fix
- Require authentication on every route that reads or writes data.
- Check that the authenticated user owns or may access the specific resource, not just that they are logged in.
- Return 404 rather than 403 for resources the user may not access, so you do not leak their existence.
Common questions
What is the difference between authentication and authorization?
Authentication checks that the user is logged in. Authorization checks that the logged-in user may act on the specific resource they are requesting. Both are needed; a missing ownership check is an authorization gap even when authentication is in place.
Why does Prbl flag this pattern specifically?
AI tools generate data-access routes that answer the task as described, which is usually just to return a resource, not to add a caller check. The result is a route that works correctly but is accessible to every logged-in user, not just the owner.
Should I return 403 or 404 when a user requests a record they do not own?
Prefer 404. A 403 confirms the resource exists, which is information. A 404 for a record the user cannot access gives attackers no signal about what they have not found.