mirror of
https://github.com/vector-im/element-call.git
synced 2026-10-16 23:45:50 +00:00
Fall back to get_token as subscriber on widget API errors
This commit is contained in:
+30
-17
@@ -5,12 +5,21 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type MatrixClient } from "matrix-js-sdk";
|
||||
import { ConnectionError, HTTPError, type MatrixClient } from "matrix-js-sdk";
|
||||
import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager";
|
||||
|
||||
import { extractFullConfigFromToken, type SFUConfig } from "./types.ts";
|
||||
import { doNetworkOperationWithRetry } from "../../utils/matrix.ts";
|
||||
|
||||
/**
|
||||
* In widget mode the host may refuse an action before any request is made
|
||||
* which surfaces as a plain error. Asking again cannot change its answer,
|
||||
* so only errors from the network are worth retrying.
|
||||
*/
|
||||
function isNetworkError(e: unknown): boolean {
|
||||
return e instanceof HTTPError || e instanceof ConnectionError;
|
||||
}
|
||||
|
||||
export type ClientGetTokenParts = Pick<
|
||||
MatrixClient,
|
||||
"_unstable_getLivekitToken"
|
||||
@@ -55,14 +64,16 @@ export async function getSFUConfig({
|
||||
roomId,
|
||||
slotId,
|
||||
}: GetSFUConfigParams): Promise<SFUConfig> {
|
||||
const res = await doNetworkOperationWithRetry(async () =>
|
||||
client._unstable_getLivekitToken({
|
||||
url,
|
||||
server_name: serverName,
|
||||
room_id: roomId,
|
||||
slot_id: slotId,
|
||||
member_id: membership.memberId,
|
||||
}),
|
||||
const res = await doNetworkOperationWithRetry(
|
||||
async () =>
|
||||
client._unstable_getLivekitToken({
|
||||
url,
|
||||
server_name: serverName,
|
||||
room_id: roomId,
|
||||
slot_id: slotId,
|
||||
member_id: membership.memberId,
|
||||
}),
|
||||
isNetworkError,
|
||||
);
|
||||
return extractFullConfigFromToken({ url, jwt: res.jwt });
|
||||
}
|
||||
@@ -112,13 +123,15 @@ export async function delegateDelayedLeave({
|
||||
slotId,
|
||||
delayId,
|
||||
}: DelegateDelayedLeaveParams): Promise<void> {
|
||||
await doNetworkOperationWithRetry(async () =>
|
||||
client._unstable_delegateDelayedLeave({
|
||||
url,
|
||||
room_id: roomId,
|
||||
slot_id: slotId,
|
||||
member_id: membership.memberId,
|
||||
delay_id: delayId,
|
||||
}),
|
||||
await doNetworkOperationWithRetry(
|
||||
async () =>
|
||||
client._unstable_delegateDelayedLeave({
|
||||
url,
|
||||
room_id: roomId,
|
||||
slot_id: slotId,
|
||||
member_id: membership.memberId,
|
||||
delay_id: delayId,
|
||||
}),
|
||||
isNetworkError,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -158,6 +158,34 @@ describe("getSFUConfig", () => {
|
||||
expect(openID.getSFUConfig).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("falls back to OpenID when the widget host refuses the CS API", async () => {
|
||||
// In widget mode a host without MSC4533 rejects the request before it
|
||||
// reaches the homeserver, with a plain error rather than a Matrix error
|
||||
client._unstable_getLivekitToken.mockRejectedValueOnce(
|
||||
new Error(
|
||||
"The rtc_livekit_get_token action is not supported by the client.",
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await getSFUConfig({
|
||||
...defaultParams,
|
||||
transport: urlAndServiceUrlTransport,
|
||||
}),
|
||||
).toEqual(expect.objectContaining({ jwt: openIDJWTToken }));
|
||||
expect(openID.getSFUConfig).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("surfaces the widget host's refusal with url transport", async () => {
|
||||
const error = new Error(
|
||||
"The rtc_livekit_get_token action is not supported by the client.",
|
||||
);
|
||||
client._unstable_getLivekitToken.mockRejectedValueOnce(error);
|
||||
await expect(
|
||||
getSFUConfig({ ...defaultParams, transport: urlTransport }),
|
||||
).rejects.toThrow(error);
|
||||
expect(openID.getSFUConfig).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not fall back to OpenID when publishing", async () => {
|
||||
client._unstable_getLivekitToken.mockRejectedValueOnce(unsupportedError);
|
||||
await expect(
|
||||
|
||||
@@ -8,7 +8,7 @@ Please see LICENSE in the repository root for full details.
|
||||
import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager";
|
||||
import { type Logger } from "matrix-js-sdk/lib/logger";
|
||||
|
||||
import { MatrixError } from "matrix-js-sdk";
|
||||
import { ConnectionError, MatrixError } from "matrix-js-sdk";
|
||||
import { type UnstableLivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
|
||||
import * as openID from "./openID";
|
||||
import * as csApi from "./csApi";
|
||||
@@ -38,6 +38,19 @@ function isEndpointUnsupported(e: unknown): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether an error means the MSC4195 CS API cannot be used from here:
|
||||
* the homeserver does not implement it, or the request never reached the
|
||||
* homeserver because in widget mode the host does not expose the endpoint
|
||||
* (MSC4533), which the widget API reports as a plain error rather than a
|
||||
* Matrix error. A connection error is neither, as the endpoint may well exist.
|
||||
* @param e The error to check.
|
||||
*/
|
||||
function isCsApiUnavailable(e: unknown): boolean {
|
||||
if (isEndpointUnsupported(e)) return true;
|
||||
return !(e instanceof MatrixError) && !(e instanceof ConnectionError);
|
||||
}
|
||||
|
||||
export interface GetSFUConfigParams {
|
||||
/**
|
||||
* The Matrix client.
|
||||
@@ -113,23 +126,25 @@ export async function getSFUConfig({
|
||||
slotId,
|
||||
});
|
||||
} catch (e) {
|
||||
if (!isEndpointUnsupported(e)) throw e;
|
||||
// Publishers should never fall back. If a homeserver advertises a `url`
|
||||
// but doesn't support MSC4195 itself, that's a misconfiguration which
|
||||
// the admin should know about so they can prepare for the future
|
||||
// deprecation of `livekit_service_url`.
|
||||
const mayFallBack =
|
||||
role === "subscriber" && "livekit_service_url" in transport;
|
||||
if (!mayFallBack) {
|
||||
if (mayFallBack && isCsApiUnavailable(e)) {
|
||||
logger.warn(
|
||||
`Cannot use the MSC4195 get_token endpoint (${e}), falling back to the OpenID flow with service ${transport.livekit_service_url}`,
|
||||
);
|
||||
return await viaOpenID(transport.livekit_service_url);
|
||||
}
|
||||
if (isEndpointUnsupported(e)) {
|
||||
logger.error(
|
||||
`Homeserver does not support the MSC4195 get_token endpoint, cannot get token for transport ${flow.url}`,
|
||||
);
|
||||
throw new NoMatrix2AuthorizationService(e as Error);
|
||||
}
|
||||
logger.warn(
|
||||
`Homeserver does not support the MSC4195 get_token endpoint, falling back to the OpenID flow with service ${transport.livekit_service_url}`,
|
||||
);
|
||||
return await viaOpenID(transport.livekit_service_url);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user