Choose the promise before choosing the URL
Use a direct presigned download when the product can tolerate access through an already issued link for its remaining validity window. Choose a serving path that checks current authorization on each new request when removing a workspace member must stop new downloads. A redirect endpoint can control who receives the next link. It cannot make the browser forget a target it already received.
This is an architecture decision about where permission is enforced. In a SaaS export flow, creating the file, issuing a delivery credential and admitting a request for bytes can happen at different times. A membership change reaches those boundaries only if they consult it.
The decision record below compares four paths using a small executable admission model. Its strongest counterexample is the redirect broker: after membership removal, it issues no new redirects, yet the retained storage URL still admits requests. The model is deliberately sequential. It does not sign URLs, contact AWS or measure a production revocation delay.
Keep the promise narrow enough to test. "Stop issuing links," "reject new download requests" and "interrupt an active transfer" are different acceptance criteria. None means recovering a file that somebody already downloaded.
Separate the SaaS member from the signing principal
Amazon S3's presigned URL documentation describes time-limited access using the signer's credentials. Possession of the URL can authorize the specified operation. Removing a person from an application workspace is a separate state change unless your delivery design connects it to storage authorization.
That distinction also explains why "presigned URLs cannot be revoked" is too broad. The AWS presigned-request FAQ describes permission removal, session revocation and access-key deactivation. Those measures affect uses of the relevant signing identity or credentials. They can have a much wider scope than one person's download.
A shared signing role therefore creates an operational question: what other workloads or links depend on the permission you would remove? Keep that dependency inventory before treating credential revocation as a routine per-user product feature. A dedicated credential or resource boundary changes the trade-off, but its lifecycle still needs an owner.
The same AWS FAQ states that presigned requests are reusable. A one-time redemption token in your application does not make a storage URL returned by that redemption single-use. Decide whether the product needs one redemption, one admitted request or an entire resumable transfer before implementing a consumption counter.
Compare the redirect and gateway boundaries
A broker that validates membership and returns a redirect is useful. It can deny another visit to the broker after removal. Once the client knows the redirect target, however, a later request can go directly to that target. Adding an application database check only to the broker leaves that route intact.
A gateway can retain control of new requests if it authenticates the caller, resolves the tenant and resource, checks current permission and serves the bytes without handing out a bypass credential. Its storage origin must remain unavailable through another client-accessible route. Otherwise the gateway's careful check protects only one entrance.
"Current" needs an explicit consistency contract. A permission cache, a lagging membership replica or an authorization dependency in an outage can change what the gateway knows. Specify how those states affect admission. The per-request path in this article assumes an immediately updated permission function. That is a fixture assumption, not an implementation guarantee.
Streaming through a service also places transfer work on that serving path. Consider connection lifetime, cancellation, range requests and dependency failures before choosing it. This article supplies no throughput or cost comparison. An edge authorization layer may offer another boundary, provided its check runs before protected bytes can be served and its state reaches the required freshness.
Read the executed admission timeline
Download the Python fixture and complete results. Save the code in an empty writable directory and run python3 experiment.py. It uses the standard library, makes no network requests and writes results.json.
The constructed schedule issues a capability at second 0, removes application membership at second 20 and expires the URL at second 120. Signing credentials otherwise remain valid until second 3,600. A cached gateway permission is first checked at second 0 and reused for 30 seconds. Every path receives a new request at each integer second from 0 through 120, inclusive.
There are 101 post-removal attempts, at seconds 20 through 120. Under these assumptions, the recorded outcomes are:
| Delivery path | Check on the path to bytes | Admitted after removal, out of 101 attempts |
|---|---|---|
| Direct storage URL | Modeled URL and credential validity | 100 |
| Broker's retained target | Same storage check; broker bypassed | 100 |
| Gateway with cached permission | Cached allow until second 30 | 10 |
| Gateway with current permission | Current membership on every request | 0 |
The broker's separate issuance check allows zero new redirects after second 20. Its retained target still works through second 119. That is the difference the architecture must expose: the application can correctly deny a new link while an older delivery credential remains usable.
Inspect traces to see the boundaries. Current membership denies at second 20. The cached gateway still allows at second 29 and denies at second 30. Storage allows at second 119 and denies at second 120. The endpoint equality rules are choices in this toy model, not a reproduction of a cloud service's exact clock or propagation behavior.
Four additional assertions cover credentials that expire at second 90, their validity at second 89, removal of the signer's modeled permission and repeated use of the same capability. All pass. No HTTP server, CDN, permission database or active byte stream was exercised. The chart reports admission counts for this fixed schedule, not an incident rate or a recommended TTL.
Keep expiry, cache behavior and transfer completion distinct
S3's documentation says temporary credentials can shorten a URL's useful lifetime. It also documents a s3:signatureAge policy condition for bounding signature age. Those controls limit request validity. Neither automatically represents a SaaS member's current workspace entitlement.
S3 checks expiry when the HTTP request begins. A transfer admitted before expiry can continue afterward; a later restart can fail. Your product wording should distinguish a new request from a connection already receiving bytes. If the requirement includes stopping active transfers, design and test cancellation separately from request admission.
A CDN changes the serving boundary too. CloudFront's signed URL flow validates the signature and policy before normal cache-or-origin processing. That is a useful example of access enforcement preceding a cache hit. CloudFront signed URLs and S3 presigned URLs are different mechanisms. Do not assume changing one invalidates the other.
For an application-controlled sensitive download, consider a response caching policy that forbids storage, and verify the configured intermediaries. RFC 9111's `no-store` response directive defines that behavior for compliant caches and explicitly limits its privacy assurances. A cache header cannot erase a downloaded file or repair an alternate serving path that omitted authorization.
Record the decision and its reversal conditions
For a product that accepts a bounded remaining-link window, a short-lived direct capability can be the simpler design. Authorize issuance for the exact tenant and immutable export object. Define the permitted validity window and the incident action for a leaked link. Keep capability-bearing query strings out of ordinary application logs and support screenshots.
For a product that must reject new requests after a membership change, put the authorization boundary on every serving path. Document what "after" means: which permission source is authoritative, when removal is visible there, whether any allow decision is cached and how dependency failure is handled. Assign the freshness contract to the team responsible for authorization, rather than assuming storage expiry provides it.
Write the architecture decision record around four fields:
- Admission promise: stopping new issuance, new requests or an active transfer.
- Serving boundary: direct storage, redirect broker, gateway or an explicitly reviewed edge check.
- Stale-state policy: permitted authorization age, dependency failure behavior and the evidence required to change it.
- Reversal trigger: a tighter removal requirement, a new serving route or a transfer workload that changes the chosen boundary's feasibility.
The export handoff article covers package completeness and account closure. Keep this narrower delivery decision attached to that workflow without treating a completed export job as continuing authorization to retrieve its output.
Verify the retained link through the real serving path
Use a synthetic workspace and file to obtain the actual download target. Remove the recipient's membership, then exercise both the application entry point and the retained target. Test a new range request, another tenant's resource identifier and a permission dependency failure. A denied application page is insufficient evidence if the bytes remain reachable elsewhere.
Repeat the test with a warm CDN or browser cache where those caches are permitted. Inspect which component admitted the request and which object version it returned. Keep raw delivery credentials out of the diagnostic record. Record a safe request identifier, decision version and tested boundary instead. The authorization permission matrix supplies the surrounding resource and tenant acceptance cases.
These are proposed integration checks, not completed cloud tests. The downloadable model proves only the outcomes of its explicit predicates. Close the real release decision with evidence from the deployment's complete serving path, including a separate decision about transfers already in progress.
Sources
Documentation checked .

Continue the conversation
Comments (0)