Knowledge tree
On this page

API Authorization Testing

Prepare identities, replay requests, and verify object, function, property, and tenant access.
Updated 6 Oct 2026

A valid session establishes authentication. The API still has to decide whether that identity may use a function, operate on an object, and read or write its properties. Where tenants exist, that boundary needs its own check. In an assessment, the reference is the expected behavior for each identity and resource, not a status code on its own.

Prepare accounts and objects

Create or identify two ordinary users, user_a and user_b, each with a recognizable object. Add an administrator and a second tenant only if the application has those roles or boundaries. Record IDs and actual ownership from an authorized view. Use test data so writes can be checked and reverted.

IdentityOwn invoiceOther user’s invoiceAdmin actionrole field
user_aRead and edit 1042No access to 2048DeniedNot writable
user_bRead and edit 2048No access to 1042DeniedNot writable
adminPer target policyPer target policyAllowedPer target policy

This is a permission hypothesis for an example target. Document real exceptions: a shared invoice or support role can make cross-user access legitimate. For multi-tenant testing also prepare tenant_a/user_a and tenant_b/user_c. Two users inside one tenant do not exercise that boundary.

Minimal request replay

Capture a legitimate user_a request in Burp Suite and keep an untouched copy. Stabilize the method, path, body, and relevant headers. Change one variable at a time and keep the baseline and variant together.

GET /api/invoices/1042 HTTP/1.1
Host: example.test
Authorization: Bearer <USER_A_TOKEN>
Accept: application/json

First confirm that A owns 1042. Replay it with B’s token. Then try A’s token against 2048, owned by B. Even if both variants are denied, reads do not establish write behavior. Test a separate edit to a harmless field and inspect the object with its owner. A 200 may contain a generic success message without an effect. A 404 may be the expected way to hide someone else’s object.

Record identity, resource, operation, expected result, and observed result. If a session stops working, refresh the baseline before interpreting variants. An expired token makes the comparison meaningless.

Objects: reads and writes

Look for IDs in paths, query strings, bodies, earlier responses, and nested relationships. An unguessable UUID still needs authorization if it can be obtained elsewhere. For /accounts/7/invoices/1042, check both the account and invoice. Changing only the child ID can show which level the backend validates.

A controlled write can use a recognizable marker:

PATCH /api/invoices/1042 HTTP/1.1
Host: example.test
Authorization: Bearer <USER_B_TOKEN>
Content-Type: application/json

{"note":"auth-check-01"}

If B should not edit A’s invoice, read it again as A. Keep the before and after values. For batch endpoints, check that access is decided for each item. In GraphQL, the same issue can sit in an id or another query or mutation argument. Transport syntax changes, ownership does not.

Functions and equivalent routes

List sensitive operations seen in the UI, documentation, or traffic: POST /users/{id}/disable, DELETE, /admin/..., export, approve, or invite. Replay a valid request with a lower-privilege role. A hidden UI control says nothing about a direct API call.

If the same function has another documented path or method, check it separately. A 403 on /admin/export does not prove that /api/reports/export is protected. For asynchronous operations, inspect the job and final resource before concluding that an action ran.

Returned and writable properties

Property authorization has two directions. Compare responses for the same object across roles and note sensitive fields disclosed to a role that should not see them. Then, in an otherwise allowed profile update, test whether the backend accepts fields hidden from the UI:

{
  "display_name": "user_a",
  "role": "admin"
}

The finding needs a persisted value or a later privilege effect. A 200 that ignores role is not proof of mass assignment. Check nested properties where the endpoint accepts them, then compare the response with a fresh read. Use reversible values and test accounts.

Tenants and finding validation

A and B inside one tenant may legitimately share data. A tenant test needs an identity and object on each side: tenant_a/user_a → tenant_b/invoice. Record the effective tenant for the session, object, and any parent ID. A token with legitimate access to both tenants cannot establish isolation.

Minimum evidence is the expected rule, object ownership, identity and role, baseline request, variant, response, and later state. Check caching, jobs, eventual consistency, and other actors’ changes when results conflict. Remove live tokens from reports. Burp Repeater keeps requests comparable, while interpreting policy and impact remains a manual task.

References