diff --git a/README.md b/README.md
index 0466b88014e..f958333276d 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,10 @@ endpoints from before Matrix 1.1, for example.
# Quickstart
+> [!IMPORTANT]
+> Servers may require or use authenticated endpoints for media (images, files, avatars, etc). See the
+> [Authenticated Media](#authenticated-media) section for information on how to enable support for this.
+
Using `yarn` instead of `npm` is recommended. Please see the Yarn [install guide](https://classic.yarnpkg.com/en/docs/install)
if you do not have it already.
@@ -89,6 +93,34 @@ Object.keys(client.store.rooms).forEach((roomId) => {
});
```
+## Authenticated media
+
+Servers supporting [MSC3916](https://github.com/matrix-org/matrix-spec-proposals/pull/3916) will require clients, like
+yours, to include an `Authorization` header when `/download`ing or `/thumbnail`ing media. For NodeJS environments this
+may be as easy as the following code snippet, though web browsers may need to use [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API)
+to append the header when using the endpoints in `` elements and similar.
+
+```javascript
+const downloadUrl = client.mxcUrlToHttp(
+ /*mxcUrl=*/ "mxc://example.org/abc123", // the MXC URI to download/thumbnail, typically from an event or profile
+ /*width=*/ undefined, // part of the thumbnail API. Use as required.
+ /*height=*/ undefined, // part of the thumbnail API. Use as required.
+ /*resizeMethod=*/ undefined, // part of the thumbnail API. Use as required.
+ /*allowDirectLinks=*/ false, // should generally be left `false`.
+ /*allowRedirects=*/ true, // implied supported with authentication
+ /*useAuthentication=*/ true, // the flag we're after in this example
+);
+const img = await fetch(downloadUrl, {
+ headers: {
+ Authorization: `Bearer ${client.getAccessToken()}`,
+ },
+});
+// Do something with `img`.
+```
+
+> [!WARNING]
+> In future the js-sdk will _only_ return authentication-required URLs, mandating population of the `Authorization` header.
+
## What does this SDK do?
This SDK provides a full object model around the Matrix Client-Server API and emits
diff --git a/spec/unit/content-repo.spec.ts b/spec/unit/content-repo.spec.ts
index 33eeab12d5e..e0f0b5e0c76 100644
--- a/spec/unit/content-repo.spec.ts
+++ b/spec/unit/content-repo.spec.ts
@@ -76,5 +76,29 @@ describe("ContentRepo", function () {
baseUrl + "/_matrix/media/v3/download/server.name/resourceid#automade",
);
});
+
+ it("should return an authenticated URL when requested", function () {
+ const mxcUri = "mxc://server.name/resourceid";
+ expect(getHttpUriForMxc(baseUrl, mxcUri, undefined, undefined, undefined, undefined, true, true)).toEqual(
+ baseUrl +
+ "/_matrix/client/unstable/org.matrix.msc3916/media/download/server.name/resourceid?allow_redirect=true",
+ );
+ expect(getHttpUriForMxc(baseUrl, mxcUri, 64, 64, "scale", undefined, true, true)).toEqual(
+ baseUrl +
+ "/_matrix/client/unstable/org.matrix.msc3916/media/thumbnail/server.name/resourceid?width=64&height=64&method=scale&allow_redirect=true",
+ );
+ });
+
+ it("should force-enable allow_redirects when useAuthentication is set true", function () {
+ const mxcUri = "mxc://server.name/resourceid";
+ expect(getHttpUriForMxc(baseUrl, mxcUri, undefined, undefined, undefined, undefined, false, true)).toEqual(
+ baseUrl +
+ "/_matrix/client/unstable/org.matrix.msc3916/media/download/server.name/resourceid?allow_redirect=true",
+ );
+ expect(getHttpUriForMxc(baseUrl, mxcUri, 64, 64, "scale", undefined, false, true)).toEqual(
+ baseUrl +
+ "/_matrix/client/unstable/org.matrix.msc3916/media/thumbnail/server.name/resourceid?width=64&height=64&method=scale&allow_redirect=true",
+ );
+ });
});
});
diff --git a/spec/unit/matrix-client.spec.ts b/spec/unit/matrix-client.spec.ts
index 62b479f89e4..b83e841fcf6 100644
--- a/spec/unit/matrix-client.spec.ts
+++ b/spec/unit/matrix-client.spec.ts
@@ -386,6 +386,9 @@ describe("MatrixClient", function () {
expect(client.mxcUrlToHttp(mxc, 32, 46, "scale", false, true)).toBe(
getHttpUriForMxc(client.baseUrl, mxc, 32, 46, "scale", false, true),
);
+ expect(client.mxcUrlToHttp(mxc, 32, 46, "scale", false, true, true)).toBe(
+ getHttpUriForMxc(client.baseUrl, mxc, 32, 46, "scale", false, true, true),
+ );
});
});
diff --git a/src/client.ts b/src/client.ts
index 64e6855ea79..bbaf8fd8f80 100644
--- a/src/client.ts
+++ b/src/client.ts
@@ -5775,7 +5775,12 @@ export class MatrixClient extends TypedEventEmitter = {};
if (width) {
@@ -68,7 +89,12 @@ export function getHttpUriForMxc(
if (Object.keys(params).length > 0) {
// these are thumbnailing params so they probably want the
// thumbnailing API...
- prefix = "/_matrix/media/v3/thumbnail/";
+ if (useAuthentication) {
+ // TODO: Use stable once available (requires FCP on MSC3916).
+ prefix = "/_matrix/client/unstable/org.matrix.msc3916/media/thumbnail/";
+ } else {
+ prefix = "/_matrix/media/v3/thumbnail/";
+ }
}
if (typeof allowRedirects === "boolean") {