Skip to content

Commit 2cdfb01

Browse files
committed
feat(vncscreen.tsx): supports a websocket instance as prop which may be pre-authenticated
Currently, `react-vnc` requires a WebSocket URL and handles the connection internally. However, many VNC servers require custom authentication flows (cookie-based auth, token-based auth, MessagePack handshakes, etc.) that must be completed before the VNC protocol can begin.
1 parent e526764 commit 2cdfb01

File tree

2 files changed

+75
-5
lines changed

2 files changed

+75
-5
lines changed

README.md

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,76 @@ function App() {
131131
export default App;
132132
```
133133

134-
The only `required` parameter is `url`, which must be a `ws://` or a `wss://` (websocket) URL for the library to function properly. noVNC can display only websocket URLs. All other props to `VncScreen` are optional. The following is a list (an interface) of all props along with their types.
134+
### Using Pre-Authenticated WebSocket
135+
136+
If you need to handle authentication or perform a custom handshake before establishing the VNC connection, you can pass a pre-authenticated WebSocket instance instead of a URL:
137+
138+
```ts
139+
import React, { useEffect, useState } from 'react';
140+
import { VncScreen } from 'react-vnc';
141+
142+
function App() {
143+
const [websocket, setWebsocket] = useState<WebSocket | null>(null);
144+
145+
useEffect(() => {
146+
// Create WebSocket and handle authentication
147+
const ws = new WebSocket('ws://your-vnc-server.com');
148+
149+
ws.addEventListener('open', () => {
150+
// Perform custom authentication or handshake
151+
ws.send(JSON.stringify({ token: 'your-auth-token' }));
152+
});
153+
154+
ws.addEventListener('message', (event) => {
155+
const response = JSON.parse(event.data);
156+
if (response.authenticated) {
157+
// Once authenticated, pass the WebSocket to VncScreen
158+
setWebsocket(ws);
159+
}
160+
});
161+
162+
return () => {
163+
ws.close();
164+
};
165+
}, []);
166+
167+
if (!websocket) {
168+
return <div>Authenticating...</div>;
169+
}
170+
171+
return (
172+
<VncScreen
173+
websocket={websocket}
174+
scaleViewport
175+
style={{
176+
width: '75vw',
177+
height: '75vh',
178+
}}
179+
/>
180+
);
181+
}
182+
183+
export default App;
184+
```
185+
186+
This approach is particularly useful for:
187+
- Cookie-based authentication
188+
- Custom authentication protocols
189+
- Connection pooling or reuse
190+
- Advanced WebSocket configuration
191+
192+
Either `url` or `websocket` is required:
193+
- **`url`**: A `ws://` or `wss://` websocket URL to connect to the VNC server
194+
- **`websocket`**: A pre-authenticated WebSocket instance (useful for custom authentication flows)
195+
196+
All other props to `VncScreen` are optional. The following is a list (an interface) of all props along with their types.
135197

136198
```ts
137199
type EventListeners = { [T in NoVncEventType]?: (event: NoVncEvents[T]) => void };
138200

139201
interface Props {
140-
url: string;
202+
url?: string;
203+
websocket?: WebSocket;
141204
style?: object;
142205
className?: string;
143206
viewOnly?: boolean;

src/lib/VncScreen.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import RFB, { NoVncEventType, NoVncEvents, NoVncOptions } from '@novnc/novnc/lib
1010
type EventListeners = { [T in NoVncEventType]?: (event: NoVncEvents[T]) => void };
1111

1212
export interface Props {
13-
url: string;
13+
url?: string;
14+
websocket?: WebSocket;
1415
style?: object;
1516
className?: string;
1617
viewOnly?: boolean;
@@ -66,6 +67,7 @@ const VncScreen: React.ForwardRefRenderFunction<VncScreenHandle, Props> = (props
6667

6768
const {
6869
url,
70+
websocket,
6971
style,
7072
className,
7173
viewOnly,
@@ -134,7 +136,7 @@ const VncScreen: React.ForwardRefRenderFunction<VncScreenHandle, Props> = (props
134136
}
135137

136138
const connected = getConnected();
137-
if (connected) {
139+
if (connected && !websocket) {
138140
logger.info(`Unexpectedly disconnected from remote VNC, retrying in ${retryDuration / 1000} seconds.`);
139141

140142
timeouts.current.push(setTimeout(connect, retryDuration));
@@ -205,9 +207,14 @@ const VncScreen: React.ForwardRefRenderFunction<VncScreenHandle, Props> = (props
205207
return;
206208
}
207209

210+
if (!url && !websocket) {
211+
logger.error('Either url or websocket must be provided');
212+
return;
213+
}
214+
208215
screen.current.innerHTML = '';
209216

210-
const _rfb = new RFB(screen.current, url, rfbOptions);
217+
const _rfb = new RFB(screen.current, websocket || url!, rfbOptions);
211218

212219
_rfb.viewOnly = viewOnly ?? false;
213220
_rfb.focusOnClick = focusOnClick ?? false;

0 commit comments

Comments
 (0)