Description
Problem
We've recently rolled out authenticated media. Whilst this has been broadly successful, it requires that clients send Authentication headers when making media requests. This is problematic for web clients where the request for the media usually comes straight from the browser and therefore needs to be intercepted by a service worker (or similar on electron etc) to add the header. Critically, this means (unencrypted) media simply doesn't work anywhere service workers aren't available. At time of writing, this includes Firefox in Private Browsing mode which is a nontrivial amount of users.
The only workaround would be to treat unencrypted media as we do encrypted and fetch it separately with a fetch request, then load it into a blob to display. This wouldn't work well for large files though.
Proposal
We can either accept this situation as it is (and ignore Firefox Private Browsing unless/until they allow service workers) or we can introduce a different method.
Advantages of changing are:
- Firefox Private Browsing / other browsers without service workers will work again
- No service worker / request interception dance necessary for browser or browser-like clients, therefore much easier to write these clients.
Disadvantages:
- It's a lot of work
I've had a chat with spec core team members on this, and the main alternatives we came up with were:
Cookies
Authenticate to the media repo by sending a regular cookie, like in the 2000's. Non-web clients would likely just be able to set the Cookie header the same way they set the Authentication header. Web clients would need a little work to avoid relying on third-party cookies. The most plausible way to do this may be to add an endpoint on the homeserver that's authenticated via the regular Authentication header but sets that same token as a cookie. A browser can then call this endpoint to set the cookie in the browser, meaning that it will be sent on all requests to the homeserver from that point on (so non media requests might end up with double auth, or perhaps we'd rely on using the cookie header for all C/S requests?)
- 👍 Cookies are older than the hills and have wide support
- 👎 Mobile apps & http libraries don't do cookie management automatically
- 👎 Any solution involving third-party cookies is probably a non-starter, so we'd need something like the cookie setter endpoint
Timed Query Params
Add a query parameter to every URL that contains the auth. This would likely be some kind of hash like token_id+time+h(token+time)
so the server could check the token was valid and used within whatever time window it required. Clients could lie about the time but the URL would only be valid for x time after the given time.
Alternatively, the client could do an extra request to the HS to get a URL that would give it media directly. This would allow the HS to do whatever auth semantics it needs (linked media etc.) and would be broadly equivalent to an HTTP redirect (or even faster if we did them in batches).
- 👍 No issues about support: it's just a URL with a query param
- 👍 Potentially CDN-compatible: similar schemes are commonly used for CDNs so that the CDN doesn't need to receive the main token or worry about auth semantics: the client just presents a token saying it's allowed to get x piece of media and the CDN validates that (although we'd need something more than access token hashes to achieve this, especially with linked media).
- 👍 Widely used in practice (S3 / cloudfront, Discord)
- 👎 Time-limited tokens may linger in HTTP access logs
- 👎 More work for clients because they need to do hashing (although we could continue to support Auth headers for non-browser clients)
Next Steps
We think, to move forward on this, we would like:
- Concrete research into how else this is done in the wild. eg. for token URLs, cloudfront have already solved this: what can we learn rather than reinventing the wheel? What about other messengers that have web clients?
- A team (probably a web client implementation, cough Element) to take the lead on this and prototype out one of the solutions.
Activity