From 8a60e2d1236bacc8f040117021f50a9d66f6a43d Mon Sep 17 00:00:00 2001 From: Chase Adams Date: Mon, 11 Dec 2023 12:07:00 -0700 Subject: [PATCH] Revert "Add header-based access control (#421)" This reverts commit f056bc36c41523494c3f14078683ed2d4dbe9889. --- .changeset/beige-countries-warn.md | 16 ---- .changeset/quiet-peaches-smash.md | 8 -- examples/_next/package.json | 1 - examples/_next/src/pages/api/jwt.ts | 77 ------------------- .../src/components/media/player/usePlayer.tsx | 29 +++---- .../media/player/useSourceMimeTyped.tsx | 45 ++++------- .../src/media/browser/webrtc/shared.ts | 31 +------- .../core-web/src/media/browser/webrtc/whep.ts | 35 --------- .../core-web/src/media/browser/webrtc/whip.ts | 37 +-------- packages/core/src/index.ts | 1 - packages/core/src/media/index.ts | 1 - packages/core/src/media/src.ts | 5 -- packages/core/src/version.ts | 6 +- .../media/players/video/HLSVideoPlayer.tsx | 13 +--- .../media/players/video/WebRTCVideoPlayer.tsx | 10 --- pnpm-lock.yaml | 4 +- 16 files changed, 36 insertions(+), 283 deletions(-) delete mode 100644 .changeset/beige-countries-warn.md delete mode 100644 .changeset/quiet-peaches-smash.md delete mode 100644 examples/_next/src/pages/api/jwt.ts diff --git a/.changeset/beige-countries-warn.md b/.changeset/beige-countries-warn.md deleted file mode 100644 index bbaff303..00000000 --- a/.changeset/beige-countries-warn.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -'@livepeer/core': patch -'@livepeer/core-react': patch -'@livepeer/core-web': patch -'@livepeer/react': patch ---- - -**Fix:** added timeout for VPN playback blocking ICE candidates and stalling indefinitely. The default is 5000ms with an override with: - -```tsx - -``` diff --git a/.changeset/quiet-peaches-smash.md b/.changeset/quiet-peaches-smash.md deleted file mode 100644 index cb538185..00000000 --- a/.changeset/quiet-peaches-smash.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@livepeer/core': patch -'@livepeer/core-react': patch -'@livepeer/core-web': patch -'@livepeer/react': patch ---- - -**Feature:** added header-based access control for webrtc and hls. diff --git a/examples/_next/package.json b/examples/_next/package.json index 66cb05a4..b85e1f97 100644 --- a/examples/_next/package.json +++ b/examples/_next/package.json @@ -9,7 +9,6 @@ "lint": "next lint" }, "dependencies": { - "@livepeer/core": "^2.0.10", "@livepeer/react": "^3.0.10", "@rainbow-me/rainbowkit": "^0.8.0", "ethers": "^5.7.2", diff --git a/examples/_next/src/pages/api/jwt.ts b/examples/_next/src/pages/api/jwt.ts deleted file mode 100644 index 679539e9..00000000 --- a/examples/_next/src/pages/api/jwt.ts +++ /dev/null @@ -1,77 +0,0 @@ -// use the signAccessJwt export from `livepeer` in Node.JS -import { signAccessJwt } from '@livepeer/core/crypto'; -import { NextApiRequest, NextApiResponse } from 'next'; - -export type CreateSignedPlaybackBody = { - playbackId: string; - secret: string; -}; - -export type CreateSignedPlaybackResponse = { - token: string; -}; - -const accessControlPrivateKey = process.env.ACCESS_CONTROL_PRIVATE_KEY; -const accessControlPublicKey = - process.env.NEXT_PUBLIC_ACCESS_CONTROL_PUBLIC_KEY; - -const handler = async ( - req: NextApiRequest, - res: NextApiResponse, -) => { - try { - const method = req.method; - - if (method === 'POST') { - if (!accessControlPrivateKey || !accessControlPublicKey) { - return res - .status(500) - .json({ message: 'No private/public key configured.' }); - } - - const { playbackId, secret }: CreateSignedPlaybackBody = req.body; - - if (!playbackId || !secret) { - return res.status(400).json({ message: 'Missing data in body.' }); - } - - // we check that the "supersecretkey" was passed in the body - // this could be a more complex check, like taking a signed payload, - // getting the address for that signature, and fetching if they own an NFT - // - // https://docs.ethers.io/v5/single-page/#/v5/api/utils/signing-key/-%23-SigningKey--other-functions - if (secret !== 'supersecretkey') { - return res.status(401).json({ message: 'Incorrect secret.' }); - } - - // we sign the JWT and return it to the user - const token = await signAccessJwt({ - privateKey: accessControlPrivateKey, - publicKey: accessControlPublicKey, - issuer: 'https://docs.livepeer.org', - // playback ID to include in the JWT - playbackId, - // expire the JWT in 1 hour - expiration: '1h', - // custom metadata to include - custom: { - userId: 'user-id-1', - }, - }); - - return res.status(200).json({ - token, - }); - } - - res.setHeader('Allow', ['POST']); - return res.status(405).end(`Method ${method} Not Allowed`); - } catch (err) { - console.error(err); - return res - .status(500) - .json({ message: (err as Error)?.message ?? 'Error' }); - } -}; - -export default handler; diff --git a/packages/core-react/src/components/media/player/usePlayer.tsx b/packages/core-react/src/components/media/player/usePlayer.tsx index 2295845d..94723a50 100644 --- a/packages/core-react/src/components/media/player/usePlayer.tsx +++ b/packages/core-react/src/components/media/player/usePlayer.tsx @@ -238,19 +238,18 @@ export const usePlayer = < ) => { const [mediaElement, setMediaElement] = React.useState(null); - const { source, uploadStatus, jwtResolved, accessKeyResolved } = - useSourceMimeTyped({ - src, - playbackId, - jwt, - refetchPlaybackInfoInterval, - autoUrlUpload, - screenWidth: _screenWidth, - playbackInfo, - accessKey, - onAccessKeyRequest, - playRecording, - }); + const { source, uploadStatus } = useSourceMimeTyped({ + src, + playbackId, + jwt, + refetchPlaybackInfoInterval, + autoUrlUpload, + screenWidth: _screenWidth, + playbackInfo, + accessKey, + onAccessKeyRequest, + playRecording, + }); const [playbackError, setPlaybackError] = React.useState(null); @@ -369,8 +368,6 @@ export const usePlayer = < viewerId, onPlaybackStatusUpdate, playbackStatusSelector, - jwt: jwtResolved, - accessKey: accessKeyResolved, }), [ playerRef, @@ -388,8 +385,6 @@ export const usePlayer = < viewerId, onPlaybackStatusUpdate, playbackStatusSelector, - jwtResolved, - accessKeyResolved, ], ); diff --git a/packages/core-react/src/components/media/player/useSourceMimeTyped.tsx b/packages/core-react/src/components/media/player/useSourceMimeTyped.tsx index fcece1f9..be13e154 100644 --- a/packages/core-react/src/components/media/player/useSourceMimeTyped.tsx +++ b/packages/core-react/src/components/media/player/useSourceMimeTyped.tsx @@ -274,57 +274,46 @@ export const useSourceMimeTyped = < return defaultValue; } - const mediaSourceTypes = sources - .map((s) => (typeof s === 'string' ? getMediaSourceType(s) : null)) - .filter((s) => s) as Src[]; - - const authenticatedSources = mediaSourceTypes.map((source) => { - // we use headers for HLS and WebRTC for auth - if (source.type === 'hls' || source.type === 'webrtc') { - return source; - } - + const authenticatedSources = sources.map((source) => { // append the JWT to the query params if (jwt && source) { - const url = new URL(source.src); + const url = new URL(source); url.searchParams.append('jwt', jwt); - return { - ...source, - src: url.toString(), - }; + return url.toString(); } // append the access key to the query params if (accessKeyResolved && source) { - const url = new URL(source.src); + const url = new URL(source); url.searchParams.append('accessKey', accessKeyResolved); - return { - ...source, - src: url.toString(), - }; + return url.toString(); } return source; }); + const mediaSourceTypes = authenticatedSources + .map((s) => (typeof s === 'string' ? getMediaSourceType(s) : null)) + .filter((s) => s) as Src[]; + // we filter by either audio or video/hls - return authenticatedSources?.[0]?.type === 'audio' + return mediaSourceTypes?.[0]?.type === 'audio' ? ([ 'audio', - authenticatedSources.filter((s) => s.type === 'audio') as AudioSrc[], + mediaSourceTypes.filter((s) => s.type === 'audio') as AudioSrc[], ] as const) - : authenticatedSources?.[0]?.type === 'video' || - authenticatedSources?.[0]?.type === 'hls' || - authenticatedSources?.[0]?.type === 'webrtc' + : mediaSourceTypes?.[0]?.type === 'video' || + mediaSourceTypes?.[0]?.type === 'hls' || + mediaSourceTypes?.[0]?.type === 'webrtc' ? ([ 'video', - authenticatedSources.filter( + mediaSourceTypes.filter( (s) => s.type === 'video' || s.type === 'hls' || s.type === 'webrtc', ) as (VideoSrc | HlsSrc | WebRTCSrc)[], ] as const) : defaultValue; - }, [playbackUrls, src, playRecording]); + }, [playbackUrls, src, jwt, accessKeyResolved, playRecording]); const sourceMimeTypedSorted = React.useMemo(() => { // if there is no source mime type and the Player has dstorage fallback enabled, @@ -372,7 +361,5 @@ export const useSourceMimeTyped = < return { source: sourceMimeTypedSorted, uploadStatus, - accessKeyResolved, - jwtResolved: jwt, } as const; }; diff --git a/packages/core-web/src/media/browser/webrtc/shared.ts b/packages/core-web/src/media/browser/webrtc/shared.ts index 247fe839..41ec8598 100644 --- a/packages/core-web/src/media/browser/webrtc/shared.ts +++ b/packages/core-web/src/media/browser/webrtc/shared.ts @@ -1,7 +1,4 @@ -import { - AccessControlParams, - NOT_ACCEPTABLE_ERROR_MESSAGE, -} from '@livepeer/core'; +import { NOT_ACCEPTABLE_ERROR_MESSAGE } from '@livepeer/core'; import fetch from 'cross-fetch'; import { isClient } from '../utils'; @@ -75,12 +72,6 @@ export type WebRTCVideoConfig = { * @default 20000 */ sdpTimeout?: number; - /** - * The timeout of the time to wait for ICE candidates, in ms. - * - * @default 5000 - */ - iceCandidateTimeout?: number; /** * Disables the speedup/slowdown mechanic in WebRTC, to allow for non-distorted audio. */ @@ -106,7 +97,6 @@ export async function negotiateConnectionWithClientOffer( ofr: RTCSessionDescription | null, controller: AbortController, config?: WebRTCVideoConfig, - accessControl?: AccessControlParams, ): Promise { if (peerConnection && endpoint && ofr) { /** @@ -114,13 +104,7 @@ export async function negotiateConnectionWithClientOffer( * This specifies how the client should communicate, * and what kind of media client and server have negotiated to exchange. */ - const response = await postSDPOffer( - endpoint, - ofr.sdp, - controller, - config, - accessControl, - ); + const response = await postSDPOffer(endpoint, ofr.sdp, controller, config); if (response.ok) { const answerSDP = await response.text(); await peerConnection.setRemoteDescription( @@ -176,7 +160,6 @@ async function postSDPOffer( data: string, controller: AbortController, config?: WebRTCVideoConfig, - accessControl?: AccessControlParams, ) { const id = setTimeout( () => controller.abort(), @@ -194,16 +177,6 @@ async function postSDPOffer( mode: 'cors', headers: { 'content-type': 'application/sdp', - ...(accessControl?.accessKey - ? { - 'Livepeer-Access-Key': accessControl.accessKey, - } - : {}), - ...(accessControl?.jwt - ? { - 'Livepeer-Jwt': accessControl.jwt, - } - : {}), }, body: data, signal: controller.signal, diff --git a/packages/core-web/src/media/browser/webrtc/whep.ts b/packages/core-web/src/media/browser/webrtc/whep.ts index cacd7312..67cb8801 100644 --- a/packages/core-web/src/media/browser/webrtc/whep.ts +++ b/packages/core-web/src/media/browser/webrtc/whep.ts @@ -1,5 +1,3 @@ -import { AccessControlParams } from '@livepeer/core'; - import { WebRTCVideoConfig, constructClientOffer, @@ -23,7 +21,6 @@ export const createNewWHEP = ( onRedirect?: (url: string | null) => void; }, config?: WebRTCVideoConfig, - accessControl?: AccessControlParams, ): { destroy: () => void; } => { @@ -140,7 +137,6 @@ export const createNewWHEP = ( ofr, abortController, config, - accessControl, ); const currentDate = Date.now(); @@ -154,37 +150,6 @@ export const createNewWHEP = ( errorComposed(e as Error); } }); - - let iceCandidatesGenerated = false; - - const iceCandidateTimeout = setTimeout(() => { - if (!iceCandidatesGenerated) { - errorComposed(new Error('Failed to generate any ICE candidates')); - } - }, config?.iceCandidateTimeout ?? 5000); - - peerConnection?.addEventListener('icecandidate', (event) => { - if (event.candidate) { - clearTimeout(iceCandidateTimeout); - iceCandidatesGenerated = true; - } - }); - - peerConnection.addEventListener('iceconnectionstatechange', (_e) => { - if (peerConnection?.iceConnectionState === 'failed') { - errorComposed(new Error('ICE Connection Failed')); - } - }); - - peerConnection.addEventListener('icecandidateerror', (e) => { - errorComposed( - new Error( - `ICE Candidate Error: ${ - (e as RTCPeerConnectionIceErrorEvent)?.errorText - }`, - ), - ); - }); } }) .catch((e) => errorComposed(e as Error)); diff --git a/packages/core-web/src/media/browser/webrtc/whip.ts b/packages/core-web/src/media/browser/webrtc/whip.ts index 6e967bc9..e16dd4d4 100644 --- a/packages/core-web/src/media/browser/webrtc/whip.ts +++ b/packages/core-web/src/media/browser/webrtc/whip.ts @@ -1,4 +1,4 @@ -import { AccessControlParams, AspectRatio } from '@livepeer/core/media'; +import { AspectRatio } from '@livepeer/core/media'; import { WebRTCVideoConfig, @@ -45,7 +45,6 @@ export const createNewWHIP = ( onError?: (data: Error) => void; }, config?: WebRTCVideoConfig, - accessControl?: AccessControlParams, ): { destroy: () => void; } => { @@ -86,7 +85,6 @@ export const createNewWHIP = ( ofr, abortController, config, - accessControl, ); } catch (e) { callbacks?.onError?.(e as Error); @@ -121,39 +119,6 @@ export const createNewWHIP = ( }, ); - let iceCandidatesGenerated = false; - - const iceCandidateTimeout = setTimeout(() => { - if (!iceCandidatesGenerated) { - callbacks?.onError?.( - new Error('Failed to generate any ICE candidates'), - ); - } - }, config?.iceCandidateTimeout ?? 5000); - - peerConnection?.addEventListener('icecandidate', (event) => { - if (event.candidate) { - clearTimeout(iceCandidateTimeout); - iceCandidatesGenerated = true; - } - }); - - peerConnection.addEventListener('iceconnectionstatechange', (_e) => { - if (peerConnection?.iceConnectionState === 'failed') { - callbacks?.onError?.(new Error('ICE Connection Failed')); - } - }); - - peerConnection.addEventListener('icecandidateerror', (e) => { - callbacks?.onError?.( - new Error( - `ICE Candidate Error: ${ - (e as RTCPeerConnectionIceErrorEvent)?.errorText - }`, - ), - ); - }); - /** * While the connection is being initialized, ask for camera and microphone permissions and * add video and audio tracks to the peerConnection. diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 87f76c75..acf8e763 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -38,7 +38,6 @@ export { sanitizeMediaControllerState, } from './media'; export type { - AccessControlParams, AudioSrc, Base64Src, ClipLength, diff --git a/packages/core/src/media/index.ts b/packages/core/src/media/index.ts index 50c1b78d..4054b2d8 100644 --- a/packages/core/src/media/index.ts +++ b/packages/core/src/media/index.ts @@ -22,7 +22,6 @@ export { addMediaMetricsToStore } from './metrics'; export type { MediaMetrics, MetricsStatus, PlaybackMonitor } from './metrics'; export { getMediaSourceType } from './src'; export type { - AccessControlParams, AudioSrc, Base64Src, HlsSrc, diff --git a/packages/core/src/media/src.ts b/packages/core/src/media/src.ts index 13ecb82e..59ea4ccd 100644 --- a/packages/core/src/media/src.ts +++ b/packages/core/src/media/src.ts @@ -47,11 +47,6 @@ export interface WebRTCSrc extends BaseSrc { } export type Src = AudioSrc | HlsSrc | VideoSrc | Base64Src | WebRTCSrc; -export type AccessControlParams = { - jwt?: string; - accessKey?: string; -}; - const audioExtensions = /\.(m4a|mp4a|mpga|mp2|mp2a|mp3|m2a|m3a|wav|weba|aac|oga|spx)($|\?)/i; const videoExtensions = /\.(mp4|ogv|webm|mov|m4v|avi|m3u8)($|\?)/i; diff --git a/packages/core/src/version.ts b/packages/core/src/version.ts index 7036f620..32a2520e 100644 --- a/packages/core/src/version.ts +++ b/packages/core/src/version.ts @@ -1,6 +1,6 @@ -const core = `@livepeer/core@2.0.10`; -const react = `@livepeer/react@3.0.10`; -const reactNative = `@livepeer/react-native@2.0.9`; +const core = `@livepeer/core@2.0.4`; +const react = `@livepeer/react@3.0.4`; +const reactNative = `@livepeer/react-native@2.0.4`; export const version = { core, diff --git a/packages/react/src/components/media/players/video/HLSVideoPlayer.tsx b/packages/react/src/components/media/players/video/HLSVideoPlayer.tsx index be0f25e6..0cda2d2f 100644 --- a/packages/react/src/components/media/players/video/HLSVideoPlayer.tsx +++ b/packages/react/src/components/media/players/video/HLSVideoPlayer.tsx @@ -33,7 +33,6 @@ export type HLSVideoPlayerProps = Omit< > & { src: HlsSrc; fullscreen: boolean; - accessKey?: string; }; export const HLSVideoPlayer = React.forwardRef< @@ -54,8 +53,6 @@ export const HLSVideoPlayer = React.forwardRef< priority, allowCrossOriginCredentials, playRecording, - jwt, - accessKey, } = props; const { @@ -111,12 +108,7 @@ export const HLSVideoPlayer = React.forwardRef< { autoplay: autoPlay, xhrSetup(xhr, _url) { - xhr.withCredentials = Boolean( - allowCrossOriginCredentials || jwt || accessKey, - ); - if (accessKey) - xhr.setRequestHeader('Livepeer-Access-Key', accessKey); - else if (jwt) xhr.setRequestHeader('Livepeer-Jwt', jwt); + xhr.withCredentials = Boolean(allowCrossOriginCredentials); }, ...hlsConfig, }, @@ -138,9 +130,6 @@ export const HLSVideoPlayer = React.forwardRef< allowCrossOriginCredentials, debouncedErrorCount, _updatePlaybackOffsetMs, - onRedirect, - jwt, - accessKey, ]); return ( diff --git a/packages/react/src/components/media/players/video/WebRTCVideoPlayer.tsx b/packages/react/src/components/media/players/video/WebRTCVideoPlayer.tsx index e3a456e5..a35cd657 100644 --- a/packages/react/src/components/media/players/video/WebRTCVideoPlayer.tsx +++ b/packages/react/src/components/media/players/video/WebRTCVideoPlayer.tsx @@ -36,7 +36,6 @@ export type WebRTCVideoPlayerProps = Omit< src: WebRTCSrc; fullscreen: boolean; webrtcConfig?: WebRTCVideoConfig; - accessKey?: string; }; export const WebRTCVideoPlayer = React.forwardRef< @@ -55,8 +54,6 @@ export const WebRTCVideoPlayer = React.forwardRef< onPlaybackError, priority, webrtcConfig, - jwt, - accessKey, } = props; const { metadata, _element, setLive, _updatePlaybackOffsetMs, onRedirect } = @@ -103,10 +100,6 @@ export const WebRTCVideoPlayer = React.forwardRef< onRedirect, }, webrtcConfig, - { - jwt, - accessKey, - }, ); return () => { @@ -122,9 +115,6 @@ export const WebRTCVideoPlayer = React.forwardRef< onPlaybackError, debouncedErrorCount, _updatePlaybackOffsetMs, - onRedirect, - jwt, - accessKey, ]); return ( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 36043199..d93bd7d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -117,7 +117,6 @@ importers: examples/_next: specifiers: - '@livepeer/core': ^2.0.10 '@livepeer/react': ^3.0.10 '@preconstruct/next': ^4.0.0 '@rainbow-me/rainbowkit': ^0.8.0 @@ -133,7 +132,6 @@ importers: typescript: ^4.9.3 wagmi: ^0.8.10 dependencies: - '@livepeer/core': link:../../packages/core '@livepeer/react': link:../../packages/react '@rainbow-me/rainbowkit': 0.8.0_tpe4dxlvg2xz35c35q3zxhbejm ethers: 5.7.2 @@ -11612,7 +11610,7 @@ packages: /@types/bn.js/4.11.6: resolution: {integrity: sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==} dependencies: - '@types/node': 20.6.2 + '@types/node': 18.11.11 dev: false /@types/chai-subset/1.3.3: