Key idea
Authentication is knowing who's asking. Authorization is deciding whether they may do this, to this data. Agents build the feature and skip the second part, because the demo works without it. Every endpoint that reads or changes someone's data needs its own check.
Find it in the starter
app.delete("/api/tasks/:id", (req, res) => {
if (!store.remove(req.params.id)) {
return res.status(404).json({ error: "task not found" });
}
res.status(204).end();
});
The app knows who's asking: currentUser(req) reads the x-user header, and each task stores its owner. The delete route never compares them.
See it for yourself. Start the app and list the tasks: task 3 belongs to sam. Then, as demo (no header), delete it:
curl localhost:3000/api/tasks
curl -i -X DELETE localhost:3000/api/tasks/3
204 No Content: sam's task is gone, deleted by someone else.
Fix it
Look the task up, then check the owner before removing anything:
app.delete("/api/tasks/:id", (req, res) => {
const task = store.all().find((t) => t.id === req.params.id);
if (!task) {
return res.status(404).json({ error: "task not found" });
}
if (task.owner !== currentUser(req)) {
return res.status(403).json({ error: "not your task" });
}
store.remove(task.id);
res.status(204).end();
});
Restart the app and run the delete again: 403 Forbidden. Add -H "x-user: sam" and it's 204. An ID that doesn't exist still gets 404. Run npm test to check nothing else broke, then commit the fix on its own.
Some apps answer 404 for other people's data too, so nobody can probe which IDs exist. Either is fine; pick one, write it in the brief, and test it.
Test the refusal, not just the success
The easy test deletes your own task and gets 204. It passed before the fix too, so it proves nothing about authorization. The test that matters sends a different user and expects 403, and it must fail against the old code (lesson 4.5.2).
Ask it of every endpoint
When you review an agent's diff, put these to each route:
- Who may call it? Anyone, a signed-in user, an admin?
- On whose data? Does it check the record belongs to the caller?
- What if the ID belongs to someone else? Try it with curl.
- What does it return to other users?
GET /api/taskslists everyone's tasks today. Is that what you want?
Put the rule in your brief's acceptance criteria, as the module 3 brief did: "Only the task's owner may change it; anyone else gets 403."
The x-user header is a toy
Any client can send any header, so x-user: sam proves nothing about who's really asking. The starter uses it to keep the lesson small. Real apps get the user from a sign-in: a session cookie or a token that the server verifies on every request. The authorization check stays exactly the same; only currentUser changes. Don't ship header-based identity, and don't let an agent add it to your app as a shortcut.
Check yourself