Backend

Trace an authorization rule from the page to the database query

Kamruz
Kamruz
Oct 14, 2026
5 min read
Trace an authorization rule from the page to the database query

The interface hides the edit button from viewers. A viewer opens browser developer tools, copies an edit request, changes the document identifier, and updates another organization's document. The page implemented a useful presentation rule, but the API trusted the caller's identifier and performed a lookup without organization scope. The protected action was never tied to the authenticated actor.

This example assumes a Node API with MongoDB and organization-scoped documents. The same reasoning applies to server actions, background tools, and other storage systems. Authorization is the decision that a verified actor may perform a particular operation on a particular resource under current policy.

State the rule in one sentence

For example: an organization editor can update documents belonging to that organization; a viewer can read published documents but cannot update them. State exceptions explicitly, such as a global administrator role with audited access. Avoid the vague rule that logged-in users can edit documents.

Separate authentication from authorization. Authentication establishes who the server believes the actor is. Authorization evaluates that actor against the resource and operation. A valid session proves neither membership in every organization nor ownership of the requested document.

Decide how removed membership and revoked roles affect existing sessions. If a role is copied into a long-lived token, it can remain stale after the database policy changes. The product needs a revocation or refresh policy proportional to the protected operation. Do not assume a hidden button updates every token's privilege.

Trace the request path

Start with the UI event, then follow request parsing, session verification, service call, repository filter, update, and response mapping. Write down the actor source and resource source at each step. A browser-supplied organization identifier can be a requested scope; it cannot establish membership by itself.

Look for alternate entry points. The page may call a protected service while a legacy API route updates the collection directly. An administrative import or AI tool might bypass the same rule. Authorization review needs to cover every path capable of the action.

Ask an AI assistant to enumerate these paths with file evidence. Require it to show where actor verification and resource scope enter the query. A statement that middleware protects the application is incomplete if the actual data operation still accepts arbitrary identifiers.

Enforce scope at the data operation

Where possible, include the authorized organization in the query filter. A scoped update can require document identifier, organization identifier derived from verified membership, and any state predicate. This limits the chance that a later operation accidentally acts on an unscoped record.

const result = await documents.updateOne(
{ _id: documentId, organizationId: authorizedOrganizationId },
{ $set: allowedChanges }
)
if (result.matchedCount !== 1) throw new DocumentNotAccessible()

The example assumes validated identifiers, a prior verified editor permission, and an allowlisted update object. The query scope is not a substitute for checking the editor role. Likewise, checking the role is not a substitute for scoping the document. Both establish different parts of the rule.

If policy depends on the current document state, include that state in a conditional update or use an appropriate atomic boundary. Reading a state, authorizing, and later writing can race with a policy-changing update. Decide whether that race matters for the operation and enforce the relevant invariant.

Avoid privilege through fields

Do not spread an incoming body into the update. A caller might set organizationId, ownerId, role, or published even if the form never exposes those fields. Build an allowed change object after validation and policy checks.

Read permissions also include field visibility. A viewer allowed to read a document may still be prohibited from seeing internal review notes or private author contact details. A populated record can carry more fields than the page needs. Map a public or role-appropriate view before serialization.

Error behavior can reveal resource existence. Some products intentionally return a common inaccessible response for both missing and unauthorized resources. Others expose a distinction. Choose the policy and keep it consistent; do not leak another organization's document title in an error message while denying its body.

Build a role and resource matrix

Use synthetic actors: anonymous, viewer, editor in the correct organization, editor in another organization, revoked member, and administrator if applicable. Use resources in two organizations and at least one missing identifier. Test read and update separately.

Assert stored data remains unchanged after denied updates. A 403 response alone is insufficient if the write occurred before the response was chosen. Check response fields for private values on allowed reads as well. Authorization includes both the operation and the information returned.

Exercise direct API requests without the UI. Repeat tests through any server action or alternate endpoint that performs the same operation. Test list, search, and pagination filters because collection endpoints often leak cross-organization data even when single-record routes are scoped. See cursor pagination.

Verify revocation and cache behavior

Remove a member's edit role while an old session remains active, then attempt the operation. Compare the result with the documented revocation policy. If role checks are cached, define their freshness and invalidation. A stale permission cache can continue granting access after the UI updates.

Keep privileged data out of shared public caches. A correct query can still leak through a cache keyed only by resource identifier after an authorized read. Draft-cache isolation gives a concrete alternating-identity test for that failure.

For sensitive actions, log actor identifier, resource identifier, operation, decision, and a request correlation value without private payloads. Logs help explain access decisions, but they should not become a second channel exposing document content.

Use AI as a path reviewer

Provide the policy sentence, route, service, repository, and public mapper. Ask the assistant to find any input accepted from the caller that influences authority. Ask it to identify missing filters and allowlist gaps. These are reviewable findings tied to code.

Challenge suggestions to centralize every permission in one giant helper. Some shared checks are useful, but resource-specific rules often need context. A helper named isAuthenticated should not be presented as a complete document authorization check. Clear naming helps future readers see what a function actually proves.

Safe API audit fixtures keep this review independent from real accounts. Private populated fields extends it to serialization. The smallest safe repair makes the actor, resource scope, permitted fields, and verification visible in one coherent path.

Protect ownership-changing operations

Moving a document between organizations needs a separate rule from editing its title. Require authority over the source and the destination, validate both scopes, and decide how related records move. Otherwise an allowlisted organization field can become a way to escape the original query scope. Test the transfer with an editor authorized only in the source, only in the destination, and in both. A normal content-update endpoint should reject ownership changes unless that operation is explicitly part of its contract.

Sources

About the Author

Kamruz

Kamruz

Full Stack Developer

A passionate developer who loves creating modern web applications and sharing knowledge with the community.

More About Me

Related Articles