Make use of the new jwt service endpoint (with delayed event delegation)

This also does all the compatibility work. When to use which endpoint to
authenticate agains a jwt service.
This commit is contained in:
Timo K
2025-12-17 09:53:49 +01:00
parent 9bd51fdfc4
commit ab7e3486b3
17 changed files with 294 additions and 74 deletions

View File

@@ -24,6 +24,7 @@ import {
mockLivekitRoom,
mockMuteStates,
withTestScheduler,
ownMemberMock,
} from "../../../utils/test";
import {
TransportState,
@@ -108,6 +109,7 @@ describe("LocalMembership", () => {
enterRTCSession(
mockedSession,
ownMemberMock,
{
livekit_alias: "roomId",
livekit_service_url: "http://my-well-known-service-url.com",
@@ -166,6 +168,7 @@ describe("LocalMembership", () => {
enterRTCSession(
mockedSession,
ownMemberMock,
{
livekit_alias: "roomId",
livekit_service_url: "http://my-well-known-service-url.com",

View File

@@ -34,6 +34,7 @@ import {
} from "rxjs";
import { type Logger } from "matrix-js-sdk/lib/logger";
import { deepCompare } from "matrix-js-sdk/lib/utils";
import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager";
import { constant, type Behavior } from "../../Behavior.ts";
import { type IConnectionManager } from "../remoteMembers/ConnectionManager.ts";
@@ -657,6 +658,7 @@ interface EnterRTCSessionOptions {
// Exported for unit testing
export function enterRTCSession(
rtcSession: MatrixRTCSession,
ownMembershipIdentity: CallMembershipIdentityParts,
transport: LivekitTransport,
{ encryptMedia, matrixRTCMode }: EnterRTCSessionOptions,
): void {
@@ -674,7 +676,8 @@ export function enterRTCSession(
const multiSFU = matrixRTCMode !== MatrixRTCMode.Legacy;
// Multi-sfu does not need a preferred foci list. just the focus that is actually used.
// TODO where/how do we track errors originating from the ongoing rtcSession?
rtcSession.joinRoomSession(
rtcSession.joinRTCSession(
ownMembershipIdentity,
multiSFU ? [] : [transport],
multiSFU ? transport : undefined,
{

View File

@@ -9,7 +9,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { type CallMembership } from "matrix-js-sdk/lib/matrixrtc";
import { BehaviorSubject } from "rxjs";
import { mockConfig, flushPromises } from "../../../utils/test";
import { mockConfig, flushPromises, ownMemberMock } from "../../../utils/test";
import { createLocalTransport$ } from "./LocalTransport";
import { constant } from "../../Behavior";
import { Epoch, ObservableScope } from "../../ObservableScope";
@@ -32,10 +32,14 @@ describe("LocalTransport", () => {
memberships$: constant(new Epoch<CallMembership[]>([])),
client: {
getDomain: () => "",
baseUrl: "example.org",
// These won't be called in this error path but satisfy the type
getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(),
},
ownMembershipIdentity: ownMemberMock,
useMatrix2$: constant(false),
delayId$: constant("delay_id_mock"),
});
await flushPromises();
@@ -65,11 +69,15 @@ describe("LocalTransport", () => {
useOldestMember$: constant(false),
memberships$: constant(new Epoch<CallMembership[]>([])),
client: {
baseUrl: "https://lk.example.org",
// Use empty domain to skip .well-known and use config directly
getDomain: () => "",
getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(),
},
ownMembershipIdentity: ownMemberMock,
useMatrix2$: constant(false),
delayId$: constant("delay_id_mock"),
});
localTransport$.subscribe(
(o) => observations.push(o),
@@ -105,7 +113,11 @@ describe("LocalTransport", () => {
getDomain: () => "",
getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(),
baseUrl: "https://lk.example.org",
},
ownMembershipIdentity: ownMemberMock,
useMatrix2$: constant(false),
delayId$: constant("delay_id_mock"),
});
openIdResolver.resolve?.({ url: "https://lk.example.org", jwt: "jwt" });
@@ -140,7 +152,11 @@ describe("LocalTransport", () => {
getDomain: () => "",
getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(),
baseUrl: "https://lk.example.org",
},
ownMembershipIdentity: ownMemberMock,
useMatrix2$: constant(false),
delayId$: constant("delay_id_mock"),
});
openIdResolver.resolve?.({ url: "https://lk.example.org", jwt: "jwt" });

View File

@@ -23,6 +23,7 @@ import {
} from "rxjs";
import { logger as rootLogger } from "matrix-js-sdk/lib/logger";
import { AutoDiscovery } from "matrix-js-sdk/lib/autodiscovery";
import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager";
import { type Behavior } from "../../Behavior.ts";
import { type Epoch, type ObservableScope } from "../../ObservableScope.ts";
@@ -34,6 +35,7 @@ import {
} from "../../../livekit/openIDSFU.ts";
import { areLivekitTransportsEqual } from "../remoteMembers/MatrixLivekitMembers.ts";
import { customLivekitUrl } from "../../../settings/settings.ts";
import { type LivekitTransportWithVersion } from "../remoteMembers/ConnectionManager.ts";
const logger = rootLogger.getChild("[LocalTransport]");
@@ -44,10 +46,13 @@ const logger = rootLogger.getChild("[LocalTransport]");
*/
interface Props {
scope: ObservableScope;
ownMembershipIdentity: CallMembershipIdentityParts;
memberships$: Behavior<Epoch<CallMembership[]>>;
client: Pick<MatrixClient, "getDomain"> & OpenIDClientParts;
client: Pick<MatrixClient, "getDomain" | "baseUrl"> & OpenIDClientParts;
roomId: string;
useOldestMember$: Behavior<boolean>;
useMatrix2$: Behavior<boolean>;
delayId$: Behavior<string | null>;
}
/**
@@ -62,20 +67,26 @@ interface Props {
export const createLocalTransport$ = ({
scope,
memberships$,
ownMembershipIdentity,
client,
roomId,
useOldestMember$,
}: Props): Behavior<LivekitTransport | null> => {
useMatrix2$,
delayId$,
}: Props): Behavior<LivekitTransportWithVersion | null> => {
/**
* The transport over which we should be actively publishing our media.
* undefined when not joined.
*/
const oldestMemberTransport$ = scope.behavior(
memberships$.pipe(
map(
(memberships) =>
memberships.value[0]?.getTransport(memberships.value[0]) ?? null,
),
map((memberships) => {
const oldestMember = memberships.value[0];
const t = oldestMember?.getTransport(memberships.value[0]);
if (!t) return null;
// Here we will use the matrix2 information from the oldest member transport.
return { ...t, useMatrix2: oldestMember.kind === "rtc" };
}),
first((t) => t != null && isLivekitTransport(t)),
),
null,
@@ -87,12 +98,24 @@ export const createLocalTransport$ = ({
*
* @throws MatrixRTCTransportMissingError | FailToGetOpenIdToken
*/
const preferredTransport$: Behavior<LivekitTransport | null> = scope.behavior(
customLivekitUrl.value$.pipe(
switchMap((customUrl) => from(makeTransport(client, roomId, customUrl))),
),
null,
);
const preferredTransport$: Behavior<LivekitTransportWithVersion | null> =
scope.behavior(
combineLatest([customLivekitUrl.value$, useMatrix2$, delayId$]).pipe(
switchMap(([customUrl, useMatrix2, delayId]) =>
from(
makeTransport(
client,
ownMembershipIdentity,
roomId,
customUrl,
useMatrix2,
delayId ?? undefined,
),
),
),
),
null,
);
/**
* The chosen transport we should advertise in our MatrixRTC membership.
@@ -123,10 +146,13 @@ const FOCI_WK_KEY = "org.matrix.msc4143.rtc_foci";
* @throws MatrixRTCTransportMissingError | FailToGetOpenIdToken
*/
async function makeTransport(
client: Pick<MatrixClient, "getDomain"> & OpenIDClientParts,
client: Pick<MatrixClient, "getDomain" | "baseUrl"> & OpenIDClientParts,
membership: CallMembershipIdentityParts,
roomId: string,
urlFromDevSettings: string | null,
): Promise<LivekitTransport> {
matrix2jwt = false,
delayId?: string,
): Promise<LivekitTransportWithVersion> {
let transport: LivekitTransport | undefined;
logger.trace("Searching for a preferred transport");
//TODO refactor this to use the jwt service returned alias.
@@ -176,13 +202,18 @@ async function makeTransport(
transport = transportFromConf;
}
if (!transport) throw new MatrixRTCTransportMissingError(domain ?? ""); // this will call the jwt/sfu/get endpoint to pre create the livekit room.
if (!transport) throw new MatrixRTCTransportMissingError(domain ?? "");
// this will call the jwt/sfu/get endpoint to pre create the livekit room.
await getSFUConfigWithOpenID(
client,
membership,
transport.livekit_service_url,
transport.livekit_alias,
matrix2jwt,
client.baseUrl,
delayId,
);
return transport;
return { ...transport, useMatrix2: matrix2jwt };
}