Stop calling rtc/transport in widget mode

This commit is contained in:
Valere
2026-03-31 11:38:21 +02:00
parent 40fdef89eb
commit 6b7467ce6d
3 changed files with 85 additions and 8 deletions

View File

@@ -137,6 +137,7 @@ export async function getSFUConfigWithOpenID(
);
logger?.info(`Got JWT from call's active focus URL.`);
} catch (e) {
logger?.debug(`Failed fetching jwt with matrix 2.0 endpoint:`, e);
if (e instanceof NotSupportedError) {
logger?.warn(
`Failed fetching jwt with matrix 2.0 endpoint (retry with legacy) Not supported`,

View File

@@ -21,6 +21,7 @@ import {
} from "matrix-js-sdk/lib/matrixrtc";
import { BehaviorSubject, lastValueFrom } from "rxjs";
import fetchMock from "fetch-mock";
import { AutoDiscovery } from "matrix-js-sdk/lib/autodiscovery";
import {
mockConfig,
@@ -60,6 +61,7 @@ describe("LocalTransport", () => {
client: {
// eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: async () => Promise.resolve([]),
getAccessToken: vi.fn().mockReturnValue("access_token"),
getDomain: () => "",
baseUrl: "example.org",
// These won't be called in this error path but satisfy the type
@@ -102,6 +104,7 @@ describe("LocalTransport", () => {
baseUrl: "https://lk.example.org",
// Use empty domain to skip .well-known and use config directly
getDomain: () => "",
getAccessToken: vi.fn().mockReturnValue("access_token"),
// eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: async () => Promise.resolve([]),
getOpenIdToken: vi.fn(),
@@ -149,6 +152,7 @@ describe("LocalTransport", () => {
getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(),
baseUrl: "https://lk.example.org",
getAccessToken: vi.fn().mockReturnValue("access_token"),
},
ownMembershipIdentity: ownMemberMock,
forceJwtEndpoint: JwtEndpointVersion.Legacy,
@@ -217,6 +221,7 @@ describe("LocalTransport", () => {
getDomain: () => "",
// eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: async () => Promise.resolve([]),
getAccessToken: vi.fn().mockReturnValue("access_token"),
getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(),
baseUrl: "https://lk.example.org",
@@ -273,6 +278,7 @@ describe("LocalTransport", () => {
// eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: async () =>
Promise.resolve([aliceTransport]),
getAccessToken: vi.fn().mockReturnValue("access_token"),
getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(),
baseUrl: "https://lk.example.org",
@@ -323,6 +329,7 @@ describe("LocalTransport", () => {
getDomain: vi.fn().mockReturnValue(""),
// eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: vi.fn().mockResolvedValue([]),
getAccessToken: vi.fn().mockReturnValue("access_token"),
getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(),
},
@@ -410,6 +417,49 @@ describe("LocalTransport", () => {
});
});
it("Should not call _unstable_getRTCTransports in widget mode but use well-known", async () => {
mockConfig({
livekit: { livekit_service_url: "https://do-not-use.lk.example.org" },
});
localTransportOpts.client.getDomain.mockReturnValue("example.org");
vi.spyOn(AutoDiscovery, "getRawClientConfig").mockImplementation(
async (domain) => {
if (domain === "example.org") {
return Promise.resolve({
"org.matrix.msc4143.rtc_foci": [
{
type: "livekit",
livekit_service_url: "https://use-me.jwt.call.example.org",
},
],
});
}
return Promise.resolve({});
},
);
localTransportOpts.client.getAccessToken.mockReturnValue(null);
const { advertised$, active$ } =
createLocalTransport$(localTransportOpts);
openIdResolver.resolve?.(openIdResponse);
expect(advertised$.value).toBe(null);
expect(active$.value).toBe(null);
await flushPromises();
expect(
localTransportOpts.client._unstable_getRTCTransports,
).not.toHaveBeenCalled();
const expectedTransport = {
type: "livekit",
livekit_service_url: "https://use-me.jwt.call.example.org",
};
expect(advertised$.value).toStrictEqual(expectedTransport);
});
it("fails fast if the openID request fails for backend config", async () => {
localTransportOpts.client._unstable_getRTCTransports.mockResolvedValue([
{ type: "livekit", livekit_service_url: "https://lk.example.org" },
@@ -481,6 +531,7 @@ describe("LocalTransport", () => {
baseUrl: "https://example.org",
// eslint-disable-next-line @typescript-eslint/naming-convention
_unstable_getRTCTransports: async () => Promise.resolve([]),
getAccessToken: vi.fn().mockReturnValue("access_token"),
// These won't be called in this error path but satisfy the type
getOpenIdToken: vi.fn(),
getDeviceId: vi.fn(),

View File

@@ -56,7 +56,7 @@ interface Props {
memberships$: Behavior<Epoch<CallMembership[]>>;
client: Pick<
MatrixClient,
"getDomain" | "baseUrl" | "_unstable_getRTCTransports"
"getDomain" | "baseUrl" | "_unstable_getRTCTransports" | "getAccessToken"
> &
OpenIDClientParts;
// Used by the jwt service to create the livekit room and compute the livekit alias.
@@ -314,7 +314,7 @@ const FOCI_WK_KEY = "org.matrix.msc4143.rtc_foci";
async function makeTransport(
client: Pick<
MatrixClient,
"getDomain" | "baseUrl" | "_unstable_getRTCTransports"
"getDomain" | "baseUrl" | "_unstable_getRTCTransports" | "getAccessToken"
> &
OpenIDClientParts,
membership: CallMembershipIdentityParts,
@@ -371,11 +371,18 @@ async function makeTransport(
for (const potentialTransport of transports) {
if (isLivekitTransportConfig(potentialTransport)) {
try {
logger.info(
`makeTransport: check transport authentication for "${potentialTransport.livekit_service_url}"`,
);
// This will call the jwt/sfu/get endpoint to pre create the livekit room.
return await doOpenIdAndJWTFromUrl(
potentialTransport.livekit_service_url,
);
} catch (ex) {
logger.debug(
`makeTransport: Could not use SFU service "${potentialTransport.livekit_service_url}" as SFU`,
ex,
);
// Explictly throw these
if (ex instanceof FailToGetOpenIdToken) {
throw ex;
@@ -383,24 +390,34 @@ async function makeTransport(
if (ex instanceof NoMatrix2AuthorizationService) {
throw ex;
}
logger.debug(
`Could not use SFU service "${potentialTransport.livekit_service_url}" as SFU`,
ex,
);
}
} else {
logger.info(
`makeTransport: "${potentialTransport.livekit_service_url}" is not a valid livekit transport as SFU`,
);
}
}
return null;
}
// MSC4143: Attempt to fetch transports from backend.
if ("_unstable_getRTCTransports" in client) {
// TODO: Workaround for an issue in the js-sdk RoomWidgetClient that
// is not yet implementing _unstable_getRTCTransports properly (via widget API new action).
// For now we just skip this call if we are in a widget.
// In widget mode the client is a `RoomWidgetClient` which has no access token (it is using the widget API).
// Could be removed once the js-sdk is fixed (https://github.com/matrix-org/matrix-js-sdk/issues/5245)
const isSPA = !!client.getAccessToken();
if (isSPA && "_unstable_getRTCTransports" in client) {
logger.info(
"makeTransport: First try to use getRTCTransports end point ...",
);
try {
// TODO This should also check for server support?
const transportList = await client._unstable_getRTCTransports();
const selectedTransport = await getFirstUsableTransport(transportList);
if (selectedTransport) {
logger.info(
"Using backend-configured (client.getRTCTransports) SFU",
"makeTransport: ...Using backend-configured (client.getRTCTransports) SFU",
selectedTransport,
);
return selectedTransport;
@@ -424,6 +441,10 @@ async function makeTransport(
}
}
logger.info(
`makeTransport: Trying to get transports from .well-known/matrix/client on domain ${client.getDomain()} ...`,
);
// Legacy MSC4143 (to be removed) WELL_KNOWN: Prioritize the .well-known/matrix/client, if available.
const domain = client.getDomain();
if (domain) {
@@ -441,6 +462,10 @@ async function makeTransport(
}
}
logger.info(
`makeTransport: No valid transport found via backend or .well-known, falling back to config if available.`,
);
// CONFIG: Least prioritized; Load from config file
const urlFromConf = Config.get().livekit?.livekit_service_url;
if (urlFromConf) {