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.