diff --git a/content/docs/build/server-api.mdx b/content/docs/build/server-api.mdx index ecef5af..e4a2215 100644 --- a/content/docs/build/server-api.mdx +++ b/content/docs/build/server-api.mdx @@ -76,7 +76,35 @@ Sending is `chat:send` over the socket. There's no POST here. | DELETE | `/api/uploads/avatar` | Bearer | | | POST | `/api/uploads/group-icon` | Bearer | multipart `file`, a group's picture. Returns `fileId` for `dm:group:update` and doesn't change your avatar | | POST | `/api/uploads/webhook-avatar` | Bearer | multipart `file`, a webhook's avatar. Needs Manage webhooks. Returns `fileId` for the webhook's `avatar_file_id` and doesn't change your avatar | -| GET | `/api/uploads/files/:fileId` | — | `?thumb=1` for the thumbnail, `?download=1` for an attachment header | +| GET | `/api/uploads/files/:fileId` | Signed link | `?thumb=1` for the thumbnail, `?download=1` for an attachment header | + +A file URL can't carry a header, because most of them end up in an ``. +So each one is signed for one file, and it stops working after a few minutes. +The server hands out the key it's signed with in `fileKey`, on `server:joined`, +`token:refreshed` and `file:key`: + +```json +{ "key": "", "user": "", "until": 1790043200, "now": 1790000000000 } +``` + +`until` is when the key expires, in seconds. `now` is the server's clock in +milliseconds, so you can sign against its time rather than yours. To sign a URL: + +1. Pick `expires`, in seconds. It can be at most ten minutes ahead of the + server's clock, and no later than `until`. +2. Sign `file-url`, the file id, `thumb` or `full`, and `expires`, joined with + `\n`, using HMAC-SHA256 with the decoded key. +3. Add `u` (the `user`), `k` (`until`), `e` (`expires`) and `s` (the signature, + base64url with no padding) to the query string. + +``` +/api/uploads/files/?thumb=1&u=&k=&e=&s= +``` + +A link for one file won't open another, and a thumbnail link won't give you the +full file. When the server's or the member's token version moves, every link +signed with the old key stops working too. The server still takes the older +`?t=` for now, and will stop in a later release. ### Server @@ -209,6 +237,10 @@ Refusals come back on `server:error` with a reason: `invite_required`, | `session:restore` | client → server | | `token:refresh` | client → server | | `token:refreshed`, `token:invalid`, `token:revoked`, `token:error` | server → client | +| `file:key` | server → client | + +A restored session gets no `token:refreshed`, so `file:key` brings it the key for +signing file links. It's the same shape as `fileKey` above. ### Chat