Skip to content

Commit da0afed

Browse files
authored
Merge pull request element-hq#15114 from vector-im/jaywink/jitsi-openidjwt-auth
Support usage of Jitsi widgets with "openidtoken-jwt" auth
2 parents e5a4092 + 7018a49 commit da0afed

File tree

4 files changed

+111
-14
lines changed

4 files changed

+111
-14
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"browser-request": "^0.3.3",
6060
"gfm.css": "^1.1.2",
6161
"highlight.js": "^9.13.1",
62+
"jsrsasign": "^9.1.5",
6263
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
6364
"matrix-react-sdk": "github:matrix-org/matrix-react-sdk#develop",
6465
"olm": "https://packages.matrix.org/npm/olm/olm-3.1.4.tgz",

src/vector/jitsi/index.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
<div class="joinConferencePrompt">
1212
<!-- TODO: i18n -->
1313
<h2>Jitsi Video Conference</h2>
14-
<button type="button" id="joinButton">Join Conference</button>
14+
<div id="widgetActionContainer">
15+
<button type="button" id="joinButton">Join Conference</button>
16+
</div>
1517
</div>
1618
</div>
1719
</div>

src/vector/jitsi/index.ts

Lines changed: 102 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ limitations under the License.
1818
require("./index.scss");
1919

2020
import * as qs from 'querystring';
21-
import { Capability, WidgetApi } from "matrix-react-sdk/src/widgets/WidgetApi";
21+
import {Capability, WidgetApi} from 'matrix-react-sdk/src/widgets/WidgetApi';
22+
import {KJUR} from 'jsrsasign';
23+
24+
const JITSI_OPENIDTOKEN_JWT_AUTH = 'openidtoken-jwt';
2225

2326
// Dev note: we use raw JS without many dependencies to reduce bundle size.
2427
// We do not need all of React to render a Jitsi conference.
@@ -33,6 +36,8 @@ let conferenceId: string;
3336
let displayName: string;
3437
let avatarUrl: string;
3538
let userId: string;
39+
let jitsiAuth: string;
40+
let roomId: string;
3641

3742
let widgetApi: WidgetApi;
3843

@@ -69,40 +74,118 @@ let widgetApi: WidgetApi;
6974
displayName = qsParam('displayName', true);
7075
avatarUrl = qsParam('avatarUrl', true); // http not mxc
7176
userId = qsParam('userId');
77+
jitsiAuth = qsParam('auth', true);
78+
roomId = qsParam('roomId', true);
7279

7380
if (widgetApi) {
7481
await widgetApi.waitReady();
7582
await widgetApi.setAlwaysOnScreen(false); // start off as detachable from the screen
76-
}
7783

78-
// TODO: register widgetApi listeners for PTT controls (https://github.com/vector-im/riot-web/issues/12795)
79-
80-
document.getElementById("joinButton").onclick = () => joinConference();
84+
// See https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification
85+
if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) {
86+
// Request credentials, give callback to continue when received
87+
widgetApi.requestOpenIDCredentials(credentialsResponseCallback);
88+
} else {
89+
enableJoinButton();
90+
}
91+
// TODO: register widgetApi listeners for PTT controls (https://github.com/vector-im/riot-web/issues/12795)
92+
} else {
93+
enableJoinButton();
94+
}
8195
} catch (e) {
8296
console.error("Error setting up Jitsi widget", e);
83-
document.getElementById("jitsiContainer").innerText = "Failed to load Jitsi widget";
84-
switchVisibleContainers();
97+
document.getElementById("widgetActionContainer").innerText = "Failed to load Jitsi widget";
8598
}
8699
})();
87100

101+
/**
102+
* Enable or show error depending on what the credentials response is.
103+
*/
104+
function credentialsResponseCallback() {
105+
if (widgetApi.openIDCredentials) {
106+
console.info('Successfully got OpenID credentials.');
107+
enableJoinButton();
108+
} else {
109+
console.warn('OpenID credentials request was blocked by user.');
110+
document.getElementById("widgetActionContainer").innerText = "Failed to load Jitsi widget";
111+
}
112+
}
113+
114+
function enableJoinButton() {
115+
document.getElementById("joinButton").onclick = () => joinConference();
116+
}
117+
88118
function switchVisibleContainers() {
89119
inConference = !inConference;
90120
document.getElementById("jitsiContainer").style.visibility = inConference ? 'unset' : 'hidden';
91121
document.getElementById("joinButtonContainer").style.visibility = inConference ? 'hidden' : 'unset';
92122
}
93123

