Skip to content

Commit 402adfb

Browse files
authored
Add helpers for authenticated media, and associated documentation (#4185)
* Add helpers for authenticated media, and associated documentation * Appease the linter
1 parent 41e8c2a commit 402adfb

File tree

5 files changed

+105
-5
lines changed

5 files changed

+105
-5
lines changed

README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ endpoints from before Matrix 1.1, for example.
2121

2222
# Quickstart
2323

24+
> [!IMPORTANT]
25+
> Servers may require or use authenticated endpoints for media (images, files, avatars, etc). See the
26+
> [Authenticated Media](#authenticated-media) section for information on how to enable support for this.
27+
2428
Using `yarn` instead of `npm` is recommended. Please see the Yarn [install guide](https://classic.yarnpkg.com/en/docs/install)
2529
if you do not have it already.
2630

@@ -89,6 +93,34 @@ Object.keys(client.store.rooms).forEach((roomId) => {
8993
});
9094
```
9195

96+
## Authenticated media
97+
98+
Servers supporting [MSC3916](https://github.com/matrix-org/matrix-spec-proposals/pull/3916) will require clients, like
99+
yours, to include an `Authorization` header when `/download`ing or `/thumbnail`ing media. For NodeJS environments this
100+
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)
101+
to append the header when using the endpoints in `<img />` elements and similar.
102+
103+
```javascript
104+
const downloadUrl = client.mxcUrlToHttp(
105+
/*mxcUrl=*/ "mxc://example.org/abc123", // the MXC URI to download/thumbnail, typically from an event or profile
106+
/*width=*/ undefined, // part of the thumbnail API. Use as required.
107+
/*height=*/ undefined, // part of the thumbnail API. Use as required.
108+
/*resizeMethod=*/ undefined, // part of the thumbnail API. Use as required.
109+
/*allowDirectLinks=*/ false, // should generally be left `false`.
110+
/*allowRedirects=*/ true, // implied supported with authentication
111+
/*useAuthentication=*/ true, // the flag we're after in this example
112+
);
113+
const img = await fetch(downloadUrl, {
114+
headers: {
115+
Authorization: `Bearer ${client.getAccessToken()}`,
116+
},
117+
});
118+
// Do something with `img`.
119+
```
120+
121+
> [!WARNING]
122+
> In future the js-sdk will _only_ return authentication-required URLs, mandating population of the `Authorization` header.
123+
92124
## What does this SDK do?
93125

94126
This SDK provides a full object model around the Matrix Client-Server API and emits

spec/unit/content-repo.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,5 +76,29 @@ describe("ContentRepo", function () {
7676
baseUrl + "/_matrix/media/v3/download/server.name/resourceid#automade",
7777
);
7878
});
79+
80+
it("should return an authenticated URL when requested", function () {
81+
const mxcUri = "mxc://server.name/resourceid";
82+
expect(getHttpUriForMxc(baseUrl, mxcUri, undefined, undefined, undefined, undefined, true, true)).toEqual(
83+
baseUrl +
84+
"/_matrix/client/unstable/org.matrix.msc3916/media/download/server.name/resourceid?allow_redirect=true",
85+
);
86+
expect(getHttpUriForMxc(baseUrl, mxcUri, 64, 64, "scale", undefined, true, true)).toEqual(
87+
baseUrl +
88+
"/_matrix/client/unstable/org.matrix.msc3916/media/thumbnail/server.name/resourceid?width=64&height=64&method=scale&allow_redirect=true",
89+
);
90+
});
91+
92+
it("should force-enable allow_redirects when useAuthentication is set true", function () {
93+
const mxcUri = "mxc://server.name/resourceid";
94+
expect(getHttpUriForMxc(baseUrl, mxcUri, undefined, undefined, undefined, undefined, false, true)).toEqual(
95+
baseUrl +
96+
"/_matrix/client/unstable/org.matrix.msc3916/media/download/server.name/resourceid?allow_redirect=true",
97+
);
98+
expect(getHttpUriForMxc(baseUrl, mxcUri, 64, 64, "scale", undefined, false, true)).toEqual(
99+
baseUrl +
100+
"/_matrix/client/unstable/org.matrix.msc3916/media/thumbnail/server.name/resourceid?width=64&height=64&method=scale&allow_redirect=true",
101+
);
102+
});
79103
});
80104
});

spec/unit/matrix-client.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,9 @@ describe("MatrixClient", function () {
386386
expect(client.mxcUrlToHttp(mxc, 32, 46, "scale", false, true)).toBe(
387387
getHttpUriForMxc(client.baseUrl, mxc, 32, 46, "scale", false, true),
388388
);
389+
expect(client.mxcUrlToHttp(mxc, 32, 46, "scale", false, true, true)).toBe(
390+
getHttpUriForMxc(client.baseUrl, mxc, 32, 46, "scale", false, true, true),
391+
);
389392
});
390393
});
391394

src/client.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5775,7 +5775,12 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
57755775
* anyone they share a room with. If false, will return null for such URLs.
57765776
* @param allowRedirects - If true, the caller supports the URL being 307 or
57775777
* 308 redirected to another resource upon request. If false, redirects
5778-
* are not expected.
5778+
* are not expected. Implied `true` when `useAuthentication` is `true`.
5779+
* @param useAuthentication - If true, the caller supports authenticated
5780+
* media and wants an authentication-required URL. Note that server support
5781+
* for authenticated media will *not* be checked - it is the caller's responsibility
5782+
* to do so before calling this function. Note also that `useAuthentication`
5783+
* implies `allowRedirects`. Defaults to false (unauthenticated endpoints).
57795784
* @returns the avatar URL or null.
57805785
*/
57815786
public mxcUrlToHttp(
@@ -5785,8 +5790,18 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
57855790
resizeMethod?: string,
57865791
allowDirectLinks?: boolean,
57875792
allowRedirects?: boolean,
5793+
useAuthentication?: boolean,
57885794
): string | null {
5789-
return getHttpUriForMxc(this.baseUrl, mxcUrl, width, height, resizeMethod, allowDirectLinks, allowRedirects);
5795+
return getHttpUriForMxc(
5796+
this.baseUrl,
5797+
mxcUrl,
5798+
width,
5799+
height,
5800+
resizeMethod,
5801+
allowDirectLinks,
5802+
allowRedirects,
5803+
useAuthentication,
5804+
);
57905805
}
57915806

57925807
/**

src/content-repo.ts

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ import { encodeParams } from "./utils";
3030
* for such URLs.
3131
* @param allowRedirects - If true, the caller supports the URL being 307 or
3232
* 308 redirected to another resource upon request. If false, redirects
33-
* are not expected.
33+
* are not expected. Implied `true` when `useAuthentication` is `true`.
34+
* @param useAuthentication - If true, the caller supports authenticated
35+
* media and wants an authentication-required URL. Note that server support
36+
* for authenticated media will *not* be checked - it is the caller's responsibility
37+
* to do so before calling this function. Note also that `useAuthentication`
38+
* implies `allowRedirects`. Defaults to false (unauthenticated endpoints).
3439
* @returns The complete URL to the content.
3540
*/
3641
export function getHttpUriForMxc(
@@ -41,6 +46,7 @@ export function getHttpUriForMxc(
4146
resizeMethod?: string,
4247
allowDirectLinks = false,
4348
allowRedirects?: boolean,
49+
useAuthentication?: boolean,
4450
): string {
4551
if (typeof mxc !== "string" || !mxc) {
4652
return "";
@@ -52,8 +58,23 @@ export function getHttpUriForMxc(
5258
return "";
5359
}
5460
}
61+
62+
if (useAuthentication) {
63+
allowRedirects = true; // per docs (MSC3916 always expects redirects)
64+
65+
// Dev note: MSC3916 (as of writing) removes `allow_redirect` entirely, but
66+
// for explicitness we set it here. This makes it slightly more obvious to
67+
// callers, hopefully.
68+
}
69+
5570
let serverAndMediaId = mxc.slice(6); // strips mxc://
56-
let prefix = "/_matrix/media/v3/download/";
71+
let prefix: string;
72+
if (useAuthentication) {
73+
// TODO: Use stable once available (requires FCP on MSC3916).
74+
prefix = "/_matrix/client/unstable/org.matrix.msc3916/media/download/";
75+
} else {
76+
prefix = "/_matrix/media/v3/download/";
77+
}
5778
const params: Record<string, string> = {};
5879

5980
if (width) {
@@ -68,7 +89,12 @@ export function getHttpUriForMxc(
6889
if (Object.keys(params).length > 0) {
6990
// these are thumbnailing params so they probably want the
7091
// thumbnailing API...
71-
prefix = "/_matrix/media/v3/thumbnail/";
92+
if (useAuthentication) {
93+
// TODO: Use stable once available (requires FCP on MSC3916).
94+
prefix = "/_matrix/client/unstable/org.matrix.msc3916/media/thumbnail/";
95+
} else {
96+
prefix = "/_matrix/media/v3/thumbnail/";
97+
}
7298
}
7399

74100
if (typeof allowRedirects === "boolean") {

0 commit comments

Comments
 (0)