mirror of
https://github.com/vector-im/element-call.git
synced 2026-10-16 23:45:50 +00:00
The OpenID flow moves to src/livekit/auth/openID.ts with the SFU config types in a sibling file, takes an options object, and replaces the MatrixRTC mode with the role we take on the transport plus the session's slot ID instead of a hard-coded m.call#ROOM. This changes behaviour: publishers on a livekit_service_url transport always use the legacy /sfu/get endpoint, so that subscribers can predict their user:device participant identity, and subscribers always use the default /get_token endpoint. The MatrixRTC mode no longer influences which endpoint is used.
147 lines
4.5 KiB
TypeScript
147 lines
4.5 KiB
TypeScript
/*
|
|
Copyright 2025 Element Creations Ltd.
|
|
|
|
SPDX-License-IdFentifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
Please see LICENSE in the repository root for full details.
|
|
*/
|
|
|
|
import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
|
|
import { type MatrixClient } from "matrix-js-sdk";
|
|
import { logger as rootLogger, type Logger } from "matrix-js-sdk/lib/logger";
|
|
import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager";
|
|
|
|
import { Config } from "../../../config/Config.ts";
|
|
import {
|
|
FailToGetOpenIdToken,
|
|
MatrixRTCTransportMissingError,
|
|
NoMatrix2AuthorizationService,
|
|
} from "../../../utils/errors.ts";
|
|
import {
|
|
getSFUConfig,
|
|
type SFUConfig,
|
|
type ClientOpenIDParts,
|
|
} from "../../../livekit/auth";
|
|
import { customLivekitUrl } from "../../../settings/settings.ts";
|
|
import { RtcTransportAutoDiscovery } from "./RtcTransportAutoDiscovery.ts";
|
|
|
|
interface Props {
|
|
ownMembershipIdentity: CallMembershipIdentityParts;
|
|
client: Pick<MatrixClient, "getDomain" | "_unstable_getRTCTransports"> &
|
|
ClientOpenIDParts;
|
|
// Used by the jwt service to create the livekit room and compute the livekit alias.
|
|
roomId: string;
|
|
slotId: string;
|
|
}
|
|
|
|
export interface LocalTransport {
|
|
transport: LivekitTransport;
|
|
sfuConfig: SFUConfig;
|
|
}
|
|
|
|
export function isLocalTransport(
|
|
obj: LivekitTransport | LocalTransport,
|
|
): obj is LocalTransport {
|
|
return "transport" in obj && "sfuConfig" in obj;
|
|
}
|
|
|
|
/**
|
|
* Connects to the JWT service and determines the transport that the local member should use.
|
|
*
|
|
* @prop useOldJwtEndpoint Whether to set forceOldJwtEndpoint on the returned transport and to use the old JWT endpoint.
|
|
* This is used when the connection manager needs to know if it has to use the legacy endpoint which implies a string concatenated rtcBackendIdentity.
|
|
* (which is expected for non sticky event based rtc member events)
|
|
* @returns The transport to advertise in our MatrixRTC membership and publish media on.
|
|
* @throws MatrixRTCTransportMissingError | FailToGetOpenIdToken
|
|
*/
|
|
export async function getLocalTransport({
|
|
ownMembershipIdentity,
|
|
client,
|
|
roomId,
|
|
slotId,
|
|
}: Props): Promise<LocalTransport> {
|
|
const logger = rootLogger.getChild("[LocalTransport]");
|
|
const discovery = new RtcTransportAutoDiscovery({
|
|
client: client,
|
|
resolvedConfig: Config.get(),
|
|
logger: logger,
|
|
});
|
|
const customUrl = customLivekitUrl.value$.value;
|
|
|
|
// Respect the user's custom URL, if set
|
|
const transport: LivekitTransport | null = customUrl
|
|
? { type: "livekit", livekit_service_url: customUrl }
|
|
: await discovery.discoverPreferredTransport();
|
|
|
|
if (transport === null)
|
|
throw new MatrixRTCTransportMissingError(client.getDomain() ?? "");
|
|
|
|
try {
|
|
return await doOpenIdAndJWTFromUrl(
|
|
transport,
|
|
ownMembershipIdentity,
|
|
roomId,
|
|
slotId,
|
|
client,
|
|
logger,
|
|
);
|
|
} catch (e) {
|
|
logger.error(
|
|
`Failed to authenticate to transport ${transport.livekit_service_url}`,
|
|
e,
|
|
);
|
|
throw mapAuthErrorToUserFriendlyError(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Utility to ensure the user can authenticate with the SFU.
|
|
* We will call `getSFUConfig` once per transport here as it's our
|
|
* only mechanism of validation. This means we will also ask the
|
|
* homeserver for a OpenID token a few times. Since OpenID tokens are single
|
|
* use we don't want to risk any issues by re-using a token.
|
|
*
|
|
* @param transport The transport to authenticate with.
|
|
* @param membership The identity of the local member.
|
|
* @param roomId The room ID to use for the JWT.
|
|
* @param slotId The slot ID to use for the JWT.
|
|
* @param client The client to use for the OpenID token.
|
|
*
|
|
* @throws FailToGetOpenIdToken, NoMatrix2AuthorizationService
|
|
*/
|
|
async function doOpenIdAndJWTFromUrl(
|
|
transport: LivekitTransport,
|
|
membership: CallMembershipIdentityParts,
|
|
roomId: string,
|
|
slotId: string,
|
|
client: Pick<MatrixClient, "_unstable_getRTCTransports"> & ClientOpenIDParts,
|
|
logger: Logger,
|
|
): Promise<LocalTransport> {
|
|
const sfuConfig = await getSFUConfig({
|
|
client,
|
|
membership,
|
|
serviceUrl: transport.livekit_service_url,
|
|
roomId,
|
|
slotId,
|
|
role: "publisher",
|
|
logger,
|
|
});
|
|
return {
|
|
transport,
|
|
sfuConfig,
|
|
};
|
|
}
|
|
|
|
function mapAuthErrorToUserFriendlyError(e: unknown): Error {
|
|
if (
|
|
e instanceof FailToGetOpenIdToken ||
|
|
e instanceof NoMatrix2AuthorizationService
|
|
) {
|
|
// rethrow as is
|
|
return e;
|
|
}
|
|
// Catch others and rethrow as FailToGetOpenIdToken that has user friendly message.
|
|
return new FailToGetOpenIdToken(
|
|
e instanceof Error ? e : new Error(String(e)),
|
|
);
|
|
}
|