Evaluate tenant and object permissions on every access path.
Mechanism and reasoning
Authentication identifies a caller. Authorization decides whether that caller may perform an action on a target. A valid session is only the start of the decision. The server must consider tenant membership, object ownership, role, action and any relevant state of the object.
Place the check close to the data operation so alternate paths do not bypass it. A hidden button or route guard can improve user experience, but a direct API request can still reach the backend. Background jobs, exports, bulk endpoints and cached responses need the same permission model.
A role is not always enough. A support agent may read limited account metadata but not download invoices. A tenant administrator may manage users in one tenant but have no authority in another. Express the action and resource relationship rather than applying a broad 'admin' label everywhere.
Deny by default when no rule authorizes the action. Handle missing or stale membership carefully. If authorization data is cached, define how revocation becomes effective and what the service does when the policy source is unavailable. A cache can improve availability while extending stale permission; that trade-off should be explicit.
Database row security can add a useful layer, but its behavior depends on the database role and ownership. Privileged roles and owners may bypass policies under documented conditions. Do not assume that enabling a policy automatically constrains every application connection. Verify the actual role used by each path.
Permission matrix
Teaching product has two tenants, red and blue. User A is a red viewer. User B is a red administrator. User C is a support engineer with permission to inspect non-sensitive operational metadata across tenants. Invoice I1 belongs to red and I2 belongs to blue. Export and refund are separate actions.
Actor
Read I1
Read I2
Export red invoices
Refund I1
A, red viewer
Allow
Deny
Deny
Deny
B, red administrator
Allow
Deny
Allow
Allow only under refund policy
C, support metadata role
Metadata only
Metadata only
Deny
Deny
The matrix exposes why one boolean is insufficient. User C has cross-tenant reach but limited fields and actions. User B has stronger authority inside red, not global access. A refund may also require the invoice to be paid and within a permitted time window. That is an object-state condition in addition to role.
Translate the matrix into a data-access rule. Resolve the invoice under an authorized tenant scope, then check the requested action. A query that selects by invoice ID alone and checks only 'is signed in' misses the relationship. For an unauthorized object, the response policy may avoid revealing whether the object exists. Define this consistently so an attacker cannot enumerate another tenant's records through different error messages.
Bulk endpoint case
A single-record endpoint is fixed, but the product adds a bulk download accepting a list of invoice IDs. The implementation checks the first ID and then retrieves the entire list. A caller can place an authorized red invoice first and blue invoices later. The correct contract must authorize every target or reject the entire request under an explicit all-or-nothing policy.
A bulk response can also leak through counts. If unauthorized IDs are silently omitted, a response count may reveal existence under some designs. The product can choose partial success, but the response must not expose unauthorized details and the behavior must be documented. Security is not automatically achieved by dropping some rows after a broad query.
Cache and database case
Suppose invoice responses are cached by invoice ID alone. A red administrator reads I1 with all fields. A red viewer later requests I1 and receives the cached administrator representation. Tenant isolation is intact, but field-level permission is not. Cache keys or cached representations must preserve the relevant authorization context, or the service must filter from a safe internal representation before responding.
At the database layer, a row policy based on tenant context can help catch missing predicates. The application must set that context safely per transaction and avoid connection-pool leakage. If a pooled connection retains tenant-red context and is reused for tenant-blue, the result can be wrong or unauthorized. Test cleanup and transaction scoping. Also verify that the connected database role does not bypass the policy.
These are defense layers with different failure modes. Application authorization expresses business actions and fields. Database restrictions can limit rows. Neither should be claimed as a complete substitute for the other without proving the full contract. In an interview, show the matrix, a single-record path, a bulk path and a cached path. A permission model is only useful if each path preserves it.
The example assumes allowed_tenants comes from trusted server-side membership. Production code must address revocation, race conditions, bulk behavior and database transaction scope. The exported representation is separate from a viewer's ordinary invoice response.
Exercise
A bulk endpoint authorizes only the first ID in [red1, blue1, blue2]. The caller is a red viewer. Identify two independent defects if it then returns full export fields for all three invoices.
Model solution and rubric
It fails to authorize every target, allowing cross-tenant blue records, and it grants export-level fields to a viewer who lacks export permission. Fix target scope and action/representation checks for the full set. Choose all-or-nothing or safe partial behavior explicitly. Test an all-red permitted set, mixed-tenant set and viewer-versus-admin representation. A tenant filter alone does not repair the action-level defect.
Score out of four: one point for the correct result, one for showing the intermediate reasoning, one for identifying the stated failure case, and one for a verification that could disprove the answer. Do not award the reasoning point for a tool name alone.
Failure modes and misconceptions
Misconception 1: a valid session or admin label authorizes every object. Permission depends on the actor-target-action relationship. Misconception 2: row security always constrains the application. Privileged database roles, ownership and unsafe context handling can bypass or misapply it.
Interview probe
Evidence class: recommended. Original practice.
How can a cache introduce an authorization bug even when the database query is scoped?
Strong answer: A cached representation may contain fields or permissions from a more privileged caller. I would bind cache behavior to the relevant tenant and permission context or cache a safe internal object and apply authorization before response.
Follow-up: How do you test tenant context with a pooled database connection?
Weak answer indicators: UI-only permission checks; checking one item in a bulk list; treating all administrators as global; assuming database policies apply to owners automatically.
Sources
Technical references: OWASP authorization; PostgreSQL row security. Sources support the documented mechanisms. The numbers, decisions, rubrics and interview prompts in this lesson are original teaching examples, not measurements or employer question claims.
A red-tenant administrator requests a blue invoice. What is the correct authority check?
ATreat all administrators as globalBEvaluate tenant relationship and requested action for that objectCAllow access if the object already exists in cacheDAllow read because export permission is stronger
A bulk endpoint permits all-or-nothing access. One of ten IDs is unauthorized. What should happen?
AReturn all ten if the first is authorizedBReturn all ten after checking list sizeCReject the request under the declared per-target policyDReturn the nine permitted objects while claiming full success
What must be verified before relying on PostgreSQL row-level security?
AThe actual connection role, bypass/owner behavior and safe tenant contextBOnly that a policy object was createdCOnly that an owner query succeedsDOnly that the API includes a tenant query parameter
A viewer may read an invoice but cannot export full fields. What should the API enforce?
ARead permission implies every representationBDistinct action and representation permissions within tenant scopeCTenant filtering alone, with every field returnedDOnly a hidden export button
Explain how you would evaluate tenant and object permissions on every access path without reading the solution. State one assumption that could change your answer, and one observation that would make you revise it.
Not yetGetting thereConfident
Wrap-up
Carry actor, action and object scope through single, bulk, cached and background paths. Test the actual database role.