Authentication says who arrived. Authorization decides which world they can touch.
The boundary belongs close to the object:
const note = await db.note.findFirst({
where: {
id: params.noteId,
workspaceId: session.workspaceId,
},
});
The workspace filter is the product saying that an object exists only inside the user’s permitted world. Middleware can confirm identity. A route guard can confirm a broad role. The query has to carry the ownership rule because object access depends on the object.
Derived data keeps the same obligation. Search indexes, embeddings, summaries, analytics tables, and AI memory stores are copies of something else. If the source is private, the copy needs the same permission shape. Otherwise retrieval becomes a side door.
For each derived record, I want to know:
- which source object created it?
- which tenant owns it?
- who can see it?
- when did the source permission last change?
- should it disappear when the source is deleted?
An agent adds another pair of names. The system should know who requested a change, which process executed it, and which short-lived grant allowed the action.
initiator: jeremy
executor: blog-agent
grant: edit src/content/blog for this run
action: update markdown file
A service account should not receive a human administrator token because that was the easiest integration path. The executor needs the smallest grant that completes the task.
Denial is part of the interface too. Logged out, wrong workspace, missing role, deleted object, and hidden object are different states. Some can be specific. Others should stay deliberately vague to avoid leaking that a resource exists. Leaving the decision to a raw 403 guarantees inconsistent behavior.
The useful authorization test is not that a user can load their own note. It is that the wrong user cannot load it with a valid identifier.
member cannot read document from another workspace
viewer cannot rotate API key
expired agent grant cannot write a file
removed user cannot retrieve an old embedding result
Authentication makes a session. Authorization gives that session a boundary. Keep the rule in the data access path, carry it into derived stores, record delegation, and test the person who should be kept out.
Related posts

About Jeremy London
Engineering leader and builder in Denver. I write about AI platforms, agents, security, reliability, homelab infrastructure, and the parts of engineering work that have to survive production.