Separate function access from record access
A user may call the report GET function without being allowed to read every existing report. Authentication identifies a principal in an issuer context; authorization decides whether that principal may perform this action on this resource. The exercise contains report-a, report-a-private and report-b. All use the same token mechanism, but objects have different tenants and relationships. Checking only that a user has a session, or only that the endpoint requires authentication, leaves object-level decisions unresolved. Design a subject, action and resource matrix before implementing. Include reading, changes and administrative operations because authorization for one function does not automatically carry into another.
Retain the tenant boundary
In the model, analyst-a has a read relationship with report-a in fund-a. The same identifier appears in report-b’s reader list in fund-b, but the token belongs to fund-a, so the request is denied. One relationship does not replace the tenant boundary. Tenant is a private claim in this exercise, not a universally required protocol field. In production, attribute origin and authority need definition. A client-supplied route parameter or header must not silently replace authorized context. Also confirm that queries, caches, asynchronous jobs and exports retain that boundary; protecting the initial form does not automatically protect every access path.
Scopes do not replace business relationships
The reports:read scope permits considering a read, but report-a-private lists only analyst-b as a reader. Analyst-a is denied despite sharing the tenant and having the right scope. To update report-a, the model requires reports:write and an editor relationship. Giving that scope to analyst-a does not make the subject an editor; editor-a with the correct scope passes. The exercise compares complete scopes: reports:read-all does not satisfy reports:read merely because the strings overlap. This is an example of combining attributes and relationships. An organization may use other rules but needs to explain who manages relationships, when they change and what decision follows when required data are unavailable.
Apply the decision where the effect is controlled
Hiding a client button improves the interface but does not prevent direct requests. Enforce the decision at a trusted point controlling access or change. A gateway can validate a token without knowing each report’s relationships; the service still needs the information required to decide. Avoid treating read validation as write permission. For changes, also consider state changes between checking and applying the effect: a resource may change tenant or state during the operation. The concrete strategy depends on storage and transaction design. The lab only computes decisions over in-memory objects; it establishes neither atomicity nor isolation of an actual database.
Test denials and legitimate access
Prepare paired tests using valid identities: own and other objects, same and different tenants, sufficient and insufficient scopes, reader and editor. Add nonexistent resources, unsupported actions and unavailable authorization state. In the fixture, the default is denial and unavailability does not broaden permissions. Internal diagnostics identify the reason; external responses should follow service disclosure policy and may hide object existence. Random identifiers reduce discoverability but do not replace authorization. After correcting a failure, look for the same omission in listings, downloads, nested endpoints and bulk operations. Record coverage and gaps so the RUN team knows what was actually exercised.
# Fixture decisions
# report-a + analyst-a + read -> permitted
# report-a-private + analyst-a + read -> object-relationship
# report-b + fund-a token + read -> tenantAnalyst-a reads report-a but neither report-a-private in the same tenant nor report-b in another tenant.
Common pitfalls
Authentication as authorization; UUID as access control; gateway as knowledge of every relationship; read scope as write permission.
Related topics: Trust boundaries · API authorization · Revocation and operations
The decision must match each request’s subject, object, action and context.
Reference: Authorization Cheat Sheet · CAS-005 / SecurityX V5; objectives 3.0; launched 2024-12-17