Bohan Jiang
bdae0d2a03
fix(attachments): serve proxy-mode downloads with a scoped capability URL (MUL-5292) ( #6092 )
...
Desktop users on self-hosted deployments saw a save dialog but never got a
file. Electron's native download is a browser-level request: it carries
neither the desktop client's Authorization header nor a session cookie, so
GET /api/attachments/{id}/download answered 401.
The authenticated GET /api/attachments/{id} already exists to hand native
loaders a URL they can fetch without our credentials, and it already does so
in two of three modes -- a CloudFront-signed URL, or an S3 presigned URL.
Proxy mode (local disk, private object host) had no equivalent and kept
returning the auth-gated API path, which is the whole of the bug: one
unfinished branch of an otherwise correct design.
Finish that branch. In proxy mode the already-authenticated metadata endpoint
now mints a capability -- an HMAC-SHA256 signature over (version, attachment
id, expiry) with a key domain-separated from the JWT secret, valid for 60
seconds and scoped to exactly one attachment -- and a separate public route
redeems it. Membership is checked when the capability is minted, never at
redemption; the signature is the proof that check happened.
Nothing moves out of middleware.Auth: the existing authenticated download
route is untouched, so clients that predate this keep working and there is no
second copy of the header/cookie/PAT/task-token resolution. The capability
route always proxy-streams, so it emits no cross-origin redirect and the
signed query cannot leak to a CDN in a Referer.
The capability is site-relative and minted only by GetAttachmentByID. Both
matter: an absolute URL would be picked up by the inline-media re-sign path
and pinned into an <img> far longer than the TTL, and a capability in a list
response would expire before anything used it.
Verification: go test ./internal/handler/ ./cmd/server/ and the
@multica/views editor tests pass; gofmt, go vet, tsc --noEmit clean.
Co-authored-by: Bohan-J <bohan@devv.ai >
Co-authored-by: multica-agent <github@multica.ai >
2026-07-29 15:20:13 +08:00
..
2026-05-09 16:11:58 +08:00
2026-06-17 15:18:44 +02:00
2026-05-28 16:05:19 +08:00
2026-05-28 16:05:19 +08:00
2026-07-15 03:09:56 +08:00
2026-07-15 03:09:56 +08:00
2026-07-15 03:09:56 +08:00
2026-07-03 14:18:43 +08:00
2026-07-29 13:08:48 +08:00
2026-07-22 16:38:50 +08:00
2026-07-23 10:56:53 +08:00
2026-07-23 10:56:53 +08:00
2026-07-03 14:18:43 +08:00
2026-07-29 12:47:14 +08:00
2026-07-29 14:01:09 +08:00
2026-07-29 14:01:09 +08:00
2026-07-03 14:18:43 +08:00
2026-07-03 17:56:02 +08:00
2026-06-13 15:33:28 +08:00
2026-07-22 15:38:38 +08:00
2026-07-22 15:38:38 +08:00
2026-07-03 17:56:02 +08:00
2026-07-29 12:47:14 +08:00
2026-07-29 14:28:44 +08:00
2026-07-23 15:40:18 +08:00
2026-07-29 12:47:14 +08:00
2026-05-28 15:53:16 +08:00
2026-07-29 14:28:44 +08:00
2026-07-29 15:20:13 +08:00
2026-07-29 15:20:13 +08:00
2026-07-15 03:09:56 +08:00
2026-06-03 16:39:06 +08:00
2026-07-15 03:09:56 +08:00
2026-07-17 16:44:31 +08:00
2026-07-17 16:44:31 +08:00
2026-07-07 20:49:42 +08:00
2026-07-16 15:23:56 +08:00
2026-06-30 12:29:11 +08:00
2026-07-15 03:09:56 +08:00
2026-07-07 20:49:42 +08:00
2026-07-15 03:09:56 +08:00
2026-05-18 12:17:39 +08:00
2026-05-27 15:47:36 +08:00
2026-07-14 18:29:05 +08:00
2026-07-15 03:09:56 +08:00
2026-07-15 00:52:27 +08:00
2026-07-27 13:12:06 +08:00
2026-07-15 00:52:27 +08:00
2026-07-15 00:52:27 +08:00
2026-07-01 12:46:47 +08:00
2026-07-01 12:46:47 +08:00
2026-07-27 15:44:39 +08:00
2026-07-07 13:20:30 +08:00
2026-07-08 21:58:16 +08:00
2026-07-24 11:30:27 +08:00
2026-07-14 09:37:02 +08:00
2026-07-09 13:49:07 +08:00
2026-07-09 13:49:07 +08:00
2026-07-24 11:30:27 +08:00
2026-07-22 16:52:34 +08:00
2026-07-22 16:52:34 +08:00
2026-05-28 16:05:19 +08:00
2026-05-28 16:05:19 +08:00
2026-05-27 14:52:03 +08:00
2026-05-27 14:52:03 +08:00
2026-07-14 19:56:17 +08:00
2026-07-15 03:09:56 +08:00
2026-07-27 14:26:23 +08:00
2026-06-24 09:52:18 +08:00
2026-06-01 08:28:15 +08:00
2026-07-27 14:26:23 +08:00
2026-07-16 15:23:56 +08:00
2026-07-12 14:23:30 +08:00
2026-07-10 16:15:10 +08:00
2026-06-10 14:20:39 +08:00
2026-07-20 15:38:31 +08:00
2026-07-15 03:09:56 +08:00
2026-07-29 13:15:10 +08:00
2026-07-29 13:15:10 +08:00
2026-07-24 16:39:22 +08:00
2026-07-24 16:39:22 +08:00
2026-05-22 13:22:36 +08:00
2026-06-03 16:39:06 +08:00
2026-07-14 11:53:42 +08:00
2026-07-14 11:53:42 +08:00
2026-07-27 15:44:39 +08:00
2026-07-17 13:40:23 +08:00
2026-07-27 13:12:06 +08:00
2026-07-15 03:09:56 +08:00
2026-07-10 13:43:45 +08:00
2026-07-14 11:53:42 +08:00
2026-07-14 11:53:42 +08:00
2026-07-17 12:33:36 +08:00
2026-07-14 11:56:30 +08:00
2026-07-14 11:56:30 +08:00
2026-07-29 14:01:09 +08:00
2026-07-29 13:08:48 +08:00
2026-07-27 18:40:48 +08:00
2026-07-29 13:08:48 +08:00
2026-07-20 12:46:39 +08:00
2026-06-03 16:39:06 +08:00
2026-07-27 15:44:39 +08:00
2026-07-29 15:20:13 +08:00
2026-07-28 16:19:35 +08:00
2026-07-28 16:19:35 +08:00
2026-07-22 16:52:34 +08:00
2026-07-07 14:43:19 +08:00
2026-07-27 15:44:39 +08:00
2026-05-07 15:50:27 +08:00
2026-05-07 15:50:27 +08:00
2026-05-06 14:31:33 +08:00
2026-07-16 14:58:42 +08:00
2026-07-13 16:42:46 +08:00
2026-07-03 16:10:19 +08:00
2026-05-04 21:24:56 +08:00
2026-07-14 11:56:30 +08:00
2026-07-15 03:09:56 +08:00
2026-07-15 03:09:56 +08:00
2026-07-02 14:11:10 +08:00
2026-07-10 09:11:29 +08:00
2026-07-13 16:15:58 +08:00
2026-07-22 20:33:15 +08:00
2026-07-27 13:12:06 +08:00
2026-07-22 20:33:15 +08:00
2026-07-22 22:43:34 +08:00
2026-07-16 13:31:06 +08:00
2026-05-28 14:20:20 +08:00
2026-07-13 13:08:04 +08:00
2026-05-19 10:37:38 +08:00
2026-06-02 17:17:41 +08:00
2026-05-21 16:35:45 +08:00
2026-05-21 16:35:45 +08:00
2026-07-23 15:58:25 +08:00
2026-07-23 15:58:25 +08:00
2026-07-06 19:11:47 +08:00
2026-07-13 13:08:04 +08:00
2026-07-17 19:21:27 +08:00
2026-07-23 12:03:27 +08:00
2026-07-17 19:21:27 +08:00
2026-07-23 15:58:25 +08:00
2026-07-23 17:54:52 +08:00
2026-07-23 17:54:52 +08:00
2026-07-23 15:58:25 +08:00
2026-06-23 13:17:13 +08:00
2026-07-15 03:09:56 +08:00
2026-06-16 12:26:44 +08:00
2026-07-22 22:43:34 +08:00
2026-07-13 18:13:44 +08:00
2026-07-13 18:13:44 +08:00
2026-07-08 16:19:41 +08:00
2026-07-08 16:19:41 +08:00
2026-07-03 14:18:43 +08:00
2026-07-03 14:18:43 +08:00
2026-07-15 03:09:56 +08:00
2026-07-15 17:52:10 +08:00
2026-07-15 17:52:10 +08:00
2026-06-03 16:39:06 +08:00
2026-07-23 01:57:44 +08:00
2026-07-23 01:57:44 +08:00
2026-05-21 15:33:47 +08:00
2026-05-27 22:22:26 +08:00
2026-05-27 22:22:26 +08:00
2026-07-13 13:22:02 +08:00
2026-07-07 17:35:33 +08:00
2026-07-07 17:35:33 +08:00
2026-06-21 23:54:58 +08:00
2026-07-24 11:30:27 +08:00
2026-07-15 22:37:57 +08:00
2026-07-15 22:37:57 +08:00
2026-07-17 11:11:52 +08:00
2026-05-25 10:05:17 +08:00
2026-07-13 20:07:24 +08:00
2026-07-02 18:31:45 +08:00
2026-06-08 13:08:38 +08:00
2026-07-07 12:01:14 +08:00
2026-07-08 16:00:17 +08:00
2026-05-06 14:31:33 +08:00
2026-05-06 14:31:33 +08:00
2026-06-11 13:00:56 +08:00
2026-07-12 02:53:17 +08:00
2026-07-12 02:53:17 +08:00
2026-07-22 15:38:38 +08:00
2026-07-22 15:38:38 +08:00
2026-07-02 14:38:05 +08:00
2026-07-23 15:40:18 +08:00
2026-07-09 15:07:11 +08:00
2026-07-15 00:52:27 +08:00
2026-07-02 14:38:05 +08:00
2026-06-18 11:10:06 +08:00
2026-07-16 13:54:01 +08:00
2026-07-16 13:54:01 +08:00
2026-07-16 13:54:01 +08:00
2026-07-16 13:54:01 +08:00
2026-07-16 13:54:01 +08:00
2026-05-21 15:33:47 +08:00
2026-07-27 18:40:48 +08:00
2026-07-06 12:34:51 +08:00
2026-07-06 12:34:51 +08:00
2026-07-06 12:34:51 +08:00
2026-06-11 13:00:56 +08:00
2026-06-30 14:46:46 +08:00
2026-06-30 14:46:46 +08:00
2026-06-11 13:00:56 +08:00
2026-07-22 15:38:38 +08:00
2026-06-01 15:19:42 +08:00
2026-07-22 14:38:31 +08:00
2026-07-22 14:38:31 +08:00
2026-06-29 14:09:34 +08:00
2026-07-09 15:07:11 +08:00
2026-05-13 18:46:20 +08:00
2026-06-26 16:01:33 +08:00
2026-07-22 20:33:15 +08:00
2026-07-22 20:33:15 +08:00
2026-07-15 03:09:56 +08:00
2026-07-08 14:51:54 +08:00
2026-07-03 14:18:43 +08:00
2026-06-02 13:03:15 +08:00
2026-05-14 14:07:26 +08:00
2026-07-22 20:33:15 +08:00
2026-07-15 03:09:56 +08:00
2026-07-15 03:09:56 +08:00
2026-07-15 03:09:56 +08:00
2026-05-11 12:39:45 +08:00
2026-07-15 03:09:56 +08:00
2026-07-14 13:16:50 +08:00
2026-07-16 15:23:56 +08:00
2026-06-02 14:29:29 +08:00
2026-05-21 15:33:47 +08:00
2026-07-24 16:39:22 +08:00
2026-07-24 16:39:22 +08:00
2026-07-24 18:30:20 +08:00
2026-07-24 16:39:22 +08:00
2026-07-14 18:29:05 +08:00
2026-07-14 18:29:05 +08:00
2026-07-14 16:20:33 +08:00
2026-07-14 16:20:33 +08:00
2026-07-14 16:20:33 +08:00
2026-05-08 19:14:12 +08:00
2026-07-03 14:18:43 +08:00
2026-07-24 18:30:20 +08:00
2026-07-15 00:52:27 +08:00