DEV Community

Gathmo
Gathmo

Posted on

Short-Lived Upload Credentials Without Giving the Browser Too Much Power

A guest upload page sits in a hostile place: an unauthenticated browser, often opened from a photographed QR code, on a network the event organizer does not control. The browser needs enough authority to send one file to one event. It should not receive a reusable credential that can list albums, read other media, or write forever.

Start with an upload intent

Create a server-side intent before issuing storage access. The request contains only information needed for policy checks:

{"eventId":"evt_123","uploadId":"client-uuid","size":7340032,"type":"image/jpeg"}
Enter fullscreen mode Exit fullscreen mode

The server verifies that the event accepts contributions, the type and size are allowed, and the rate limit has room. It then stores an intent with an expiry and expected object key. A retry with the same event and upload ID returns the same intent rather than allocating a second object.

Scope the credential to one object

A signed request should bind the HTTP method, object key, content length range, media type, and a short expiry. Do not sign a bucket prefix if the client only needs one object. Do not return cloud credentials that can be refreshed independently.

Five minutes is often enough for a photograph but not for a large video on congested venue Wi-Fi. Instead of increasing every expiry, let the control API renew an active intent after checking how much progress was made. Renewal should not change the destination key.

Separate bytes from acceptance

A successful storage upload does not mean the application accepted the contribution. The browser should call a completion endpoint that verifies object existence, byte length, checksum when available, and intent state.

created -> transferring -> stored -> accepted -> processing -> ready
Enter fullscreen mode Exit fullscreen mode

The completion call must be idempotent. If the response disappears during a network switch, the next call should return the already accepted state.

Prevent confused-deputy mistakes

Never let the client choose an arbitrary final object key. Derive it from trusted event and upload identifiers. Normalize the declared type, but verify the actual file signature during processing. A file named photo.jpg can still contain another format.

Keep organizer operations on a separate authorization path. A guest token must not become valid for deletion, moderation, export, or album settings simply because those endpoints share a domain.

Make abuse limits explicit

Apply limits at intent creation and completion: requests per network range, active intents per event, total bytes, and file count. Return a stable reason such as event_closed, type_not_allowed, or capacity_reached so the UI can explain what happened without revealing internal infrastructure.

Log credential issuance by event and intent, not by a persistent cross-event guest identity. Retain enough evidence to investigate abuse while avoiding a new tracking system.

Rotate signing keys safely

Include a key identifier and accept the previous verification key during a short overlap. Rotation should not invalidate every upload already in progress. Emergency revocation is different: the control plane may mark an event or intent closed even when a storage signature has not yet expired, and completion must respect that state.

I help build Gathmo and have a commercial interest in this problem. The Gathmo corporate event workflow is one context where scoped browser uploads matter, but the credential design is platform-neutral.

The core rule is simple: give the browser authority for one narrow action, for one narrow object, for one short period. Everything else belongs behind the application control plane.

Top comments (0)