Review an asynchronous job by separating permission to enqueue, permission to start, permission to access data while running and permission to deliver the result. At each boundary, identify whose authority applies, what resource and action it covers, and how current revocation is checked. A queue message is a request to reconsider work, not permanent proof that a person still has access. If a job must continue after a person's access ends, define a distinct, limited service authority and the business reason for it. Otherwise stop new protected work and retain only the authority needed to cancel, settle or clean up safely.
This matters whenever work waits: exports, scans, reports, bulk edits and notifications. A job authorized at 09:00 may run at 09:20 after the initiator leaves the account or loses access to the repository. OWASP's Authorization Cheat Sheet recommends validating permissions on every request, and its authorization patterns guidance places enforcement near the protected resource. Applied to a worker, that means rechecking when the worker reads or changes the resource, not merely preserving an earlier HTTP decision.
Illustrative case: a delayed invoice export
Illustrative system, people and timing: Pine Ledger lets a billing analyst request an invoice export for account A. At 09:00, the analyst is authorized and a job is queued. At 09:05, an account administrator removes the analyst's billing access. At 09:20, the worker receives the message. An export, if produced, would contain private invoice rows. No real export or access attempt is described here.
Pine's intended policy says a person-initiated export must stop reading private invoices when that person's access is revoked. Operations staff may still stop the worker, release reserved capacity and mark the job canceled. Pine also has an independent, service-owned monthly finance export. That job has a different approved authority and recipient policy; it must not be confused with the analyst's personal export.
The review artifact is a boundary table:
| Moment | Authority to verify | Protected action | Required evidence | Revocation result |
|---|---|---|---|---|
| Request at 09:00 | Current analyst membership and export capability | Admit job for account A | Verified actor, selected account, scope and current policy | Reject if already revoked |
| Dispatch at 09:20 | Same live actor or explicitly approved service delegation | Start data work | Durable job binding plus current authority | Do not start personal export |
| Each data window | Current scoped authority and job fence | Read invoice rows | Verified account/object scope and unexpired run authority | Stop further reads |
| Publish artifact | Current authorized workflow | Store or expose result | Job version, completion state, destination policy | Do not publish personal export |
| Download later | Current reader permission | Deliver file or link | Live user and artifact/account binding | Deny future delivery |
| Cancellation/settlement | Narrow operations authority | Stop and account for work | Persisted job and attempt identity | May continue cleanup only |
The table forces a decision about what revocation should stop. It cannot recall rows already delivered to a user or erase a copy already downloaded. It can fence future work and delivery if the system is designed to do so. Distinguish that forward-looking guarantee from claims about past plaintext.
Inspect the job contract before the worker
1. Write down the job's principal and purpose
Input: the entry route, job schema, policy and queue message. Owner: platform engineer with the business and security owners. Output: a durable binding of job ID, tenant/account, initiating actor or service delegation, action, resource scope, creation time and policy generation if the system uses one.
A string account ID in the message is not itself authority. Nor is a copied “allowed: true” flag. The worker should resolve the job from a trusted durable record and verify that the binding has not been changed or superseded. If an external webhook or scheduler creates work, document that machine principal and its exact permitted action. Avoid treating every task as if it inherited the service's broad database permissions.
The policy should answer a hard question explicitly: after a human loses access, does the organization still want an already-admitted action to finish? For a private personal export, often no. For a financial settlement or safety cleanup, stopping mid-operation may create a worse state, so a separate service obligation may continue. That is an application policy decision, not an automatic property of the queue.
2. Trace every point where protected work happens
Enumerate the dispatcher, worker, source reader, downstream API, artifact writer, notification sender and download handler. Mark which one actually checks current permission. An API gateway may have authorized the original request, but the worker can run later under a machine identity. The service that supplies invoice data should still enforce account and object scope. OWASP's service-level authorization pattern calls for checking action, object and tenant close to the resource.
For a long job, one check at worker start can itself grow stale. Define a bounded data window or checkpoint and revalidate before each consequential read, write or publish step. How often depends on the promised revocation latency and operation cost. If current-policy data is unavailable, fail closed for new protected work and report an incomplete job; do not fall back to the payload's older role snapshot. Clean cancellation should remain possible through a separate narrow authority.
3. Inspect retries, duplicates and old workers
A delayed message may be delivered again. AWS documents at-least-once delivery for SQS standard queues and notes that even during a visibility timeout duplicate receipt is possible. Other queues have their own semantics; check the actual provider. Either way, an old attempt must not publish after a newer attempt, cancellation or revocation.
Use a durable job version, attempt identity or fence to reject stale work at each state change and release boundary. A retry should re-evaluate authority and preserve idempotency for its side effect. If it resumes from a checkpoint, confirm the checkpoint is tied to the same account, actor/delegation, scope and current job version. A message body copied from another tenant or edited to name another account must not change the trusted binding.
Do not rely on deleting the queue message as the only stop control. An already-running worker may continue. Find the component that closes source access, stops new calls and prevents publication, then the component that observes all attempts actually ended. “Cancel requested” and “worker stopped” are separate states. Pine should report which one it has evidence for.
4. Review result delivery as a new access request
A completed artifact can outlive the worker. Check status APIs, notification contents, object storage links, report previews, search indexes and exports. Current authorization should govern each future delivery to a human. A bearer URL issued before revocation may remain usable if it bypasses the app; that is a specific design tradeoff to resolve against the revocation promise. A notice can contain a minimal job link, with content fetched only after a fresh check.
For service-owned jobs, recipient authorization may be different from initiator authorization. Name the permitted audience and enforce it at delivery. An operations worker authorized to settle a canceled job is not automatically authorized to read or send its private output. OWASP's business logic guidance emphasizes mapping each sensitive operation to every entry point; artifact delivery is one of those operations.
Follow the worker that was already running
The delayed-start case is only the easy branch. Change the synthetic timeline: personal export J4 starts at 09:02, before revocation at 09:05. Attempt A holds job version 7 and has read one data window. It then loses connectivity. A replacement dispatcher later sees the revocation and cancels the job.
| Time | Authoritative job state | Attempt A's stale view | Required consequence |
|---|---|---|---|
| 09:02 | J4 running, version 7, attempt A | Version 7, authority valid | First bounded read may proceed |
| 09:05 | Revocation recorded; J4 cancel requested, version 8 | Still remembers version 7 | New protected reads require current decision and must stop |
| 09:06 | Replacement sees revoked actor; J4 canceled, version 9 | Local file still exists | Replacement must not restart personal export |
| 09:07 | J4 remains canceled/version 9 | A attempts publish using version 7 | Publication fence refuses stale attempt |
| 09:09 | Stop evidence collected; cleanup recorded | A no longer running | Report stopped, with bounded residual-data handling |
The critical write is not “set job complete” with no condition. Publication must require the current attempt/version and allowed job state at the release boundary. In this design, private scratch output is inaccessible to readers until a conditional publication record succeeds. A direct public object write followed by a failed database update would already have exposed the bytes; the database fence would be too late.
Cancellation also needs a precise promise. Pine's policy aims to stop new reads after revocation, but a request already authorized and in flight may finish unless the data service can interrupt it. The review must record the actual maximum read window and enforcement point. A source sketch cannot justify “instant revocation” or retract data already delivered. If policy requires a tighter boundary than the platform can enforce, resolve that mismatch explicitly.
The lease protects work ownership only to the extent that the resource operations honor it. Its expiry is not evidence that A's process stopped. The filled review decision is therefore: the canceled state and version fence defeat stale publication in the proposed design; current data-service checks are still needed to establish the promised read boundary; process termination is separately evidenced before reporting stopped. Cleanup retains a narrow authority to remove temporary output and settle usage, not to finish the private export.
Compare a service-owned obligation
Pine's monthly finance export J5 has a service principal, a named finance-reporting purpose and a limited account scope independent of the removed analyst. It may continue if that mandate remains valid. Delivery still checks the recipient policy: removal of the analyst prevents sending the report to that person even if production of the report is justified.
Copying serviceOwned=true into J4 would not create such a mandate. The job must have been admitted under an approved service obligation with a reviewable purpose and audience. This prevents “finish whatever was queued” from becoming an undocumented exception to human revocation.
The acceptance evidence for a real implementation is now finite: a stale attempt cannot publish; current readers cannot obtain the canceled artifact; the next data window is refused under the promised revocation contract; and cleanup can finish without restoring read or delivery permission. These are proposed checks using synthetic data, not observations of an executed system.
Give operators distinct controls for stop and cleanup
Pine's operational tooling should show whether cancellation was requested, publication was fenced, attempts terminated, and temporary output was handled. A single “cancelled” badge can conceal which boundary is actually established. The operator may need to finish cleanup or settle usage after private-data authority has ended.
Keep that residual authority narrow and tied to J4's persisted identity. It should not allow the operations worker to resume invoice reads or deliver the canceled export. This makes the stated revocation policy inspectable during failure recovery as well as on the normal dispatch path.
A useful review ends with a clear decision for each job class: stop on human revocation, continue under a separate narrow service mandate, or remain unresolved because the current checks cannot be established. For a personal export, the next practical step is to enforce current authority at dispatch, at the declared data-read checkpoints and at publication/delivery, then verify the result at a new revision using the fix verification guide. For the related synchronous rule, see object-level authorization in REST APIs.