SaaS architecture

Presigned downloads: decide where revocation happens

A redirect broker can stop issuing download links while an earlier target still works. Choose the boundary that enforces your promise: link issuance, new request admission or active-transfer cancellation.

Decision
Direct capability or current-request authorization
Executed fixture
4 paths; 121 attempted requests per path
Counterexample
Broker denies issuance; retained target still admits
Evidence boundary
Sequential model, not AWS or transfer testing
A paper pass and timer sit on an amber bridge beside a navy inspection portal leading to a file drawer.
Conceptual illustration of a retained delivery pass and a separate permission-checking boundary.

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.

Two proposed paths: a redirect exposes a storage target that bypasses later broker checks; a gateway keeps current authorization before bytes.
Proposed serving boundaries. A gateway controls new requests only if the origin cannot be reached through a bypass path. View full-size figure.

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:

Executed model: new requests admitted after membership removal, out of 101 attempts per delivery path
Delivery pathCheck on the path to bytesAdmitted after removal, out of 101 attempts
Direct storage URLModeled URL and credential validity100
Broker's retained targetSame storage check; broker bypassed100
Gateway with cached permissionCached allow until second 3010
Gateway with current permissionCurrent membership on every request0
Model post-removal admissions out of 101 attempts: direct storage 100, retained broker target 100, cached-permission gateway 10, current-permission gateway zero.
Executed sequential fixture: removal at second 20, URL expiry at 120 and permission cache expiry at 30. Counts are not production incident rates or delay measurements. View full-size figure.

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.

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 .

  1. Amazon S3 presigned URL validity
  2. AWS presigned-request permissions and reuse
  3. CloudFront signed URL validation
  4. RFC 9111 HTTP caching

Continue the conversation

Comments (0)

    Leave a comment

    Your name and comment stay in this page and are cleared after the spam check.

    10–2,000 characters. Keep the discussion relevant to this article.

    Spam protection verification
    Spam protection loads when you begin the form.

    JavaScript is required to use this form and its spam protection.