Check every job endpoint
Jobs often expose create, status, cancel, retry, result, and log endpoints. Build an owner matrix for all of them. Use random job IDs, but do not treat guessing difficulty as authorization. If a role changes while work is queued, decide whether the job should stop or finish under a service identity. Either choice needs an audit trail and a current access check before a human downloads the result.
Test cases and proof
| Case | Expected result | Proof to keep |
|---|---|---|
| B reads A job status | Deny | Status response |
| Role removed before result is ready | Deny result access | Download response |
| B cancels A job | Deny; A job keeps running | Job state |
Synthetic example
A report job starts under an analyst account. The analyst loses access before the report is ready. When the download link appears, the result endpoint must check current access rather than trust the earlier job creation.
Evidence to keep
Record job creation, queue pickup, role change, completion, and download times. These events show which access decision failed. Give tenant A and B jobs distinct marker strings so a swapped result is plain to see. Test cancellation and retry rights independently from download rights; a user may be allowed to view status but not cancel shared work.
A job may call another service with broad internal rights. That is normal for workers, but the result must still be tied to the requesting tenant. Add a test where two jobs finish in reverse order and result URLs are requested by both owners. Check that status and result IDs never cross.
Decide who may cancel shared work
A team member may need to read status for a shared report while only its owner may cancel it. An admin may need to stop a stuck job without seeing private result rows. These are separate rights. Put them in the endpoint matrix and test a second user in the same tenant as well as another tenant. If the worker finishes after a role change, the result file must not become public just because it has a hard-to-guess path. Keep the job owner and expiry in server state.
Related reading
Why these checks matter
OWASP API1 focuses on object access through identifiers. Its authorization guide says the decision belongs on each request. A job ID is an identifier used at status, cancel, retry, and result endpoints, so none of those endpoints can rely only on the check made when the job started.
Store immutable job scope
Synthetic job JOB-A exports A’s October transfers. Save creator, tenant, authorized filter, permission version at creation, and result type in server state. A queue message refers to that record. It must not accept a replacement tenant or filter supplied by a retry request. A worker’s broad service account is allowed to do work only under the saved job scope.
Create JOB-B with a different marker. Finish B first, then A, and test each status, log, retry, cancel, and result endpoint as both owners. Non-owner responses must contain neither content nor revealing row totals. Also test a same-tenant colleague against the stated sharing rule rather than assuming all team members are denied.
Test cancel against completion
Pause A’s worker immediately before publishing the result, then send cancel and completion at the same time. Choose a state rule: one transition wins; a cancelled job cannot later expose a result under a completed state. Cancellation after publication can block future download or delete the file under policy, but cannot recall a file already downloaded.
Result access needs a current gate if immediate permission revocation is promised. A direct bearer storage link instead follows its lifetime and storage policy. Test both the authenticated result route and a previously issued link. Save the state version, competing transition times, access decision, file hash, and expiry. Retry failure files and debug downloads too; they share the sensitive-data boundary. OWASP’s authorization guide supports each endpoint check; this job-state fixture tests the races.