124+
/**
125+
* Create a JWT token fot jitsi openidtoken-jwt auth
126+
*
127+
* See https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification
128+
*/
129+
function createJWTToken() {
130+
// Header
131+
const header = {alg: 'HS256', typ: 'JWT'};
132+
// Payload
133+
const payload = {
134+
// As per Jitsi token auth, `iss` needs to be set to something agreed between
135+
// JWT generating side and Prosody config. Since we have no configuration for
136+
// the widgets, we can't set one anywhere. Using the Jitsi domain here probably makes sense.
137+
iss: jitsiDomain,
138+
sub: jitsiDomain,
139+
aud: `https://${jitsiDomain}`,
140+
room: "*",
141+
context: {
142+
matrix: {
143+
token: widgetApi.openIDCredentials.accessToken,
144+
room_id: roomId,
145+
},
146+
user: {
147+
avatar: avatarUrl,
148+
name: displayName,
149+
},
150+
},
151+
};
152+
// Sign JWT
153+
// The secret string here is irrelevant, we're only using the JWT
154+
// to transport data to Prosody in the Jitsi stack.
155+
return KJUR.jws.JWS.sign(
156+
'HS256',
157+
JSON.stringify(header),
158+
JSON.stringify(payload),
159+
'notused',
160+
);
161+
}
162+
94163
function joinConference() { // event handler bound in HTML
164+
let jwt;
165+
if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) {
166+
if (!widgetApi.openIDCredentials || !widgetApi.openIDCredentials.accessToken) {
167+
// We've failing to get a token, don't try to init conference
168+
console.warn('Expected to have an OpenID credential, cannot initialize widget.');
169+
document.getElementById("widgetActionContainer").innerText = "Failed to load Jitsi widget";
170+
return;
171+
}
172+
jwt = createJWTToken();
173+
}
174+
95175
switchVisibleContainers();
96176

97-
// noinspection JSIgnoredPromiseFromCall
98-
if (widgetApi) widgetApi.setAlwaysOnScreen(true); // ignored promise because we don't care if it works
177+
if (widgetApi) {
178+
// ignored promise because we don't care if it works
179+
// noinspection JSIgnoredPromiseFromCall
180+
widgetApi.setAlwaysOnScreen(true);
181+
}
99182

100183
console.warn(
101184
"[Jitsi Widget] The next few errors about failing to parse URL parameters are fine if " +
102185
"they mention 'external_api' or 'jitsi' in the stack. They're just Jitsi Meet trying to parse " +
103186
"our fragment values and not recognizing the options.",
104187
);
105-
const meetApi = new JitsiMeetExternalAPI(jitsiDomain, {
188+
const options = {
106189
width: "100%",
107190
height: "100%",
108191
parentNode: document.querySelector("#jitsiContainer"),
@@ -113,16 +196,22 @@ function joinConference() { // event handler bound in HTML
113196
MAIN_TOOLBAR_BUTTONS: [],
114197
VIDEO_LAYOUT_FIT: "height",
115198
},
116-
});
199+
jwt: jwt,
200+
};
201+
202+
const meetApi = new JitsiMeetExternalAPI(jitsiDomain, options);
117203
if (displayName) meetApi.executeCommand("displayName", displayName);
118204
if (avatarUrl) meetApi.executeCommand("avatarUrl", avatarUrl);
119205
if (userId) meetApi.executeCommand("email", userId);
120206

121207
meetApi.on("readyToClose", () => {
122208
switchVisibleContainers();
123209

124-
// noinspection JSIgnoredPromiseFromCall
125-
if (widgetApi) widgetApi.setAlwaysOnScreen(false); // ignored promise because we don't care if it works
210+
if (widgetApi) {
211+
// ignored promise because we don't care if it works
212+
// noinspection JSIgnoredPromiseFromCall
213+
widgetApi.setAlwaysOnScreen(false);
214+
}
126215

127216
document.getElementById("jitsiContainer").innerHTML = "";
128217
});

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6914,6 +6914,11 @@ jsprim@^1.2.2:
69146914
json-schema "0.2.3"
69156915
verror "1.10.0"
69166916

6917+
jsrsasign@^9.1.5:
6918+
version "9.1.5"
6919+
resolved "https://registry.yarnpkg.com/jsrsasign/-/jsrsasign-9.1.5.tgz#fe286425d2c05b2d0865d24ded53e34b12abd2ca"
6920+
integrity sha512-iJLF8FvZHlwyQudrRtQomHj1HdPAcM8QSRTt0FJo8a6iFgaGCpKUrE7lWyELpAjrFs8jUC/Azc0vfhlj3yqHPQ==
6921+
69176922
jsx-ast-utils@^2.2.3:
69186923
version "2.3.0"
69196924
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.3.0.tgz#edd727794ea284d7fda575015ed1b0cde0289ab6"

0 commit comments

Comments
 (0)