Compare commits

...

9 Commits

3 changed files with 84 additions and 7 deletions

View File

@@ -10,6 +10,7 @@ import { logger } from "matrix-js-sdk/lib/logger";
import { FailToGetOpenIdToken } from "../utils/errors";
import { doNetworkOperationWithRetry } from "../utils/matrix";
import { Config } from "../config/Config.ts";
export interface SFUConfig {
url: string;
@@ -19,7 +20,7 @@ export interface SFUConfig {
// The bits we need from MatrixClient
export type OpenIDClientParts = Pick<
MatrixClient,
"getOpenIdToken" | "getDeviceId"
"getOpenIdToken" | "getDeviceId" | "baseUrl" | "getUserId"
>;
/**
* Gets a bearer token from the homeserver and then use it to authenticate
@@ -35,6 +36,7 @@ export async function getSFUConfigWithOpenID(
client: OpenIDClientParts,
serviceUrl: string,
matrixRoomId: string,
delayId: string,
): Promise<SFUConfig> {
let openIdToken: IOpenIDToken;
try {
@@ -54,6 +56,7 @@ export async function getSFUConfigWithOpenID(
serviceUrl,
matrixRoomId,
openIdToken,
delayId,
);
logger.info(`Got JWT from call's active focus URL.`);
@@ -65,18 +68,46 @@ async function getLiveKitJWT(
livekitServiceURL: string,
roomName: string,
openIDToken: IOpenIDToken,
delayId: string,
): Promise<SFUConfig> {
// TODO: rawId is missing a random component
// Note This random component needs to be stored/present during the lifetime this MatrixRTC client
let rawId = (client.getUserId() ?? "") + client.getDeviceId()
const utf8 = new TextEncoder().encode(rawId);
let idHashed = await crypto.subtle.digest('SHA-256', utf8).then((hashBuffer) => {
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(bytes => bytes.toString(16).padStart(2, '0')).join('');
return hashHex;
});
let body = {
room_id: roomName,
slot_id: "m.call#ROOM",
openid_token: openIDToken,
member: {
id: idHashed,
claimed_user_id: client.getUserId(),
claimed_device_id: client.getDeviceId(),
}
};
if (delayId) {
const { features, matrix_rtc_session: matrixRtcSessionConfig } = Config.get();
const delay_timeout_ms = matrixRtcSessionConfig?.delayed_leave_event_delay_ms ?? 10000
body = {
...body,
...{delay_id: delayId, delay_timeout: delay_timeout_ms, delay_cs_api_url: client.baseUrl}
}
};
try {
const res = await fetch(livekitServiceURL + "/sfu/get", {
const res = await fetch(livekitServiceURL + "/get_token", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
room: roomName,
openid_token: openIDToken,
device_id: client.getDeviceId(),
}),
body: JSON.stringify(body),
});
if (!res.ok) {
throw new Error("SFU Config fetch failed with status code " + res.status);

View File

@@ -99,6 +99,7 @@ import { createHomeserverConnected$ } from "./localMember/HomeserverConnected.ts
import {
createLocalMembership$,
enterRTCSession,
MatrixState,
RTCBackendState,
} from "./localMember/LocalMembership.ts";
import { createLocalTransport$ } from "./localMember/LocalTransport.ts";

View File

@@ -53,6 +53,9 @@ import { MatrixRTCMode } from "../../../settings/settings.ts";
import { Config } from "../../../config/Config.ts";
import { type Connection } from "../remoteMembers/Connection.ts";
import { getSFUConfigWithOpenID } from "../../../livekit/openIDSFU.ts"
import { type MatrixClient } from "matrix-js-sdk";
export enum RTCBackendState {
Error = "error",
/** Not even a transport is available to the LocalMembership */
@@ -580,6 +583,28 @@ export const createLocalMembership$ = ({
};
}
matrixState$.pipe(scope.bind()).subscribe((matrixState) => {
if (matrixState.state !== MatrixState.Connected) return;
// UNSAVE. Arbitrary change some types to read properties we should not have access to (private)
// TODO this is bad and we need a proper solution to expose the delayId (or let the js-sdk take care of delegating the delayed event)
const sessionWithAccessToPrivateMembers = matrixRTCSession as unknown as {
membershipManager: { state: { delayId: string } },
room: { client: MatrixClient, roomId: string } ;
};
const delayId = sessionWithAccessToPrivateMembers.membershipManager.state.delayId;
const roomId = sessionWithAccessToPrivateMembers.room.roomId
const mxClient = sessionWithAccessToPrivateMembers.room.client
const serviceUrl = localConnection$?.value?.transport?.livekit_service_url
//logger.debug("delayId is available", serviceUrl, delayId, roomId, mxClient);
if (serviceUrl) {
void getJWTTokenWithDelaydEventDelegation(serviceUrl, delayId, mxClient, roomId, logger);
}
});
return {
startTracks,
requestConnect,
@@ -673,3 +698,23 @@ export async function enterRTCSession(
await widget.api.transport.send(ElementWidgetActions.JoinCall, {});
}
}
async function getJWTTokenWithDelaydEventDelegation(
serviceUrl: string,
delayId: string,
client: MatrixClient,
matrixRoomId: string,
logger: Logger,
): Promise<void> {
try {
const sfuConfig = await getSFUConfigWithOpenID(
client,
serviceUrl,
matrixRoomId,
delayId,
);
logger.debug("SFU Config retrieved successfully with delayId:", delayId, sfuConfig);
} catch (error) {
logger.error("Failed to get SFU config with OpenID:", error);
}
}