Authorization
Enforce object-level authorization
Check the caller’s relationship to every requested object instead of accepting an object identifier as proof of access.
8 minute lesson
An object ID tells the server what to load. It does not tell the server who may load it.
Resolve identity from trusted authentication state, then constrain the query by owner, tenant, membership, or policy. Apply the same rule to read, update, delete, export, and nested routes. Test with two valid identities.
SELECT id, total
FROM invoices
WHERE id = ? AND account_id = ?
Customer 42 can be signed in correctly and still request customer 43’s invoice. Random-looking IDs reduce guessing, but they never replace the ownership condition in the query.
Check indirect paths too. An attachment, comment, or export nested below an authorized invoice can still load a foreign child object unless every relationship stays constrained.
Create invoices for two valid accounts and exercise read, update, delete, and export. Save the response and database state proving every cross-account attempt failed without changing the target.
Lesson completed