diff --git a/src/vector/jitsi/index.ts b/src/vector/jitsi/index.ts
index 081246c0a9..6e697fee2f 100644
--- a/src/vector/jitsi/index.ts
+++ b/src/vector/jitsi/index.ts
@@ -18,7 +18,10 @@ limitations under the License.
require("./index.scss");
import * as qs from 'querystring';
-import { Capability, WidgetApi } from "matrix-react-sdk/src/widgets/WidgetApi";
+import {Capability, WidgetApi} from 'matrix-react-sdk/src/widgets/WidgetApi';
+import {KJUR} from 'jsrsasign';
+
+const JITSI_OPENIDTOKEN_JWT_AUTH = 'openidtoken-jwt';
// Dev note: we use raw JS without many dependencies to reduce bundle size.
// We do not need all of React to render a Jitsi conference.
@@ -33,6 +36,8 @@ let conferenceId: string;
let displayName: string;
let avatarUrl: string;
let userId: string;
+let jitsiAuth: string;
+let roomId: string;
let widgetApi: WidgetApi;
@@ -69,40 +74,118 @@ let widgetApi: WidgetApi;
displayName = qsParam('displayName', true);
avatarUrl = qsParam('avatarUrl', true); // http not mxc
userId = qsParam('userId');
+ jitsiAuth = qsParam('auth', true);
+ roomId = qsParam('roomId', true);
if (widgetApi) {
await widgetApi.waitReady();
await widgetApi.setAlwaysOnScreen(false); // start off as detachable from the screen
+
+ // See https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification
+ if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) {
+ // Request credentials, give callback to continue when received
+ widgetApi.requestOpenIDCredentials(credentialsResponseCallback);
+ } else {
+ enableJoinButton();
+ }
+ // TODO: register widgetApi listeners for PTT controls (https://github.com/vector-im/riot-web/issues/12795)
+ } else {
+ enableJoinButton();
}
-
- // TODO: register widgetApi listeners for PTT controls (https://github.com/vector-im/riot-web/issues/12795)
-
- document.getElementById("joinButton").onclick = () => joinConference();
} catch (e) {
console.error("Error setting up Jitsi widget", e);
- document.getElementById("jitsiContainer").innerText = "Failed to load Jitsi widget";
- switchVisibleContainers();
+ document.getElementById("widgetActionContainer").innerText = "Failed to load Jitsi widget";
}
})();
+/**
+ * Enable or show error depending on what the credentials response is.
+ */
+function credentialsResponseCallback() {
+ if (widgetApi.openIDCredentials) {
+ console.info('Successfully got OpenID credentials.');
+ enableJoinButton();
+ } else {
+ console.warn('OpenID credentials request was blocked by user.');
+ document.getElementById("widgetActionContainer").innerText = "Failed to load Jitsi widget";
+ }
+}
+
+function enableJoinButton() {
+ document.getElementById("joinButton").onclick = () => joinConference();
+}
+
function switchVisibleContainers() {
inConference = !inConference;
document.getElementById("jitsiContainer").style.visibility = inConference ? 'unset' : 'hidden';
document.getElementById("joinButtonContainer").style.visibility = inConference ? 'hidden' : 'unset';
}
+/**
+ * Create a JWT token fot jitsi openidtoken-jwt auth
+ *
+ * See https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification
+ */
+function createJWTToken() {
+ // Header
+ const header = {alg: 'HS256', typ: 'JWT'};
+ // Payload
+ const payload = {
+ // As per Jitsi token auth, `iss` needs to be set to something agreed between
+ // JWT generating side and Prosody config. Since we have no configuration for
+ // the widgets, we can't set one anywhere. Using the Jitsi domain here probably makes sense.
+ iss: jitsiDomain,
+ sub: jitsiDomain,
+ aud: `https://${jitsiDomain}`,
+ room: "*",
+ context: {
+ matrix: {
+ token: widgetApi.openIDCredentials.accessToken,
+ room_id: roomId,
+ },
+ user: {
+ avatar: avatarUrl,
+ name: displayName,
+ },
+ },
+ };
+ // Sign JWT
+ // The secret string here is irrelevant, we're only using the JWT
+ // to transport data to Prosody in the Jitsi stack.
+ return KJUR.jws.JWS.sign(
+ 'HS256',
+ JSON.stringify(header),
+ JSON.stringify(payload),
+ 'notused',
+ );
+}
+
function joinConference() { // event handler bound in HTML
+ let jwt;
+ if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) {
+ if (!widgetApi.openIDCredentials || !widgetApi.openIDCredentials.accessToken) {
+ // We've failing to get a token, don't try to init conference
+ console.warn('Expected to have an OpenID credential, cannot initialize widget.');
+ document.getElementById("widgetActionContainer").innerText = "Failed to load Jitsi widget";
+ return;
+ }
+ jwt = createJWTToken();
+ }
+
switchVisibleContainers();
- // noinspection JSIgnoredPromiseFromCall
- if (widgetApi) widgetApi.setAlwaysOnScreen(true); // ignored promise because we don't care if it works
+ if (widgetApi) {
+ // ignored promise because we don't care if it works
+ // noinspection JSIgnoredPromiseFromCall
+ widgetApi.setAlwaysOnScreen(true);
+ }
console.warn(
"[Jitsi Widget] The next few errors about failing to parse URL parameters are fine if " +
"they mention 'external_api' or 'jitsi' in the stack. They're just Jitsi Meet trying to parse " +
"our fragment values and not recognizing the options.",
);
- const meetApi = new JitsiMeetExternalAPI(jitsiDomain, {
+ const options = {
width: "100%",
height: "100%",
parentNode: document.querySelector("#jitsiContainer"),
@@ -113,7 +196,10 @@ function joinConference() { // event handler bound in HTML
MAIN_TOOLBAR_BUTTONS: [],
VIDEO_LAYOUT_FIT: "height",
},
- });
+ jwt: jwt,
+ };
+
+ const meetApi = new JitsiMeetExternalAPI(jitsiDomain, options);
if (displayName) meetApi.executeCommand("displayName", displayName);
if (avatarUrl) meetApi.executeCommand("avatarUrl", avatarUrl);
if (userId) meetApi.executeCommand("email", userId);
@@ -121,8 +207,11 @@ function joinConference() { // event handler bound in HTML
meetApi.on("readyToClose", () => {
switchVisibleContainers();
- // noinspection JSIgnoredPromiseFromCall
- if (widgetApi) widgetApi.setAlwaysOnScreen(false); // ignored promise because we don't care if it works
+ if (widgetApi) {
+ // ignored promise because we don't care if it works
+ // noinspection JSIgnoredPromiseFromCall
+ widgetApi.setAlwaysOnScreen(false);
+ }
document.getElementById("jitsiContainer").innerHTML = "";
});
diff --git a/yarn.lock b/yarn.lock
index 9b1342e5e6..441250b72c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6914,6 +6914,11 @@ jsprim@^1.2.2:
json-schema "0.2.3"
verror "1.10.0"
+jsrsasign@^9.1.5:
+ version "9.1.5"
+ resolved "https://registry.yarnpkg.com/jsrsasign/-/jsrsasign-9.1.5.tgz#fe286425d2c05b2d0865d24ded53e34b12abd2ca"
+ integrity sha512-iJLF8FvZHlwyQudrRtQomHj1HdPAcM8QSRTt0FJo8a6iFgaGCpKUrE7lWyELpAjrFs8jUC/Azc0vfhlj3yqHPQ==
+
jsx-ast-utils@^2.2.3:
version "2.3.0"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.3.0.tgz#edd727794ea284d7fda575015ed1b0cde0289ab6"