From 8996aa772cfc77dc1b93839f4b67d399c43928be Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 28 Jun 2023 16:35:56 +0100 Subject: [PATCH 01/13] Initial support for getting SFO config using OIDC * Change `jwt_service_url` to `livekit_service_url` * Make it a POST so we can send the openID token sensibly * Get an OIDC token & pass it with the request * Read the SFU URL from there too and convert the auth server accordingly, althugh with no actual OIDC support yet, it just issues tokens blindly just as before and ignores the openid token completely. We'll need to update configs & the JWT service before merging this. --- backend/auth/server.go | 86 ++++++++++++++++++++++++------------- src/config/ConfigOptions.ts | 6 +-- src/livekit/useLiveKit.ts | 28 +++--------- src/room/GroupCallView.tsx | 7 +-- src/room/InCallView.tsx | 17 +++----- 5 files changed, 74 insertions(+), 70 deletions(-) diff --git a/backend/auth/server.go b/backend/auth/server.go index b3721e2..f099784 100644 --- a/backend/auth/server.go +++ b/backend/auth/server.go @@ -15,41 +15,74 @@ type Handler struct { key, secret string } +type OpenIDTokenType struct { +} + +type SFURequest struct { + Room string `json:"room"` + OpenIDToken OpenIDTokenType `json:"openid_token"` + DeviceID string `json:"device_id"` + RemoveMeUserID string `json:"remove_me_user_id"` // we'll get this from OIDC +} + +type SFUResponse struct { + URL string `json:"url"` + JWT string `json:"jwt"` +} + func (h *Handler) handle(w http.ResponseWriter, r *http.Request) { log.Printf("Request from %s", r.RemoteAddr) // Set the CORS headers w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") - w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") + w.Header().Set("Access-Control-Allow-Methods", "POST") + w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token") // Handle preflight request (CORS) if r.Method == "OPTIONS" { w.WriteHeader(http.StatusOK) return + } else if r.Method == "POST" { + var body SFURequest + err := json.NewDecoder(r.Body).Decode(&body) + if err != nil { + log.Printf("Error decoding JSON: %v", err) + w.WriteHeader(http.StatusBadRequest) + return + } + + if body.Room == "" { + log.Printf("Request missing room") + w.WriteHeader(http.StatusBadRequest) + return + } + + token, err := getJoinToken(h.key, h.secret, body.Room, body.RemoveMeUserID+":"+body.DeviceID) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + res := SFUResponse{URL: "http://localhost:7880/", JWT: token} + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(res) + } else { + w.WriteHeader(http.StatusMethodNotAllowed) } - roomName := r.URL.Query().Get("roomName") - name := r.URL.Query().Get("name") - identity := r.URL.Query().Get("identity") + /* + roomName := r.URL.Query().Get("roomName") + name := r.URL.Query().Get("name") + identity := r.URL.Query().Get("identity") - log.Printf("roomName: %s, name: %s, identity: %s", roomName, name, identity) + log.Printf("roomName: %s, name: %s, identity: %s", roomName, name, identity) - if roomName == "" || name == "" || identity == "" { - w.WriteHeader(http.StatusBadRequest) - return - } - - token, err := getJoinToken(h.key, h.secret, roomName, identity, name) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - - res := Response{token} - - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(res) + if roomName == "" || name == "" || identity == "" { + w.WriteHeader(http.StatusBadRequest) + return + } + */ } func main() { @@ -68,15 +101,11 @@ func main() { secret: secret, } - http.HandleFunc("/token", handler.handle) + http.HandleFunc("/sfu/get", handler.handle) log.Fatal(http.ListenAndServe(":8080", nil)) } -type Response struct { - Token string `json:"accessToken"` -} - -func getJoinToken(apiKey, apiSecret, room, identity, name string) (string, error) { +func getJoinToken(apiKey, apiSecret, room, identity string) (string, error) { at := auth.NewAccessToken(apiKey, apiSecret) canPublish := true @@ -91,8 +120,7 @@ func getJoinToken(apiKey, apiSecret, room, identity, name string) (string, error at.AddGrant(grant). SetIdentity(identity). - SetValidFor(time.Hour). - SetName(name) + SetValidFor(time.Hour) return at.ToJWT() } diff --git a/src/config/ConfigOptions.ts b/src/config/ConfigOptions.ts index 1cee00f..f878ec5 100644 --- a/src/config/ConfigOptions.ts +++ b/src/config/ConfigOptions.ts @@ -55,10 +55,8 @@ export interface ConfigOptions { // Describes the LiveKit configuration to be used. livekit?: { - // The LiveKit server URL to connect to. - server_url: string; - // The link to the service that generates JWT tokens to join LiveKit rooms. - jwt_service_url: string; + // The link to the service that returns a livekit url and token to use it + livekit_service_url: string; }; /** diff --git a/src/livekit/useLiveKit.ts b/src/livekit/useLiveKit.ts index b160550..3fd763e 100644 --- a/src/livekit/useLiveKit.ts +++ b/src/livekit/useLiveKit.ts @@ -1,8 +1,9 @@ import { Room, RoomOptions } from "livekit-client"; -import { useLiveKitRoom, useToken } from "@livekit/components-react"; +import { useLiveKitRoom } from "@livekit/components-react"; import React from "react"; import { defaultLiveKitOptions } from "./options"; +import { SFUConfig } from "./openIDSFU"; export type UserChoices = { audio?: DeviceChoices; @@ -14,29 +15,10 @@ export type DeviceChoices = { enabled: boolean; }; -export type LiveKitConfig = { - sfuUrl: string; - jwtUrl: string; - roomName: string; - userDisplayName: string; - userIdentity: string; -}; - export function useLiveKit( userChoices: UserChoices, - config: LiveKitConfig + sfuConfig: SFUConfig ): Room | undefined { - const tokenOptions = React.useMemo( - () => ({ - userInfo: { - name: config.userDisplayName, - identity: config.userIdentity, - }, - }), - [config.userDisplayName, config.userIdentity] - ); - const token = useToken(config.jwtUrl, config.roomName, tokenOptions); - const roomOptions = React.useMemo((): RoomOptions => { const options = defaultLiveKitOptions; options.videoCaptureDefaults = { @@ -51,8 +33,8 @@ export function useLiveKit( }, [userChoices.video, userChoices.audio]); const { room } = useLiveKitRoom({ - token, - serverUrl: config.sfuUrl, + token: sfuConfig.jwt, + serverUrl: sfuConfig.url, audio: userChoices.audio?.enabled ?? false, video: userChoices.video?.enabled ?? false, options: roomOptions, diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 471bfa4..2a81f44 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -28,7 +28,6 @@ import { useGroupCall } from "./useGroupCall"; import { ErrorView, FullScreenView } from "../FullScreenView"; import { LobbyView } from "./LobbyView"; import { MatrixInfo } from "./VideoPreview"; -import { ActiveCall } from "./InCallView"; import { CallEndedView } from "./CallEndedView"; import { useSentryGroupCallHandler } from "./useSentryGroupCallHandler"; import { PosthogAnalytics } from "../analytics/PosthogAnalytics"; @@ -36,6 +35,7 @@ import { useProfile } from "../profile/useProfile"; import { UserChoices } from "../livekit/useLiveKit"; import { findDeviceByName } from "../media-utils"; import { useRoomAvatar } from "./useRoomAvatar"; +import { OpenIDLoader } from "../livekit/OpenIDLoader"; declare global { interface Window { @@ -225,9 +225,10 @@ export function GroupCallView({ return ; } else if (state === GroupCallState.Entered && userChoices) { return ( - { +export interface ActiveCallProps extends Omit { userChoices: UserChoices; + sfuConfig: SFUConfig; } export function ActiveCall(props: ActiveCallProps) { - const livekitRoom = useLiveKit(props.userChoices, { - sfuUrl: Config.get().livekit!.server_url, - jwtUrl: `${Config.get().livekit!.jwt_service_url}/token`, - roomName: props.matrixInfo.roomName, - userDisplayName: props.matrixInfo.displayName, - userIdentity: `${props.client.getUserId()}:${props.client.getDeviceId()}`, - }); + const livekitRoom = useLiveKit(props.userChoices, props.sfuConfig); return livekitRoom && ; } -interface Props { +export interface InCallViewProps { client: MatrixClient; groupCall: GroupCall; livekitRoom: Room; @@ -125,7 +120,7 @@ export function InCallView({ hideHeader, matrixInfo, otelGroupCallMembership, -}: Props) { +}: InCallViewProps) { const { t } = useTranslation(); usePreventScroll(); From 008bb4f41d80a8cd46febec0795b8c8dfc37ceb0 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 28 Jun 2023 16:42:51 +0100 Subject: [PATCH 02/13] Also the other files --- src/livekit/OpenIDLoader.tsx | 55 ++++++++++++++++++++++++++++++++++ src/livekit/openIDSFU.ts | 58 ++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 src/livekit/OpenIDLoader.tsx create mode 100644 src/livekit/openIDSFU.ts diff --git a/src/livekit/OpenIDLoader.tsx b/src/livekit/OpenIDLoader.tsx new file mode 100644 index 0000000..9e22003 --- /dev/null +++ b/src/livekit/OpenIDLoader.tsx @@ -0,0 +1,55 @@ +/* +Copyright 2023 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { MatrixClient } from "matrix-js-sdk"; +import React, { useEffect, useState } from "react"; +import { logger } from "matrix-js-sdk/src/logger"; + +import { SFUConfig, getSFUConfigWithOpenID } from "./openIDSFU"; +import { ErrorView, LoadingView } from "../FullScreenView"; +import { ActiveCall, InCallViewProps } from "../room/InCallView"; +import { UserChoices } from "./useLiveKit"; + +interface Props extends Omit { + client: MatrixClient; + roomName: string; + userChoices: UserChoices; +} + +export function OpenIDLoader({ client, roomName, ...rest }: Props) { + const [sfuConfig, setSFUConfig] = useState(); + const [error, setError] = useState(); + + useEffect(() => { + (async () => { + try { + const result = await getSFUConfigWithOpenID(client, roomName); + setSFUConfig(result); + } catch (e) { + logger.error("Failed to fetch SFU config: ", e); + setError(new Error("Failed to fetch SFU config")); + } + })(); + }, [client, roomName]); + + if (error) { + return ; + } else if (sfuConfig) { + return ; + } else { + return ; + } +} diff --git a/src/livekit/openIDSFU.ts b/src/livekit/openIDSFU.ts new file mode 100644 index 0000000..422632a --- /dev/null +++ b/src/livekit/openIDSFU.ts @@ -0,0 +1,58 @@ +/* +Copyright 2023 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { MatrixClient } from "matrix-js-sdk"; +import { logger } from "matrix-js-sdk/src/logger"; + +import { Config } from "../config/Config"; + +export interface SFUConfig { + url: string; + jwt: string; +} + +export async function getSFUConfigWithOpenID( + client: MatrixClient, + roomName: string +): Promise { + const openIdToken = await client.getOpenIdToken(); + logger.debug("Got openID token", openIdToken); + + const livekitCfg = Config.get().livekit; + + if (!livekitCfg?.livekit_service_url) { + throw new Error("No livekit service URL defined"); + } + + const res = await fetch(livekitCfg.livekit_service_url + "/sfu/get", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + room: roomName, + openid_token: openIdToken, + remove_me_user_id: client.getUserId(), // the service will get this from the openid request + device_id: client.getDeviceId(), + }), + }); + if (res.status / 100 !== 2) { + throw new Error("SFO Config fetch failed with status code " + res.status); + } + const sfuConfig = await res.json(); + + return sfuConfig; +} From 4efd88905dfc7ba38b203b0cac25aa47867b61f9 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 30 Jun 2023 16:13:02 +0100 Subject: [PATCH 03/13] Move ...rest param --- src/livekit/OpenIDLoader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/livekit/OpenIDLoader.tsx b/src/livekit/OpenIDLoader.tsx index 9e22003..c6d6bd9 100644 --- a/src/livekit/OpenIDLoader.tsx +++ b/src/livekit/OpenIDLoader.tsx @@ -48,7 +48,7 @@ export function OpenIDLoader({ client, roomName, ...rest }: Props) { if (error) { return ; } else if (sfuConfig) { - return ; + return ; } else { return ; } From 23b8a61e7aae1b6d1fb43b269178af65053ada0d Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 30 Jun 2023 18:12:58 +0100 Subject: [PATCH 04/13] Provide sfu config via context --- src/livekit/OpenIDLoader.tsx | 26 +++++++++++++++++++------- src/room/GroupCallView.tsx | 26 ++++++++++++++------------ src/room/InCallView.tsx | 6 +++--- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/livekit/OpenIDLoader.tsx b/src/livekit/OpenIDLoader.tsx index c6d6bd9..34aa5ec 100644 --- a/src/livekit/OpenIDLoader.tsx +++ b/src/livekit/OpenIDLoader.tsx @@ -15,21 +15,29 @@ limitations under the License. */ import { MatrixClient } from "matrix-js-sdk"; -import React, { useEffect, useState } from "react"; +import React, { + ReactNode, + createContext, + useContext, + useEffect, + useState, +} from "react"; import { logger } from "matrix-js-sdk/src/logger"; import { SFUConfig, getSFUConfigWithOpenID } from "./openIDSFU"; import { ErrorView, LoadingView } from "../FullScreenView"; -import { ActiveCall, InCallViewProps } from "../room/InCallView"; -import { UserChoices } from "./useLiveKit"; -interface Props extends Omit { +interface Props { client: MatrixClient; roomName: string; - userChoices: UserChoices; + children: ReactNode; } -export function OpenIDLoader({ client, roomName, ...rest }: Props) { +const SFUConfigContext = createContext(undefined); + +export const useSFUConfig = () => useContext(SFUConfigContext); + +export function OpenIDLoader({ client, roomName, children }: Props) { const [sfuConfig, setSFUConfig] = useState(); const [error, setError] = useState(); @@ -48,7 +56,11 @@ export function OpenIDLoader({ client, roomName, ...rest }: Props) { if (error) { return ; } else if (sfuConfig) { - return ; + return ( + + {children} + + ); } else { return ; } diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 2bbd210..888dcbf 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -35,6 +35,7 @@ import { useProfile } from "../profile/useProfile"; import { UserChoices } from "../livekit/useLiveKit"; import { findDeviceByName } from "../media-utils"; import { OpenIDLoader } from "../livekit/OpenIDLoader"; +import { ActiveCall } from "./InCallView"; declare global { interface Window { @@ -222,18 +223,19 @@ export function GroupCallView({ return ; } else if (state === GroupCallState.Entered && userChoices) { return ( - + + + ); } else if (left) { // The call ended view is shown for two reasons: prompting guests to create diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 10d8818..79a7e73 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -81,9 +81,9 @@ import { RageshakeRequestModal } from "./RageshakeRequestModal"; import { VideoTile } from "../video-grid/VideoTile"; import { UserChoices, useLiveKit } from "../livekit/useLiveKit"; import { useMediaDevices } from "../livekit/useMediaDevices"; -import { SFUConfig } from "../livekit/openIDSFU"; import { useFullscreen } from "./useFullscreen"; import { useLayoutStates } from "../video-grid/Layout"; +import { useSFUConfig } from "../livekit/OpenIDLoader"; const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {}); // There is currently a bug in Safari our our code with cloning and sending MediaStreams @@ -93,11 +93,11 @@ const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); export interface ActiveCallProps extends Omit { userChoices: UserChoices; - sfuConfig: SFUConfig; } export function ActiveCall(props: ActiveCallProps) { - const livekitRoom = useLiveKit(props.userChoices, props.sfuConfig); + const sfuConfig = useSFUConfig(); + const livekitRoom = useLiveKit(props.userChoices, sfuConfig); return ( livekitRoom && ( From 9e95e8b5a7ae63c31c0bf813a42371f1446c9718 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 3 Jul 2023 13:00:25 +0100 Subject: [PATCH 05/13] User ID field no longer neccessary --- src/livekit/openIDSFU.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/livekit/openIDSFU.ts b/src/livekit/openIDSFU.ts index 422632a..59a5da5 100644 --- a/src/livekit/openIDSFU.ts +++ b/src/livekit/openIDSFU.ts @@ -45,7 +45,6 @@ export async function getSFUConfigWithOpenID( body: JSON.stringify({ room: roomName, openid_token: openIdToken, - remove_me_user_id: client.getUserId(), // the service will get this from the openid request device_id: client.getDeviceId(), }), }); From f2eabec382b803e4be4642075f43f99191d224a4 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 3 Jul 2023 16:21:56 +0100 Subject: [PATCH 06/13] Be stricter with what is passed in to the openid components --- src/livekit/OpenIDLoader.tsx | 25 +++++++++++++++++++------ src/livekit/openIDSFU.ts | 19 +++++++++---------- src/room/GroupCallView.tsx | 13 ++++++++++++- 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/livekit/OpenIDLoader.tsx b/src/livekit/OpenIDLoader.tsx index 34aa5ec..df692f6 100644 --- a/src/livekit/OpenIDLoader.tsx +++ b/src/livekit/OpenIDLoader.tsx @@ -14,7 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { MatrixClient } from "matrix-js-sdk"; import React, { ReactNode, createContext, @@ -24,11 +23,16 @@ import React, { } from "react"; import { logger } from "matrix-js-sdk/src/logger"; -import { SFUConfig, getSFUConfigWithOpenID } from "./openIDSFU"; +import { + OpenIDClientParts, + SFUConfig, + getSFUConfigWithOpenID, +} from "./openIDSFU"; import { ErrorView, LoadingView } from "../FullScreenView"; interface Props { - client: MatrixClient; + client: OpenIDClientParts; + livekitServiceURL: string; roomName: string; children: ReactNode; } @@ -37,21 +41,30 @@ const SFUConfigContext = createContext(undefined); export const useSFUConfig = () => useContext(SFUConfigContext); -export function OpenIDLoader({ client, roomName, children }: Props) { +export function OpenIDLoader({ + client, + livekitServiceURL, + roomName, + children, +}: Props) { const [sfuConfig, setSFUConfig] = useState(); const [error, setError] = useState(); useEffect(() => { (async () => { try { - const result = await getSFUConfigWithOpenID(client, roomName); + const result = await getSFUConfigWithOpenID( + client, + livekitServiceURL, + roomName + ); setSFUConfig(result); } catch (e) { logger.error("Failed to fetch SFU config: ", e); setError(new Error("Failed to fetch SFU config")); } })(); - }, [client, roomName]); + }, [client, livekitServiceURL, roomName]); if (error) { return ; diff --git a/src/livekit/openIDSFU.ts b/src/livekit/openIDSFU.ts index 59a5da5..9d46c51 100644 --- a/src/livekit/openIDSFU.ts +++ b/src/livekit/openIDSFU.ts @@ -17,27 +17,26 @@ limitations under the License. import { MatrixClient } from "matrix-js-sdk"; import { logger } from "matrix-js-sdk/src/logger"; -import { Config } from "../config/Config"; - export interface SFUConfig { url: string; jwt: string; } +// The bits we need from MatrixClient +export type OpenIDClientParts = Pick< + MatrixClient, + "getOpenIdToken" | "getDeviceId" +>; + export async function getSFUConfigWithOpenID( - client: MatrixClient, + client: OpenIDClientParts, + livekitServiceURL: string, roomName: string ): Promise { const openIdToken = await client.getOpenIdToken(); logger.debug("Got openID token", openIdToken); - const livekitCfg = Config.get().livekit; - - if (!livekitCfg?.livekit_service_url) { - throw new Error("No livekit service URL defined"); - } - - const res = await fetch(livekitCfg.livekit_service_url + "/sfu/get", { + const res = await fetch(livekitServiceURL + "/sfu/get", { method: "POST", headers: { "Content-Type": "application/json", diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 888dcbf..6df1246 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -36,6 +36,7 @@ import { UserChoices } from "../livekit/useLiveKit"; import { findDeviceByName } from "../media-utils"; import { OpenIDLoader } from "../livekit/OpenIDLoader"; import { ActiveCall } from "./InCallView"; +import { Config } from "../config/Config"; declare global { interface Window { @@ -219,11 +220,21 @@ export function GroupCallView({ undefined ); + const lkServiceURL = Config.get().livekit?.livekit_service_url; + + if (!lkServiceURL) { + return ; + } + if (error) { return ; } else if (state === GroupCallState.Entered && userChoices) { return ( - + Date: Mon, 3 Jul 2023 17:06:22 +0100 Subject: [PATCH 07/13] Unused import --- src/livekit/useLiveKit.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/livekit/useLiveKit.ts b/src/livekit/useLiveKit.ts index 7e8f26e..c35f4f4 100644 --- a/src/livekit/useLiveKit.ts +++ b/src/livekit/useLiveKit.ts @@ -1,6 +1,5 @@ import { Room, RoomOptions } from "livekit-client"; import { useLiveKitRoom } from "@livekit/components-react"; -import React from "react"; import { useMemo } from "react"; import { defaultLiveKitOptions } from "./options"; From edcf9f3fd575dc0f424489a7d70c678958c45e04 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 3 Jul 2023 17:07:00 +0100 Subject: [PATCH 08/13] Use the repsonse,.ok rather than manual status code check --- src/livekit/openIDSFU.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/livekit/openIDSFU.ts b/src/livekit/openIDSFU.ts index 9d46c51..e4d0d45 100644 --- a/src/livekit/openIDSFU.ts +++ b/src/livekit/openIDSFU.ts @@ -47,7 +47,7 @@ export async function getSFUConfigWithOpenID( device_id: client.getDeviceId(), }), }); - if (res.status / 100 !== 2) { + if (!res.ok) { throw new Error("SFO Config fetch failed with status code " + res.status); } const sfuConfig = await res.json(); From 5305a287fecc1574bb8b033482563f0f31e032d6 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 3 Jul 2023 17:10:36 +0100 Subject: [PATCH 09/13] Briefer syntax --- src/livekit/openIDSFU.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/livekit/openIDSFU.ts b/src/livekit/openIDSFU.ts index e4d0d45..89a5d4f 100644 --- a/src/livekit/openIDSFU.ts +++ b/src/livekit/openIDSFU.ts @@ -50,7 +50,5 @@ export async function getSFUConfigWithOpenID( if (!res.ok) { throw new Error("SFO Config fetch failed with status code " + res.status); } - const sfuConfig = await res.json(); - - return sfuConfig; + return await res.json(); } From c5f3941ad754a3d109fe1358f55fa232ce8bd5e9 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 3 Jul 2023 17:14:46 +0100 Subject: [PATCH 10/13] Unused import --- src/livekit/OpenIDLoader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/livekit/OpenIDLoader.tsx b/src/livekit/OpenIDLoader.tsx index df692f6..8ce3c99 100644 --- a/src/livekit/OpenIDLoader.tsx +++ b/src/livekit/OpenIDLoader.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { +import { ReactNode, createContext, useContext, From 6ca5b46df75cc5ccd2a72a99f8d7eac75d3a4e61 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 3 Jul 2023 21:43:11 +0100 Subject: [PATCH 11/13] Update config to deployed lk jwt service --- config/element_io_preview.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/config/element_io_preview.json b/config/element_io_preview.json index 30c2cc8..cb6fc08 100644 --- a/config/element_io_preview.json +++ b/config/element_io_preview.json @@ -6,8 +6,7 @@ } }, "livekit": { - "server_url": "wss://sfu.call.element.dev", - "jwt_service_url": "https://voip-sip-poc.element.io/lk/jwt_service" + "livekit_service_url": "https://lk-jwt-service.lab.element.dev" }, "posthog": { "api_key": "phc_rXGHx9vDmyEvyRxPziYtdVIv0ahEv8A9uLWFcCi1WcU", From fc7a7b17990aad156164a556cb728829bad6c2a5 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 4 Jul 2023 15:44:55 +0100 Subject: [PATCH 12/13] Use union type for openidloader state Co-authored-by: Daniel Abramov --- src/livekit/OpenIDLoader.tsx | 44 ++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/src/livekit/OpenIDLoader.tsx b/src/livekit/OpenIDLoader.tsx index 8ce3c99..9e68d31 100644 --- a/src/livekit/OpenIDLoader.tsx +++ b/src/livekit/OpenIDLoader.tsx @@ -47,8 +47,9 @@ export function OpenIDLoader({ roomName, children, }: Props) { - const [sfuConfig, setSFUConfig] = useState(); - const [error, setError] = useState(); + const [state, setState] = useState< + SFUConfigLoading | SFUConfigLoaded | SFUConfigFailed + >({ kind: "loading" }); useEffect(() => { (async () => { @@ -58,23 +59,38 @@ export function OpenIDLoader({ livekitServiceURL, roomName ); - setSFUConfig(result); + setState({ kind: "loaded", sfuConfig: result }); } catch (e) { logger.error("Failed to fetch SFU config: ", e); - setError(new Error("Failed to fetch SFU config")); + setState({ kind: "failed", error: e }); } })(); }, [client, livekitServiceURL, roomName]); - if (error) { - return ; - } else if (sfuConfig) { - return ( - - {children} - - ); - } else { - return ; + switch (state.kind) { + case "loading": + return ; + case "failed": + return ; + case "loaded": + return ( + + {children} + + ); } } + +type SFUConfigLoading = { + kind: "loading"; +}; + +type SFUConfigLoaded = { + kind: "loaded"; + sfuConfig: SFUConfig; +}; + +type SFUConfigFailed = { + kind: "failed"; + error: Error; +}; From be89fb7dd9782aa8b6f8f8365b6d8ae9d8189ec8 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 4 Jul 2023 19:01:11 +0100 Subject: [PATCH 13/13] Revert changes to auth server code --- backend/auth/server.go | 86 ++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 57 deletions(-) diff --git a/backend/auth/server.go b/backend/auth/server.go index f099784..b3721e2 100644 --- a/backend/auth/server.go +++ b/backend/auth/server.go @@ -15,74 +15,41 @@ type Handler struct { key, secret string } -type OpenIDTokenType struct { -} - -type SFURequest struct { - Room string `json:"room"` - OpenIDToken OpenIDTokenType `json:"openid_token"` - DeviceID string `json:"device_id"` - RemoveMeUserID string `json:"remove_me_user_id"` // we'll get this from OIDC -} - -type SFUResponse struct { - URL string `json:"url"` - JWT string `json:"jwt"` -} - func (h *Handler) handle(w http.ResponseWriter, r *http.Request) { log.Printf("Request from %s", r.RemoteAddr) // Set the CORS headers w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Methods", "POST") - w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token") + w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") + w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") // Handle preflight request (CORS) if r.Method == "OPTIONS" { w.WriteHeader(http.StatusOK) return - } else if r.Method == "POST" { - var body SFURequest - err := json.NewDecoder(r.Body).Decode(&body) - if err != nil { - log.Printf("Error decoding JSON: %v", err) - w.WriteHeader(http.StatusBadRequest) - return - } - - if body.Room == "" { - log.Printf("Request missing room") - w.WriteHeader(http.StatusBadRequest) - return - } - - token, err := getJoinToken(h.key, h.secret, body.Room, body.RemoveMeUserID+":"+body.DeviceID) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - - res := SFUResponse{URL: "http://localhost:7880/", JWT: token} - - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(res) - } else { - w.WriteHeader(http.StatusMethodNotAllowed) } - /* - roomName := r.URL.Query().Get("roomName") - name := r.URL.Query().Get("name") - identity := r.URL.Query().Get("identity") + roomName := r.URL.Query().Get("roomName") + name := r.URL.Query().Get("name") + identity := r.URL.Query().Get("identity") - log.Printf("roomName: %s, name: %s, identity: %s", roomName, name, identity) + log.Printf("roomName: %s, name: %s, identity: %s", roomName, name, identity) - if roomName == "" || name == "" || identity == "" { - w.WriteHeader(http.StatusBadRequest) - return - } - */ + if roomName == "" || name == "" || identity == "" { + w.WriteHeader(http.StatusBadRequest) + return + } + + token, err := getJoinToken(h.key, h.secret, roomName, identity, name) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + res := Response{token} + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(res) } func main() { @@ -101,11 +68,15 @@ func main() { secret: secret, } - http.HandleFunc("/sfu/get", handler.handle) + http.HandleFunc("/token", handler.handle) log.Fatal(http.ListenAndServe(":8080", nil)) } -func getJoinToken(apiKey, apiSecret, room, identity string) (string, error) { +type Response struct { + Token string `json:"accessToken"` +} + +func getJoinToken(apiKey, apiSecret, room, identity, name string) (string, error) { at := auth.NewAccessToken(apiKey, apiSecret) canPublish := true @@ -120,7 +91,8 @@ func getJoinToken(apiKey, apiSecret, room, identity string) (string, error) { at.AddGrant(grant). SetIdentity(identity). - SetValidFor(time.Hour) + SetValidFor(time.Hour). + SetName(name) return at.ToJWT() }