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") {