Skip to content

Commit

Permalink
Typescriptify & use service worker for MSC3916 authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
turt2live committed Apr 11, 2024
1 parent 8ce46d3 commit 69817ca
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 4 deletions.
1 change: 0 additions & 1 deletion res/sw.js

This file was deleted.

77 changes: 77 additions & 0 deletions src/serviceworker/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
const serverSupportMap: {
[serverUrl: string]: {
supportsMSC3916: boolean,
cacheExpires: number,
},
} = {};

const credentialStore: {
[serverUrl: string]: string,
} = {};

// We skipWaiting() to update the service worker more frequently, particularly in development environments.
// @ts-expect-error - service worker types are not available. See 'fetch' event handler.
skipWaiting();

self.addEventListener("message", (event) => {
if (event.data?.type !== "credentials") return; // ignore
credentialStore[event.data.homeserverUrl] = event.data.accessToken;
console.log(`[Service Worker] Updated access token for ${event.data.homeserverUrl} (accessToken? ${Boolean(event.data.accessToken)})`);
});

// @ts-expect-error - getting types to work for this is difficult, so we anticipate that "addEventListener" doesn't
// have a valid signature.
self.addEventListener("fetch", (event: FetchEvent) => {
// This is the authenticated media (MSC3916) check, proxying what was unauthenticated to the authenticated variants.

if (event.request.method !== "GET") {
return; // not important to us
}

// Note: ideally we'd keep the request headers and etc, but in practice we can't even see those details.
// See https://stackoverflow.com/a/59152482
let url = event.request.url;

// We only intercept v3 download and thumbnail requests as presumably everything else is deliberate.
// For example, `/_matrix/media/unstable` or `/_matrix/media/v3/preview_url` are something well within
// the control of the application, and appear to be choices made at a higher level than us.
if (url.includes("/_matrix/media/v3/download") || url.includes("/_matrix/media/v3/thumbnail")) {
// We need to call respondWith synchronously, otherwise we may never execute properly. This means
// later on we need to proxy the request through if it turns out the server doesn't support authentication.
event.respondWith((async (): Promise<Response> => {
// Figure out which homeserver we're communicating with
const csApi = url.substring(0, url.indexOf("/_matrix/media/v3"));

// Locate our access token, and populate the fetchConfig with the authentication header.
const accessToken = credentialStore[csApi];
let fetchConfig: {headers?: {[key: string]: string}} = {};
if (accessToken) {
fetchConfig = {
headers: {
Authorization: `Bearer ${accessToken}`,
},
};
}

// Update or populate the server support map using a (usually) authenticated `/versions` call.
if (!serverSupportMap[csApi] || serverSupportMap[csApi].cacheExpires <= (new Date()).getTime()) {
const versions = await (await fetch(`${csApi}/_matrix/client/versions`, fetchConfig)).json();
serverSupportMap[csApi] = {
supportsMSC3916: Boolean(versions?.unstable_features?.["org.matrix.msc3916"]),
cacheExpires: (new Date()).getTime() + (2 * 60 * 60 * 1000), // 2 hours from now
};
}

// If we have server support (and a means of authentication), rewrite the URL to use MSC3916 endpoints.
if (serverSupportMap[csApi].supportsMSC3916 && accessToken) {
// Currently unstable only.
url = url.replace(/\/media\/v3\/(.*)\//, "/client/unstable/org.matrix.msc3916/media/$1/");
} // else by default we make no changes

// Add authentication and send the request. We add authentication even if MSC3916 endpoints aren't
// being used to ensure patches like this work:
// https://github.com/matrix-org/synapse/commit/2390b66bf0ec3ff5ffb0c7333f3c9b239eeb92bb
return fetch(url, fetchConfig);
})());
}
});
5 changes: 4 additions & 1 deletion src/vector/platform/WebPlatform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ export default class WebPlatform extends VectorBasePlatform {
super();
// Register service worker if available on this platform
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("sw.js");
// sw.js is exported by webpack, sourced from `/src/serviceworker/index.ts`
navigator.serviceWorker.register("sw.js")

Check warning on line 50 in src/vector/platform/WebPlatform.ts

View workflow job for this annotation

GitHub Actions / Jest

RETRY 1: WebPlatform › registers service worker

TypeError: Cannot read properties of undefined (reading 'then') at new WebPlatform (src/vector/platform/WebPlatform.ts:50:54) at Object.<anonymous> (test/unit-tests/vector/platform/WebPlatform-test.ts:40:9)

Check warning on line 50 in src/vector/platform/WebPlatform.ts

View workflow job for this annotation

GitHub Actions / Jest

RETRY 2: WebPlatform › registers service worker

TypeError: Cannot read properties of undefined (reading 'then') at new WebPlatform (src/vector/platform/WebPlatform.ts:50:54) at Object.<anonymous> (test/unit-tests/vector/platform/WebPlatform-test.ts:40:9)

Check failure on line 50 in src/vector/platform/WebPlatform.ts

View workflow job for this annotation

GitHub Actions / Jest

WebPlatform › registers service worker

TypeError: Cannot read properties of undefined (reading 'then') at new WebPlatform (src/vector/platform/WebPlatform.ts:50:54) at Object.<anonymous> (test/unit-tests/vector/platform/WebPlatform-test.ts:40:9)

Check warning on line 50 in src/vector/platform/WebPlatform.ts

View workflow job for this annotation

GitHub Actions / Jest

RETRY 1: WebPlatform › should call reload on window location object

TypeError: Cannot read properties of undefined (reading 'then') at new WebPlatform (src/vector/platform/WebPlatform.ts:50:54) at Object.<anonymous> (test/unit-tests/vector/platform/WebPlatform-test.ts:47:26)

Check warning on line 50 in src/vector/platform/WebPlatform.ts

View workflow job for this annotation

GitHub Actions / Jest

RETRY 2: WebPlatform › should call reload on window location object

TypeError: Cannot read properties of undefined (reading 'then') at new WebPlatform (src/vector/platform/WebPlatform.ts:50:54) at Object.<anonymous> (test/unit-tests/vector/platform/WebPlatform-test.ts:47:26)

Check failure on line 50 in src/vector/platform/WebPlatform.ts

View workflow job for this annotation

GitHub Actions / Jest

WebPlatform › should call reload on window location object

TypeError: Cannot read properties of undefined (reading 'then') at new WebPlatform (src/vector/platform/WebPlatform.ts:50:54) at Object.<anonymous> (test/unit-tests/vector/platform/WebPlatform-test.ts:47:26)

Check warning on line 50 in src/vector/platform/WebPlatform.ts

View workflow job for this annotation

GitHub Actions / Jest

RETRY 1: WebPlatform › should call reload to install update

TypeError: Cannot read properties of undefined (reading 'then') at new WebPlatform (src/vector/platform/WebPlatform.ts:50:54) at Object.<anonymous> (test/unit-tests/vector/platform/WebPlatform-test.ts:56:26)

Check warning on line 50 in src/vector/platform/WebPlatform.ts

View workflow job for this annotation

GitHub Actions / Jest

RETRY 2: WebPlatform › should call reload to install update

TypeError: Cannot read properties of undefined (reading 'then') at new WebPlatform (src/vector/platform/WebPlatform.ts:50:54) at Object.<anonymous> (test/unit-tests/vector/platform/WebPlatform-test.ts:56:26)

Check failure on line 50 in src/vector/platform/WebPlatform.ts

View workflow job for this annotation

GitHub Actions / Jest

WebPlatform › should call reload to install update

TypeError: Cannot read properties of undefined (reading 'then') at new WebPlatform (src/vector/platform/WebPlatform.ts:50:54) at Object.<anonymous> (test/unit-tests/vector/platform/WebPlatform-test.ts:56:26)
.then(r => r.update())
.catch(e => console.error("Error registering/updating service worker:", e));
}
}

Expand Down
7 changes: 5 additions & 2 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ module.exports = (env, argv) => {
mobileguide: "./src/vector/mobile_guide/index.ts",
jitsi: "./src/vector/jitsi/index.ts",
usercontent: "./node_modules/matrix-react-sdk/src/usercontent/index.ts",
serviceworker: {
import: "./src/serviceworker/index.ts",
filename: "sw.js", // update WebPlatform if this changes
},
...(useHMR ? {} : cssThemes),
},

Expand Down Expand Up @@ -666,7 +670,7 @@ module.exports = (env, argv) => {
// HtmlWebpackPlugin will screw up our formatting like the names
// of the themes and which chunks we actually care about.
inject: false,
excludeChunks: ["mobileguide", "usercontent", "jitsi"],
excludeChunks: ["mobileguide", "usercontent", "jitsi", "serviceworker"],
minify: false,
templateParameters: {
og_image_url: ogImageUrl,
Expand Down Expand Up @@ -739,7 +743,6 @@ module.exports = (env, argv) => {
"res/jitsi_external_api.min.js",
"res/jitsi_external_api.min.js.LICENSE.txt",
"res/manifest.json",
"res/sw.js",
"res/welcome.html",
{ from: "welcome/**", context: path.resolve(__dirname, "res") },
{ from: "themes/**", context: path.resolve(__dirname, "res") },
Expand Down

0 comments on commit 69817ca

Please sign in to comment.