@dxos/websocket-rpc 0.4.10-main.fa5a270 → 0.4.10-main.fd8ea31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,10 +8,68 @@ import {
8
8
  } from "@dxos/node-std/inject-globals";
9
9
 
10
10
  // packages/core/mesh/websocket-rpc/src/client.ts
11
- import WebSocket from "isomorphic-ws";
11
+ import WebSocket2 from "isomorphic-ws";
12
12
  import { Event, Trigger } from "@dxos/async";
13
- import { log, logInfo } from "@dxos/log";
13
+ import { log as log2, logInfo } from "@dxos/log";
14
14
  import { createProtoRpcPeer } from "@dxos/rpc";
15
+
16
+ // packages/core/mesh/websocket-rpc/src/token-auth.ts
17
+ import WebSocket from "isomorphic-ws";
18
+ import { log } from "@dxos/log";
19
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/mesh/websocket-rpc/src/token-auth.ts";
20
+ var PROTOCOL_TOKEN_PREFIX = "base64url.bearer.authorization.dxos.org";
21
+ var authenticateRequestWithTokenAuth = (request, socket, upgradeHead, token, cb) => {
22
+ const protocolHeader = request.headers["sec-websocket-protocol"];
23
+ if (!protocolHeader) {
24
+ log("upgrade unauthorized, header missing", {
25
+ header: request.headers["sec-websocket-protocol"]
26
+ }, {
27
+ F: __dxlog_file,
28
+ L: 28,
29
+ S: void 0,
30
+ C: (f, a) => f(...a)
31
+ });
32
+ socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
33
+ socket.destroy();
34
+ return;
35
+ }
36
+ const tokenHeader = protocolHeader.replace(new RegExp(`^${PROTOCOL_TOKEN_PREFIX}.`), "");
37
+ const encodedToken = Buffer.from(token).toString("base64").replace(/=*$/, "");
38
+ if (tokenHeader !== encodedToken) {
39
+ log("upgrade unauthorized", {
40
+ token,
41
+ foo: encodedToken
42
+ }, {
43
+ F: __dxlog_file,
44
+ L: 40,
45
+ S: void 0,
46
+ C: (f, a) => f(...a)
47
+ });
48
+ socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
49
+ socket.destroy();
50
+ return;
51
+ }
52
+ cb(request, socket, upgradeHead);
53
+ };
54
+ var WebSocketWithTokenAuth = class extends WebSocket {
55
+ constructor(url, token) {
56
+ const encodedToken = Buffer.from(token).toString("base64").replace(/=*$/, "");
57
+ const wsProtocols = [
58
+ `base64url.bearer.authorization.dxos.org.${encodedToken}`
59
+ ];
60
+ super(url, wsProtocols);
61
+ log("encodedToken", {
62
+ encodedToken
63
+ }, {
64
+ F: __dxlog_file,
65
+ L: 55,
66
+ S: this,
67
+ C: (f, a) => f(...a)
68
+ });
69
+ }
70
+ };
71
+
72
+ // packages/core/mesh/websocket-rpc/src/client.ts
15
73
  function _ts_decorate(decorators, target, key, desc) {
16
74
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
17
75
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
@@ -22,7 +80,7 @@ function _ts_decorate(decorators, target, key, desc) {
22
80
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
23
81
  return c > 3 && r && Object.defineProperty(target, key, r), r;
24
82
  }
25
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/mesh/websocket-rpc/src/client.ts";
83
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/mesh/websocket-rpc/src/client.ts";
26
84
  var WebsocketRpcClient = class {
27
85
  constructor(_params) {
28
86
  this._params = _params;
@@ -54,19 +112,23 @@ var WebsocketRpcClient = class {
54
112
  return this._params.url;
55
113
  }
56
114
  async open() {
57
- this._socket = new WebSocket(this._params.url);
115
+ if (this._params.authenticationToken) {
116
+ this._socket = new WebSocketWithTokenAuth(this._params.url, this._params.authenticationToken);
117
+ } else {
118
+ this._socket = new WebSocket2(this._params.url);
119
+ }
58
120
  this._socket.onopen = async () => {
59
- log("Socket open", void 0, {
60
- F: __dxlog_file,
61
- L: 55,
121
+ log2("Socket open", void 0, {
122
+ F: __dxlog_file2,
123
+ L: 61,
62
124
  S: this,
63
125
  C: (f, a) => f(...a)
64
126
  });
65
127
  try {
66
128
  await this._rpc.open();
67
- log(`RPC open ${this._params.url}`, void 0, {
68
- F: __dxlog_file,
69
- L: 58,
129
+ log2(`RPC open ${this._params.url}`, void 0, {
130
+ F: __dxlog_file2,
131
+ L: 64,
70
132
  S: this,
71
133
  C: (f, a) => f(...a)
72
134
  });
@@ -77,9 +139,9 @@ var WebsocketRpcClient = class {
77
139
  }
78
140
  };
79
141
  this._socket.onclose = async () => {
80
- log(`Disconnected ${this._params.url}`, void 0, {
81
- F: __dxlog_file,
82
- L: 67,
142
+ log2(`Disconnected ${this._params.url}`, void 0, {
143
+ F: __dxlog_file2,
144
+ L: 73,
83
145
  S: this,
84
146
  C: (f, a) => f(...a)
85
147
  });
@@ -87,11 +149,11 @@ var WebsocketRpcClient = class {
87
149
  await this.close();
88
150
  };
89
151
  this._socket.onerror = (event) => {
90
- log.error(event.message ?? "Socket error", {
152
+ log2.error(event.message ?? "Socket error", {
91
153
  url: this._params.url
92
154
  }, {
93
- F: __dxlog_file,
94
- L: 73,
155
+ F: __dxlog_file2,
156
+ L: 80,
95
157
  S: this,
96
158
  C: (f, a) => f(...a)
97
159
  });
@@ -105,9 +167,9 @@ var WebsocketRpcClient = class {
105
167
  try {
106
168
  await this._rpc?.close();
107
169
  } catch (err) {
108
- log.catch(err, void 0, {
109
- F: __dxlog_file,
110
- L: 86,
170
+ log2.catch(err, void 0, {
171
+ F: __dxlog_file2,
172
+ L: 93,
111
173
  S: this,
112
174
  C: (f, a) => f(...a)
113
175
  });
@@ -123,24 +185,29 @@ _ts_decorate([
123
185
  ], WebsocketRpcClient.prototype, "url", null);
124
186
 
125
187
  // packages/core/mesh/websocket-rpc/src/server.ts
126
- import WebSocket2 from "isomorphic-ws";
127
- import { log as log2 } from "@dxos/log";
188
+ import WebSocket3 from "isomorphic-ws";
189
+ import { log as log3 } from "@dxos/log";
128
190
  import { createProtoRpcPeer as createProtoRpcPeer2 } from "@dxos/rpc";
129
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/mesh/websocket-rpc/src/server.ts";
191
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/mesh/websocket-rpc/src/server.ts";
130
192
  var WebsocketRpcServer = class {
131
193
  constructor(_params) {
132
194
  this._params = _params;
133
195
  }
196
+ handleUpgrade(request, socket, head) {
197
+ this._server?.handleUpgrade(request, socket, head, (ws) => {
198
+ this._server?.emit("connection", ws, request);
199
+ });
200
+ }
134
201
  async open() {
135
- this._server = new WebSocket2.Server({
202
+ this._server = new WebSocket3.Server({
136
203
  ...this._params
137
204
  });
138
205
  this._server.on("connection", async (socket, request) => {
139
- log2("connection", {
206
+ log3("connection", {
140
207
  request
141
208
  }, {
142
- F: __dxlog_file2,
143
- L: 34,
209
+ F: __dxlog_file3,
210
+ L: 40,
144
211
  S: this,
145
212
  C: (f, a) => f(...a)
146
213
  });
@@ -178,7 +245,9 @@ var WebsocketRpcServer = class {
178
245
  }
179
246
  };
180
247
  export {
248
+ WebSocketWithTokenAuth,
181
249
  WebsocketRpcClient,
182
- WebsocketRpcServer
250
+ WebsocketRpcServer,
251
+ authenticateRequestWithTokenAuth
183
252
  };
184
253
  //# sourceMappingURL=index.mjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../../src/client.ts", "../../../src/server.ts"],
4
- "sourcesContent": ["//\n// Copyright 2022 DXOS.org\n//\n\nimport WebSocket from 'isomorphic-ws';\n\nimport { Event, Trigger } from '@dxos/async';\nimport { log, logInfo } from '@dxos/log';\nimport { createProtoRpcPeer, type ProtoRpcPeer, type ProtoRpcPeerOptions } from '@dxos/rpc';\n\nexport type WebsocketRpcClientParams<C, S> = {\n url: string;\n} & Pick<ProtoRpcPeerOptions<C, S>, 'requested' | 'exposed' | 'handlers'>;\n\nexport class WebsocketRpcClient<C, S> {\n private _socket?: WebSocket;\n private _rpc?: ProtoRpcPeer<C>;\n private readonly _connectTrigger = new Trigger();\n\n readonly connected = new Event();\n readonly disconnected = new Event();\n readonly error = new Event<Error>();\n\n constructor(private readonly _params: WebsocketRpcClientParams<C, S>) {\n this._rpc = createProtoRpcPeer({\n requested: this._params.requested,\n exposed: this._params.exposed,\n handlers: this._params.handlers,\n port: {\n send: (msg) => {\n this._socket!.send(msg);\n },\n subscribe: (cb) => {\n this._socket!.onmessage = async (msg: WebSocket.MessageEvent) => {\n if (typeof Blob !== 'undefined' && msg.data instanceof Blob) {\n cb(Buffer.from(await msg.data.arrayBuffer()));\n } else {\n cb(msg.data as any);\n }\n };\n },\n },\n });\n }\n\n @logInfo\n get url() {\n return this._params.url;\n }\n\n async open() {\n this._socket = new WebSocket(this._params.url);\n\n this._socket.onopen = async () => {\n log('Socket open');\n try {\n await this._rpc!.open();\n log(`RPC open ${this._params.url}`);\n this.connected.emit();\n this._connectTrigger.wake();\n } catch (err: any) {\n this.error.emit(err);\n }\n };\n\n this._socket.onclose = async () => {\n log(`Disconnected ${this._params.url}`);\n this.disconnected.emit();\n await this.close();\n };\n\n this._socket.onerror = (event: WebSocket.ErrorEvent) => {\n log.error(event.message ?? 'Socket error', { url: this._params.url });\n const error = event.error ?? new Error(event.message);\n this.error.emit(error);\n this._connectTrigger.throw(error);\n };\n\n await this._connectTrigger.wait();\n }\n\n async close() {\n try {\n await this._rpc?.close();\n } catch (err) {\n log.catch(err);\n }\n this._socket?.close();\n }\n\n get rpc() {\n return this._rpc!.rpc;\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { type IncomingMessage } from 'http';\nimport WebSocket from 'isomorphic-ws';\n\nimport { log } from '@dxos/log';\nimport { createProtoRpcPeer, type ProtoRpcPeer, type ProtoRpcPeerOptions } from '@dxos/rpc';\n\nexport type ConnectionInfo = {\n request: IncomingMessage;\n};\n\nexport type ConnectionHandler<C, S> = {\n onOpen?: (rpc: ProtoRpcPeer<C>) => Promise<void>;\n onClose?: (rpc: ProtoRpcPeer<C>) => Promise<void>;\n} & Pick<ProtoRpcPeerOptions<C, S>, 'requested' | 'exposed' | 'handlers'>;\n\nexport type WebsocketRpcServerParams<C, S> = {\n onConnection: (info: ConnectionInfo) => Promise<ConnectionHandler<C, S>>;\n} & WebSocket.ServerOptions;\n\nexport class WebsocketRpcServer<C, S> {\n private _server?: WebSocket.Server;\n\n constructor(private readonly _params: WebsocketRpcServerParams<C, S>) {}\n\n async open() {\n this._server = new WebSocket.Server({\n ...this._params,\n });\n this._server.on('connection', async (socket, request) => {\n log('connection', { request });\n const info: ConnectionInfo = {\n request,\n };\n const { onOpen, onClose, ...options } = await this._params.onConnection(info);\n const rpc = createProtoRpcPeer<C, S>({\n ...options,\n port: {\n send: (msg) => {\n socket.send(msg);\n },\n subscribe: (cb) => {\n socket.onmessage = async (msg: WebSocket.MessageEvent) => {\n if (typeof Blob !== 'undefined' && msg.data instanceof Blob) {\n cb(Buffer.from(await msg.data.arrayBuffer()));\n } else {\n cb(msg.data as any);\n }\n };\n },\n },\n });\n\n await rpc.open();\n await onOpen?.(rpc);\n socket.onclose = async () => {\n await onClose?.(rpc);\n await rpc.close();\n };\n });\n }\n\n async close() {\n this._server?.close();\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["WebSocket", "Event", "Trigger", "log", "logInfo", "createProtoRpcPeer", "WebsocketRpcClient", "constructor", "_params", "_connectTrigger", "connected", "disconnected", "error", "_rpc", "requested", "exposed", "handlers", "port", "send", "msg", "_socket", "subscribe", "cb", "onmessage", "Blob", "data", "Buffer", "from", "arrayBuffer", "url", "open", "onopen", "emit", "wake", "err", "onclose", "close", "onerror", "event", "message", "Error", "throw", "wait", "catch", "rpc", "WebSocket", "log", "createProtoRpcPeer", "WebsocketRpcServer", "constructor", "_params", "open", "_server", "Server", "on", "socket", "request", "info", "onOpen", "onClose", "options", "onConnection", "rpc", "port", "send", "msg", "subscribe", "cb", "onmessage", "Blob", "data", "Buffer", "from", "arrayBuffer", "onclose", "close"]
3
+ "sources": ["../../../src/client.ts", "../../../src/token-auth.ts", "../../../src/server.ts"],
4
+ "sourcesContent": ["//\n// Copyright 2022 DXOS.org\n//\n\nimport WebSocket from 'isomorphic-ws';\n\nimport { Event, Trigger } from '@dxos/async';\nimport { log, logInfo } from '@dxos/log';\nimport { createProtoRpcPeer, type ProtoRpcPeer, type ProtoRpcPeerOptions } from '@dxos/rpc';\n\nimport { WebSocketWithTokenAuth } from './token-auth';\n\nexport type WebsocketRpcClientParams<C, S> = {\n url: string;\n authenticationToken?: string;\n} & Pick<ProtoRpcPeerOptions<C, S>, 'requested' | 'exposed' | 'handlers'>;\n\nexport class WebsocketRpcClient<C, S> {\n private _socket?: WebSocket;\n private _rpc?: ProtoRpcPeer<C>;\n private readonly _connectTrigger = new Trigger();\n\n readonly connected = new Event();\n readonly disconnected = new Event();\n readonly error = new Event<Error>();\n\n constructor(private readonly _params: WebsocketRpcClientParams<C, S>) {\n this._rpc = createProtoRpcPeer({\n requested: this._params.requested,\n exposed: this._params.exposed,\n handlers: this._params.handlers,\n port: {\n send: (msg) => {\n this._socket!.send(msg);\n },\n subscribe: (cb) => {\n this._socket!.onmessage = async (msg: WebSocket.MessageEvent) => {\n if (typeof Blob !== 'undefined' && msg.data instanceof Blob) {\n cb(Buffer.from(await msg.data.arrayBuffer()));\n } else {\n cb(msg.data as any);\n }\n };\n },\n },\n });\n }\n\n @logInfo\n get url() {\n return this._params.url;\n }\n\n async open() {\n if (this._params.authenticationToken) {\n this._socket = new WebSocketWithTokenAuth(this._params.url, this._params.authenticationToken);\n } else {\n this._socket = new WebSocket(this._params.url);\n }\n this._socket.onopen = async () => {\n log('Socket open');\n try {\n await this._rpc!.open();\n log(`RPC open ${this._params.url}`);\n this.connected.emit();\n this._connectTrigger.wake();\n } catch (err: any) {\n this.error.emit(err);\n }\n };\n\n this._socket.onclose = async () => {\n log(`Disconnected ${this._params.url}`);\n this.disconnected.emit();\n await this.close();\n };\n\n this._socket.onerror = (event: WebSocket.ErrorEvent) => {\n // Browsers do not include the error message in the event object, so we cannot discern 401 errors from other errors.\n log.error(event.message ?? 'Socket error', { url: this._params.url });\n const error = event.error ?? new Error(event.message);\n this.error.emit(error);\n this._connectTrigger.throw(error);\n };\n\n await this._connectTrigger.wait();\n }\n\n async close() {\n try {\n await this._rpc?.close();\n } catch (err) {\n log.catch(err);\n }\n this._socket?.close();\n }\n\n get rpc() {\n return this._rpc!.rpc;\n }\n}\n", "//\n// Copyright 2024 DXOS.org\n//\n\n// Implement WS header-based auth similar to https://github.com/kubernetes/kubernetes/commit/714f97d7baf4975ad3aa47735a868a81a984d1f0\n// https://stackoverflow.com/questions/4361173/http-headers-in-websockets-client-api/77060459#77060459\n\nimport { type IncomingMessage } from 'http';\nimport WebSocket from 'isomorphic-ws';\nimport { type Socket } from 'node:net';\n\nimport { log } from '@dxos/log';\n\nconst PROTOCOL_TOKEN_PREFIX = 'base64url.bearer.authorization.dxos.org';\n\n// TODO: implement middleware which removes the token from the request\n\nexport const authenticateRequestWithTokenAuth = (\n request: IncomingMessage,\n socket: Socket,\n upgradeHead: Buffer,\n token: string,\n cb: (request: IncomingMessage, socket: Socket, upgradeHead: Buffer) => void,\n) => {\n const protocolHeader = request.headers['sec-websocket-protocol'];\n\n if (!protocolHeader) {\n log('upgrade unauthorized, header missing', { header: request.headers['sec-websocket-protocol'] });\n socket.write('HTTP/1.1 401 Unauthorized\\r\\n\\r\\n');\n socket.destroy();\n return;\n }\n\n const tokenHeader = protocolHeader.replace(new RegExp(`^${PROTOCOL_TOKEN_PREFIX}.`), '');\n\n // padding characters are not allowed as a websocket protocol.\n const encodedToken = Buffer.from(token).toString('base64').replace(/=*$/, '');\n\n if (tokenHeader !== encodedToken) {\n log('upgrade unauthorized', { token, foo: encodedToken });\n socket.write('HTTP/1.1 401 Unauthorized\\r\\n\\r\\n');\n socket.destroy();\n return;\n }\n\n cb(request, socket, upgradeHead);\n};\n\nexport class WebSocketWithTokenAuth extends WebSocket {\n constructor(url: string, token: string) {\n const encodedToken = Buffer.from(token).toString('base64').replace(/=*$/, '');\n\n const wsProtocols = [`base64url.bearer.authorization.dxos.org.${encodedToken}`];\n super(url, wsProtocols);\n log('encodedToken', {\n encodedToken,\n });\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { type IncomingMessage } from 'http';\nimport WebSocket from 'isomorphic-ws';\nimport { type Socket } from 'node:net';\n\nimport { log } from '@dxos/log';\nimport { createProtoRpcPeer, type ProtoRpcPeer, type ProtoRpcPeerOptions } from '@dxos/rpc';\n\nexport type ConnectionInfo = {\n request: IncomingMessage;\n};\n\nexport type ConnectionHandler<C, S> = {\n onOpen?: (rpc: ProtoRpcPeer<C>) => Promise<void>;\n onClose?: (rpc: ProtoRpcPeer<C>) => Promise<void>;\n} & Pick<ProtoRpcPeerOptions<C, S>, 'requested' | 'exposed' | 'handlers'>;\n\nexport type WebsocketRpcServerParams<C, S> = {\n onConnection: (info: ConnectionInfo) => Promise<ConnectionHandler<C, S>>;\n} & WebSocket.ServerOptions;\n\nexport class WebsocketRpcServer<C, S> {\n private _server?: WebSocket.Server;\n\n constructor(private readonly _params: WebsocketRpcServerParams<C, S>) {}\n handleUpgrade(request: IncomingMessage, socket: Socket, head: Buffer) {\n this._server?.handleUpgrade(request, socket, head, (ws) => {\n this._server?.emit('connection', ws, request);\n });\n }\n\n async open() {\n this._server = new WebSocket.Server({\n ...this._params,\n });\n this._server.on('connection', async (socket, request) => {\n log('connection', { request });\n const info: ConnectionInfo = {\n request,\n };\n const { onOpen, onClose, ...options } = await this._params.onConnection(info);\n const rpc = createProtoRpcPeer<C, S>({\n ...options,\n port: {\n send: (msg) => {\n socket.send(msg);\n },\n subscribe: (cb) => {\n socket.onmessage = async (msg: WebSocket.MessageEvent) => {\n if (typeof Blob !== 'undefined' && msg.data instanceof Blob) {\n cb(Buffer.from(await msg.data.arrayBuffer()));\n } else {\n cb(msg.data as any);\n }\n };\n },\n },\n });\n\n await rpc.open();\n await onOpen?.(rpc);\n socket.onclose = async () => {\n await onClose?.(rpc);\n await rpc.close();\n };\n });\n }\n\n async close() {\n this._server?.close();\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["WebSocket", "Event", "Trigger", "log", "logInfo", "createProtoRpcPeer", "WebSocket", "log", "PROTOCOL_TOKEN_PREFIX", "authenticateRequestWithTokenAuth", "request", "socket", "upgradeHead", "token", "cb", "protocolHeader", "headers", "header", "write", "destroy", "tokenHeader", "replace", "RegExp", "encodedToken", "Buffer", "from", "toString", "foo", "WebSocketWithTokenAuth", "constructor", "url", "wsProtocols", "WebsocketRpcClient", "constructor", "_params", "_connectTrigger", "Trigger", "connected", "Event", "disconnected", "error", "_rpc", "createProtoRpcPeer", "requested", "exposed", "handlers", "port", "send", "msg", "_socket", "subscribe", "cb", "onmessage", "Blob", "data", "Buffer", "from", "arrayBuffer", "url", "open", "authenticationToken", "WebSocketWithTokenAuth", "WebSocket", "onopen", "log", "emit", "wake", "err", "onclose", "close", "onerror", "event", "message", "Error", "throw", "wait", "catch", "rpc", "logInfo", "WebSocket", "log", "createProtoRpcPeer", "WebsocketRpcServer", "constructor", "_params", "handleUpgrade", "request", "socket", "head", "_server", "ws", "emit", "open", "Server", "on", "info", "onOpen", "onClose", "options", "onConnection", "rpc", "port", "send", "msg", "subscribe", "cb", "onmessage", "Blob", "data", "Buffer", "from", "arrayBuffer", "onclose", "close"]
7
7
  }
@@ -1 +1 @@
1
- {"inputs":{"inject-globals:@inject-globals":{"bytes":384,"imports":[{"path":"@dxos/node-std/inject-globals","kind":"import-statement","external":true}],"format":"esm"},"packages/core/mesh/websocket-rpc/src/client.ts":{"bytes":10810,"imports":[{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true},{"path":"@inject-globals","kind":"import-statement","external":true}],"format":"esm"},"packages/core/mesh/websocket-rpc/src/server.ts":{"bytes":6378,"imports":[{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true},{"path":"@inject-globals","kind":"import-statement","external":true}],"format":"esm"},"packages/core/mesh/websocket-rpc/src/index.ts":{"bytes":552,"imports":[{"path":"packages/core/mesh/websocket-rpc/src/client.ts","kind":"import-statement","original":"./client"},{"path":"packages/core/mesh/websocket-rpc/src/server.ts","kind":"import-statement","original":"./server"},{"path":"@inject-globals","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"packages/core/mesh/websocket-rpc/dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":8047},"packages/core/mesh/websocket-rpc/dist/lib/browser/index.mjs":{"imports":[{"path":"@dxos/node-std/inject-globals","kind":"import-statement","external":true},{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true},{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true}],"exports":["WebsocketRpcClient","WebsocketRpcServer"],"entryPoint":"packages/core/mesh/websocket-rpc/src/index.ts","inputs":{"inject-globals:@inject-globals":{"bytesInOutput":79},"packages/core/mesh/websocket-rpc/src/client.ts":{"bytesInOutput":3270},"packages/core/mesh/websocket-rpc/src/index.ts":{"bytesInOutput":0},"packages/core/mesh/websocket-rpc/src/server.ts":{"bytesInOutput":1474}},"bytes":5083}}}
1
+ {"inputs":{"inject-globals:@inject-globals":{"bytes":384,"imports":[{"path":"@dxos/node-std/inject-globals","kind":"import-statement","external":true}],"format":"esm"},"packages/core/mesh/websocket-rpc/src/token-auth.ts":{"bytes":6800,"imports":[{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@inject-globals","kind":"import-statement","external":true}],"format":"esm"},"packages/core/mesh/websocket-rpc/src/client.ts":{"bytes":12028,"imports":[{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true},{"path":"packages/core/mesh/websocket-rpc/src/token-auth.ts","kind":"import-statement","original":"./token-auth"},{"path":"@inject-globals","kind":"import-statement","external":true}],"format":"esm"},"packages/core/mesh/websocket-rpc/src/server.ts":{"bytes":7161,"imports":[{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true},{"path":"@inject-globals","kind":"import-statement","external":true}],"format":"esm"},"packages/core/mesh/websocket-rpc/src/index.ts":{"bytes":646,"imports":[{"path":"packages/core/mesh/websocket-rpc/src/client.ts","kind":"import-statement","original":"./client"},{"path":"packages/core/mesh/websocket-rpc/src/server.ts","kind":"import-statement","original":"./server"},{"path":"packages/core/mesh/websocket-rpc/src/token-auth.ts","kind":"import-statement","original":"./token-auth"},{"path":"@inject-globals","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"packages/core/mesh/websocket-rpc/dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":12349},"packages/core/mesh/websocket-rpc/dist/lib/browser/index.mjs":{"imports":[{"path":"@dxos/node-std/inject-globals","kind":"import-statement","external":true},{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true},{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true}],"exports":["WebSocketWithTokenAuth","WebsocketRpcClient","WebsocketRpcServer","authenticateRequestWithTokenAuth"],"entryPoint":"packages/core/mesh/websocket-rpc/src/index.ts","inputs":{"inject-globals:@inject-globals":{"bytesInOutput":79},"packages/core/mesh/websocket-rpc/src/client.ts":{"bytesInOutput":3457},"packages/core/mesh/websocket-rpc/src/token-auth.ts":{"bytesInOutput":1669},"packages/core/mesh/websocket-rpc/src/index.ts":{"bytesInOutput":0},"packages/core/mesh/websocket-rpc/src/server.ts":{"bytesInOutput":1645}},"bytes":7278}}}
@@ -28,8 +28,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
  var node_exports = {};
30
30
  __export(node_exports, {
31
+ WebSocketWithTokenAuth: () => WebSocketWithTokenAuth,
31
32
  WebsocketRpcClient: () => WebsocketRpcClient,
32
- WebsocketRpcServer: () => WebsocketRpcServer
33
+ WebsocketRpcServer: () => WebsocketRpcServer,
34
+ authenticateRequestWithTokenAuth: () => authenticateRequestWithTokenAuth
33
35
  });
34
36
  module.exports = __toCommonJS(node_exports);
35
37
  var import_isomorphic_ws = __toESM(require("isomorphic-ws"));
@@ -38,7 +40,61 @@ var import_log = require("@dxos/log");
38
40
  var import_rpc = require("@dxos/rpc");
39
41
  var import_isomorphic_ws2 = __toESM(require("isomorphic-ws"));
40
42
  var import_log2 = require("@dxos/log");
43
+ var import_isomorphic_ws3 = __toESM(require("isomorphic-ws"));
44
+ var import_log3 = require("@dxos/log");
41
45
  var import_rpc2 = require("@dxos/rpc");
46
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/mesh/websocket-rpc/src/token-auth.ts";
47
+ var PROTOCOL_TOKEN_PREFIX = "base64url.bearer.authorization.dxos.org";
48
+ var authenticateRequestWithTokenAuth = (request, socket, upgradeHead, token, cb) => {
49
+ const protocolHeader = request.headers["sec-websocket-protocol"];
50
+ if (!protocolHeader) {
51
+ (0, import_log2.log)("upgrade unauthorized, header missing", {
52
+ header: request.headers["sec-websocket-protocol"]
53
+ }, {
54
+ F: __dxlog_file,
55
+ L: 28,
56
+ S: void 0,
57
+ C: (f, a) => f(...a)
58
+ });
59
+ socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
60
+ socket.destroy();
61
+ return;
62
+ }
63
+ const tokenHeader = protocolHeader.replace(new RegExp(`^${PROTOCOL_TOKEN_PREFIX}.`), "");
64
+ const encodedToken = Buffer.from(token).toString("base64").replace(/=*$/, "");
65
+ if (tokenHeader !== encodedToken) {
66
+ (0, import_log2.log)("upgrade unauthorized", {
67
+ token,
68
+ foo: encodedToken
69
+ }, {
70
+ F: __dxlog_file,
71
+ L: 40,
72
+ S: void 0,
73
+ C: (f, a) => f(...a)
74
+ });
75
+ socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
76
+ socket.destroy();
77
+ return;
78
+ }
79
+ cb(request, socket, upgradeHead);
80
+ };
81
+ var WebSocketWithTokenAuth = class extends import_isomorphic_ws2.default {
82
+ constructor(url, token) {
83
+ const encodedToken = Buffer.from(token).toString("base64").replace(/=*$/, "");
84
+ const wsProtocols = [
85
+ `base64url.bearer.authorization.dxos.org.${encodedToken}`
86
+ ];
87
+ super(url, wsProtocols);
88
+ (0, import_log2.log)("encodedToken", {
89
+ encodedToken
90
+ }, {
91
+ F: __dxlog_file,
92
+ L: 55,
93
+ S: this,
94
+ C: (f, a) => f(...a)
95
+ });
96
+ }
97
+ };
42
98
  function _ts_decorate(decorators, target, key, desc) {
43
99
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
44
100
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
@@ -49,7 +105,7 @@ function _ts_decorate(decorators, target, key, desc) {
49
105
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
50
106
  return c > 3 && r && Object.defineProperty(target, key, r), r;
51
107
  }
52
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/mesh/websocket-rpc/src/client.ts";
108
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/mesh/websocket-rpc/src/client.ts";
53
109
  var WebsocketRpcClient = class {
54
110
  constructor(_params) {
55
111
  this._params = _params;
@@ -81,19 +137,23 @@ var WebsocketRpcClient = class {
81
137
  return this._params.url;
82
138
  }
83
139
  async open() {
84
- this._socket = new import_isomorphic_ws.default(this._params.url);
140
+ if (this._params.authenticationToken) {
141
+ this._socket = new WebSocketWithTokenAuth(this._params.url, this._params.authenticationToken);
142
+ } else {
143
+ this._socket = new import_isomorphic_ws.default(this._params.url);
144
+ }
85
145
  this._socket.onopen = async () => {
86
146
  (0, import_log.log)("Socket open", void 0, {
87
- F: __dxlog_file,
88
- L: 55,
147
+ F: __dxlog_file2,
148
+ L: 61,
89
149
  S: this,
90
150
  C: (f, a) => f(...a)
91
151
  });
92
152
  try {
93
153
  await this._rpc.open();
94
154
  (0, import_log.log)(`RPC open ${this._params.url}`, void 0, {
95
- F: __dxlog_file,
96
- L: 58,
155
+ F: __dxlog_file2,
156
+ L: 64,
97
157
  S: this,
98
158
  C: (f, a) => f(...a)
99
159
  });
@@ -105,8 +165,8 @@ var WebsocketRpcClient = class {
105
165
  };
106
166
  this._socket.onclose = async () => {
107
167
  (0, import_log.log)(`Disconnected ${this._params.url}`, void 0, {
108
- F: __dxlog_file,
109
- L: 67,
168
+ F: __dxlog_file2,
169
+ L: 73,
110
170
  S: this,
111
171
  C: (f, a) => f(...a)
112
172
  });
@@ -117,8 +177,8 @@ var WebsocketRpcClient = class {
117
177
  import_log.log.error(event.message ?? "Socket error", {
118
178
  url: this._params.url
119
179
  }, {
120
- F: __dxlog_file,
121
- L: 73,
180
+ F: __dxlog_file2,
181
+ L: 80,
122
182
  S: this,
123
183
  C: (f, a) => f(...a)
124
184
  });
@@ -133,8 +193,8 @@ var WebsocketRpcClient = class {
133
193
  await this._rpc?.close();
134
194
  } catch (err) {
135
195
  import_log.log.catch(err, void 0, {
136
- F: __dxlog_file,
137
- L: 86,
196
+ F: __dxlog_file2,
197
+ L: 93,
138
198
  S: this,
139
199
  C: (f, a) => f(...a)
140
200
  });
@@ -148,21 +208,26 @@ var WebsocketRpcClient = class {
148
208
  _ts_decorate([
149
209
  import_log.logInfo
150
210
  ], WebsocketRpcClient.prototype, "url", null);
151
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/mesh/websocket-rpc/src/server.ts";
211
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/mesh/websocket-rpc/src/server.ts";
152
212
  var WebsocketRpcServer = class {
153
213
  constructor(_params) {
154
214
  this._params = _params;
155
215
  }
216
+ handleUpgrade(request, socket, head) {
217
+ this._server?.handleUpgrade(request, socket, head, (ws) => {
218
+ this._server?.emit("connection", ws, request);
219
+ });
220
+ }
156
221
  async open() {
157
- this._server = new import_isomorphic_ws2.default.Server({
222
+ this._server = new import_isomorphic_ws3.default.Server({
158
223
  ...this._params
159
224
  });
160
225
  this._server.on("connection", async (socket, request) => {
161
- (0, import_log2.log)("connection", {
226
+ (0, import_log3.log)("connection", {
162
227
  request
163
228
  }, {
164
- F: __dxlog_file2,
165
- L: 34,
229
+ F: __dxlog_file3,
230
+ L: 40,
166
231
  S: this,
167
232
  C: (f, a) => f(...a)
168
233
  });
@@ -201,7 +266,9 @@ var WebsocketRpcServer = class {
201
266
  };
202
267
  // Annotate the CommonJS export names for ESM import in node:
203
268
  0 && (module.exports = {
269
+ WebSocketWithTokenAuth,
204
270
  WebsocketRpcClient,
205
- WebsocketRpcServer
271
+ WebsocketRpcServer,
272
+ authenticateRequestWithTokenAuth
206
273
  });
207
274
  //# sourceMappingURL=index.cjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../../src/client.ts", "../../../src/server.ts"],
4
- "sourcesContent": ["//\n// Copyright 2022 DXOS.org\n//\n\nimport WebSocket from 'isomorphic-ws';\n\nimport { Event, Trigger } from '@dxos/async';\nimport { log, logInfo } from '@dxos/log';\nimport { createProtoRpcPeer, type ProtoRpcPeer, type ProtoRpcPeerOptions } from '@dxos/rpc';\n\nexport type WebsocketRpcClientParams<C, S> = {\n url: string;\n} & Pick<ProtoRpcPeerOptions<C, S>, 'requested' | 'exposed' | 'handlers'>;\n\nexport class WebsocketRpcClient<C, S> {\n private _socket?: WebSocket;\n private _rpc?: ProtoRpcPeer<C>;\n private readonly _connectTrigger = new Trigger();\n\n readonly connected = new Event();\n readonly disconnected = new Event();\n readonly error = new Event<Error>();\n\n constructor(private readonly _params: WebsocketRpcClientParams<C, S>) {\n this._rpc = createProtoRpcPeer({\n requested: this._params.requested,\n exposed: this._params.exposed,\n handlers: this._params.handlers,\n port: {\n send: (msg) => {\n this._socket!.send(msg);\n },\n subscribe: (cb) => {\n this._socket!.onmessage = async (msg: WebSocket.MessageEvent) => {\n if (typeof Blob !== 'undefined' && msg.data instanceof Blob) {\n cb(Buffer.from(await msg.data.arrayBuffer()));\n } else {\n cb(msg.data as any);\n }\n };\n },\n },\n });\n }\n\n @logInfo\n get url() {\n return this._params.url;\n }\n\n async open() {\n this._socket = new WebSocket(this._params.url);\n\n this._socket.onopen = async () => {\n log('Socket open');\n try {\n await this._rpc!.open();\n log(`RPC open ${this._params.url}`);\n this.connected.emit();\n this._connectTrigger.wake();\n } catch (err: any) {\n this.error.emit(err);\n }\n };\n\n this._socket.onclose = async () => {\n log(`Disconnected ${this._params.url}`);\n this.disconnected.emit();\n await this.close();\n };\n\n this._socket.onerror = (event: WebSocket.ErrorEvent) => {\n log.error(event.message ?? 'Socket error', { url: this._params.url });\n const error = event.error ?? new Error(event.message);\n this.error.emit(error);\n this._connectTrigger.throw(error);\n };\n\n await this._connectTrigger.wait();\n }\n\n async close() {\n try {\n await this._rpc?.close();\n } catch (err) {\n log.catch(err);\n }\n this._socket?.close();\n }\n\n get rpc() {\n return this._rpc!.rpc;\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { type IncomingMessage } from 'http';\nimport WebSocket from 'isomorphic-ws';\n\nimport { log } from '@dxos/log';\nimport { createProtoRpcPeer, type ProtoRpcPeer, type ProtoRpcPeerOptions } from '@dxos/rpc';\n\nexport type ConnectionInfo = {\n request: IncomingMessage;\n};\n\nexport type ConnectionHandler<C, S> = {\n onOpen?: (rpc: ProtoRpcPeer<C>) => Promise<void>;\n onClose?: (rpc: ProtoRpcPeer<C>) => Promise<void>;\n} & Pick<ProtoRpcPeerOptions<C, S>, 'requested' | 'exposed' | 'handlers'>;\n\nexport type WebsocketRpcServerParams<C, S> = {\n onConnection: (info: ConnectionInfo) => Promise<ConnectionHandler<C, S>>;\n} & WebSocket.ServerOptions;\n\nexport class WebsocketRpcServer<C, S> {\n private _server?: WebSocket.Server;\n\n constructor(private readonly _params: WebsocketRpcServerParams<C, S>) {}\n\n async open() {\n this._server = new WebSocket.Server({\n ...this._params,\n });\n this._server.on('connection', async (socket, request) => {\n log('connection', { request });\n const info: ConnectionInfo = {\n request,\n };\n const { onOpen, onClose, ...options } = await this._params.onConnection(info);\n const rpc = createProtoRpcPeer<C, S>({\n ...options,\n port: {\n send: (msg) => {\n socket.send(msg);\n },\n subscribe: (cb) => {\n socket.onmessage = async (msg: WebSocket.MessageEvent) => {\n if (typeof Blob !== 'undefined' && msg.data instanceof Blob) {\n cb(Buffer.from(await msg.data.arrayBuffer()));\n } else {\n cb(msg.data as any);\n }\n };\n },\n },\n });\n\n await rpc.open();\n await onOpen?.(rpc);\n socket.onclose = async () => {\n await onClose?.(rpc);\n await rpc.close();\n };\n });\n }\n\n async close() {\n this._server?.close();\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["import_isomorphic_ws", "import_log", "import_rpc", "WebsocketRpcClient", "constructor", "_params", "_connectTrigger", "Trigger", "connected", "Event", "disconnected", "error", "_rpc", "createProtoRpcPeer", "requested", "exposed", "handlers", "port", "send", "msg", "_socket", "subscribe", "cb", "onmessage", "Blob", "data", "Buffer", "from", "arrayBuffer", "url", "open", "WebSocket", "onopen", "log", "emit", "wake", "err", "onclose", "close", "onerror", "event", "message", "Error", "throw", "wait", "catch", "rpc", "logInfo", "WebsocketRpcServer", "_server", "Server", "on", "socket", "request", "info", "onOpen", "onClose", "options", "onConnection"]
3
+ "sources": ["../../../src/client.ts", "../../../src/token-auth.ts", "../../../src/server.ts"],
4
+ "sourcesContent": ["//\n// Copyright 2022 DXOS.org\n//\n\nimport WebSocket from 'isomorphic-ws';\n\nimport { Event, Trigger } from '@dxos/async';\nimport { log, logInfo } from '@dxos/log';\nimport { createProtoRpcPeer, type ProtoRpcPeer, type ProtoRpcPeerOptions } from '@dxos/rpc';\n\nimport { WebSocketWithTokenAuth } from './token-auth';\n\nexport type WebsocketRpcClientParams<C, S> = {\n url: string;\n authenticationToken?: string;\n} & Pick<ProtoRpcPeerOptions<C, S>, 'requested' | 'exposed' | 'handlers'>;\n\nexport class WebsocketRpcClient<C, S> {\n private _socket?: WebSocket;\n private _rpc?: ProtoRpcPeer<C>;\n private readonly _connectTrigger = new Trigger();\n\n readonly connected = new Event();\n readonly disconnected = new Event();\n readonly error = new Event<Error>();\n\n constructor(private readonly _params: WebsocketRpcClientParams<C, S>) {\n this._rpc = createProtoRpcPeer({\n requested: this._params.requested,\n exposed: this._params.exposed,\n handlers: this._params.handlers,\n port: {\n send: (msg) => {\n this._socket!.send(msg);\n },\n subscribe: (cb) => {\n this._socket!.onmessage = async (msg: WebSocket.MessageEvent) => {\n if (typeof Blob !== 'undefined' && msg.data instanceof Blob) {\n cb(Buffer.from(await msg.data.arrayBuffer()));\n } else {\n cb(msg.data as any);\n }\n };\n },\n },\n });\n }\n\n @logInfo\n get url() {\n return this._params.url;\n }\n\n async open() {\n if (this._params.authenticationToken) {\n this._socket = new WebSocketWithTokenAuth(this._params.url, this._params.authenticationToken);\n } else {\n this._socket = new WebSocket(this._params.url);\n }\n this._socket.onopen = async () => {\n log('Socket open');\n try {\n await this._rpc!.open();\n log(`RPC open ${this._params.url}`);\n this.connected.emit();\n this._connectTrigger.wake();\n } catch (err: any) {\n this.error.emit(err);\n }\n };\n\n this._socket.onclose = async () => {\n log(`Disconnected ${this._params.url}`);\n this.disconnected.emit();\n await this.close();\n };\n\n this._socket.onerror = (event: WebSocket.ErrorEvent) => {\n // Browsers do not include the error message in the event object, so we cannot discern 401 errors from other errors.\n log.error(event.message ?? 'Socket error', { url: this._params.url });\n const error = event.error ?? new Error(event.message);\n this.error.emit(error);\n this._connectTrigger.throw(error);\n };\n\n await this._connectTrigger.wait();\n }\n\n async close() {\n try {\n await this._rpc?.close();\n } catch (err) {\n log.catch(err);\n }\n this._socket?.close();\n }\n\n get rpc() {\n return this._rpc!.rpc;\n }\n}\n", "//\n// Copyright 2024 DXOS.org\n//\n\n// Implement WS header-based auth similar to https://github.com/kubernetes/kubernetes/commit/714f97d7baf4975ad3aa47735a868a81a984d1f0\n// https://stackoverflow.com/questions/4361173/http-headers-in-websockets-client-api/77060459#77060459\n\nimport { type IncomingMessage } from 'http';\nimport WebSocket from 'isomorphic-ws';\nimport { type Socket } from 'node:net';\n\nimport { log } from '@dxos/log';\n\nconst PROTOCOL_TOKEN_PREFIX = 'base64url.bearer.authorization.dxos.org';\n\n// TODO: implement middleware which removes the token from the request\n\nexport const authenticateRequestWithTokenAuth = (\n request: IncomingMessage,\n socket: Socket,\n upgradeHead: Buffer,\n token: string,\n cb: (request: IncomingMessage, socket: Socket, upgradeHead: Buffer) => void,\n) => {\n const protocolHeader = request.headers['sec-websocket-protocol'];\n\n if (!protocolHeader) {\n log('upgrade unauthorized, header missing', { header: request.headers['sec-websocket-protocol'] });\n socket.write('HTTP/1.1 401 Unauthorized\\r\\n\\r\\n');\n socket.destroy();\n return;\n }\n\n const tokenHeader = protocolHeader.replace(new RegExp(`^${PROTOCOL_TOKEN_PREFIX}.`), '');\n\n // padding characters are not allowed as a websocket protocol.\n const encodedToken = Buffer.from(token).toString('base64').replace(/=*$/, '');\n\n if (tokenHeader !== encodedToken) {\n log('upgrade unauthorized', { token, foo: encodedToken });\n socket.write('HTTP/1.1 401 Unauthorized\\r\\n\\r\\n');\n socket.destroy();\n return;\n }\n\n cb(request, socket, upgradeHead);\n};\n\nexport class WebSocketWithTokenAuth extends WebSocket {\n constructor(url: string, token: string) {\n const encodedToken = Buffer.from(token).toString('base64').replace(/=*$/, '');\n\n const wsProtocols = [`base64url.bearer.authorization.dxos.org.${encodedToken}`];\n super(url, wsProtocols);\n log('encodedToken', {\n encodedToken,\n });\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport { type IncomingMessage } from 'http';\nimport WebSocket from 'isomorphic-ws';\nimport { type Socket } from 'node:net';\n\nimport { log } from '@dxos/log';\nimport { createProtoRpcPeer, type ProtoRpcPeer, type ProtoRpcPeerOptions } from '@dxos/rpc';\n\nexport type ConnectionInfo = {\n request: IncomingMessage;\n};\n\nexport type ConnectionHandler<C, S> = {\n onOpen?: (rpc: ProtoRpcPeer<C>) => Promise<void>;\n onClose?: (rpc: ProtoRpcPeer<C>) => Promise<void>;\n} & Pick<ProtoRpcPeerOptions<C, S>, 'requested' | 'exposed' | 'handlers'>;\n\nexport type WebsocketRpcServerParams<C, S> = {\n onConnection: (info: ConnectionInfo) => Promise<ConnectionHandler<C, S>>;\n} & WebSocket.ServerOptions;\n\nexport class WebsocketRpcServer<C, S> {\n private _server?: WebSocket.Server;\n\n constructor(private readonly _params: WebsocketRpcServerParams<C, S>) {}\n handleUpgrade(request: IncomingMessage, socket: Socket, head: Buffer) {\n this._server?.handleUpgrade(request, socket, head, (ws) => {\n this._server?.emit('connection', ws, request);\n });\n }\n\n async open() {\n this._server = new WebSocket.Server({\n ...this._params,\n });\n this._server.on('connection', async (socket, request) => {\n log('connection', { request });\n const info: ConnectionInfo = {\n request,\n };\n const { onOpen, onClose, ...options } = await this._params.onConnection(info);\n const rpc = createProtoRpcPeer<C, S>({\n ...options,\n port: {\n send: (msg) => {\n socket.send(msg);\n },\n subscribe: (cb) => {\n socket.onmessage = async (msg: WebSocket.MessageEvent) => {\n if (typeof Blob !== 'undefined' && msg.data instanceof Blob) {\n cb(Buffer.from(await msg.data.arrayBuffer()));\n } else {\n cb(msg.data as any);\n }\n };\n },\n },\n });\n\n await rpc.open();\n await onOpen?.(rpc);\n socket.onclose = async () => {\n await onClose?.(rpc);\n await rpc.close();\n };\n });\n }\n\n async close() {\n this._server?.close();\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["import_isomorphic_ws", "import_log", "import_rpc", "PROTOCOL_TOKEN_PREFIX", "authenticateRequestWithTokenAuth", "request", "socket", "upgradeHead", "token", "cb", "protocolHeader", "headers", "log", "header", "write", "destroy", "tokenHeader", "replace", "RegExp", "encodedToken", "Buffer", "from", "toString", "foo", "WebSocketWithTokenAuth", "WebSocket", "constructor", "url", "wsProtocols", "WebsocketRpcClient", "_params", "_connectTrigger", "Trigger", "connected", "Event", "disconnected", "error", "_rpc", "createProtoRpcPeer", "requested", "exposed", "handlers", "port", "send", "msg", "_socket", "subscribe", "onmessage", "Blob", "data", "arrayBuffer", "open", "authenticationToken", "onopen", "emit", "wake", "err", "onclose", "close", "onerror", "event", "message", "Error", "throw", "wait", "catch", "rpc", "logInfo", "WebsocketRpcServer", "handleUpgrade", "head", "_server", "ws", "Server", "on", "info", "onOpen", "onClose", "options", "onConnection"]
7
7
  }
@@ -1 +1 @@
1
- {"inputs":{"packages/core/mesh/websocket-rpc/src/client.ts":{"bytes":10810,"imports":[{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true}],"format":"esm"},"packages/core/mesh/websocket-rpc/src/server.ts":{"bytes":6378,"imports":[{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true}],"format":"esm"},"packages/core/mesh/websocket-rpc/src/index.ts":{"bytes":552,"imports":[{"path":"packages/core/mesh/websocket-rpc/src/client.ts","kind":"import-statement","original":"./client"},{"path":"packages/core/mesh/websocket-rpc/src/server.ts","kind":"import-statement","original":"./server"}],"format":"esm"}},"outputs":{"packages/core/mesh/websocket-rpc/dist/lib/node/index.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":8038},"packages/core/mesh/websocket-rpc/dist/lib/node/index.cjs":{"imports":[{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true},{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true}],"exports":["WebsocketRpcClient","WebsocketRpcServer"],"entryPoint":"packages/core/mesh/websocket-rpc/src/index.ts","inputs":{"packages/core/mesh/websocket-rpc/src/client.ts":{"bytesInOutput":3270},"packages/core/mesh/websocket-rpc/src/index.ts":{"bytesInOutput":0},"packages/core/mesh/websocket-rpc/src/server.ts":{"bytesInOutput":1474}},"bytes":4935}}}
1
+ {"inputs":{"packages/core/mesh/websocket-rpc/src/token-auth.ts":{"bytes":6800,"imports":[{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true}],"format":"esm"},"packages/core/mesh/websocket-rpc/src/client.ts":{"bytes":12028,"imports":[{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true},{"path":"packages/core/mesh/websocket-rpc/src/token-auth.ts","kind":"import-statement","original":"./token-auth"}],"format":"esm"},"packages/core/mesh/websocket-rpc/src/server.ts":{"bytes":7161,"imports":[{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true}],"format":"esm"},"packages/core/mesh/websocket-rpc/src/index.ts":{"bytes":646,"imports":[{"path":"packages/core/mesh/websocket-rpc/src/client.ts","kind":"import-statement","original":"./client"},{"path":"packages/core/mesh/websocket-rpc/src/server.ts","kind":"import-statement","original":"./server"},{"path":"packages/core/mesh/websocket-rpc/src/token-auth.ts","kind":"import-statement","original":"./token-auth"}],"format":"esm"}},"outputs":{"packages/core/mesh/websocket-rpc/dist/lib/node/index.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":12340},"packages/core/mesh/websocket-rpc/dist/lib/node/index.cjs":{"imports":[{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true},{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"isomorphic-ws","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/rpc","kind":"import-statement","external":true}],"exports":["WebSocketWithTokenAuth","WebsocketRpcClient","WebsocketRpcServer","authenticateRequestWithTokenAuth"],"entryPoint":"packages/core/mesh/websocket-rpc/src/index.ts","inputs":{"packages/core/mesh/websocket-rpc/src/client.ts":{"bytesInOutput":3457},"packages/core/mesh/websocket-rpc/src/token-auth.ts":{"bytesInOutput":1669},"packages/core/mesh/websocket-rpc/src/index.ts":{"bytesInOutput":0},"packages/core/mesh/websocket-rpc/src/server.ts":{"bytesInOutput":1645}},"bytes":7130}}}
@@ -2,6 +2,7 @@ import { Event } from '@dxos/async';
2
2
  import { type ProtoRpcPeerOptions } from '@dxos/rpc';
3
3
  export type WebsocketRpcClientParams<C, S> = {
4
4
  url: string;
5
+ authenticationToken?: string;
5
6
  } & Pick<ProtoRpcPeerOptions<C, S>, 'requested' | 'exposed' | 'handlers'>;
6
7
  export declare class WebsocketRpcClient<C, S> {
7
8
  private readonly _params;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/client.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,EAAW,MAAM,aAAa,CAAC;AAE7C,OAAO,EAAyC,KAAK,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAE5F,MAAM,MAAM,wBAAwB,CAAC,CAAC,EAAE,CAAC,IAAI;IAC3C,GAAG,EAAE,MAAM,CAAC;CACb,GAAG,IAAI,CAAC,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,GAAG,SAAS,GAAG,UAAU,CAAC,CAAC;AAE1E,qBAAa,kBAAkB,CAAC,CAAC,EAAE,CAAC;IAStB,OAAO,CAAC,QAAQ,CAAC,OAAO;IARpC,OAAO,CAAC,OAAO,CAAC,CAAY;IAC5B,OAAO,CAAC,IAAI,CAAC,CAAkB;IAC/B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAiB;IAEjD,QAAQ,CAAC,SAAS,cAAe;IACjC,QAAQ,CAAC,YAAY,cAAe;IACpC,QAAQ,CAAC,KAAK,eAAsB;gBAEP,OAAO,EAAE,wBAAwB,CAAC,CAAC,EAAE,CAAC,CAAC;IAsBpE,IACI,GAAG,WAEN;IAEK,IAAI;IA+BJ,KAAK;IASX,IAAI,GAAG,MAEN;CACF"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/client.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,EAAW,MAAM,aAAa,CAAC;AAE7C,OAAO,EAAyC,KAAK,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAI5F,MAAM,MAAM,wBAAwB,CAAC,CAAC,EAAE,CAAC,IAAI;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,GAAG,IAAI,CAAC,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,GAAG,SAAS,GAAG,UAAU,CAAC,CAAC;AAE1E,qBAAa,kBAAkB,CAAC,CAAC,EAAE,CAAC;IAStB,OAAO,CAAC,QAAQ,CAAC,OAAO;IARpC,OAAO,CAAC,OAAO,CAAC,CAAY;IAC5B,OAAO,CAAC,IAAI,CAAC,CAAkB;IAC/B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAiB;IAEjD,QAAQ,CAAC,SAAS,cAAe;IACjC,QAAQ,CAAC,YAAY,cAAe;IACpC,QAAQ,CAAC,KAAK,eAAsB;gBAEP,OAAO,EAAE,wBAAwB,CAAC,CAAC,EAAE,CAAC,CAAC;IAsBpE,IACI,GAAG,WAEN;IAEK,IAAI;IAmCJ,KAAK;IASX,IAAI,GAAG,MAEN;CACF"}
@@ -1,3 +1,4 @@
1
1
  export * from './client';
2
2
  export * from './server';
3
+ export * from './token-auth';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC"}
@@ -1,6 +1,9 @@
1
1
  /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ /// <reference types="node" />
2
4
  import { type IncomingMessage } from 'http';
3
5
  import WebSocket from 'isomorphic-ws';
6
+ import { type Socket } from 'node:net';
4
7
  import { type ProtoRpcPeer, type ProtoRpcPeerOptions } from '@dxos/rpc';
5
8
  export type ConnectionInfo = {
6
9
  request: IncomingMessage;
@@ -16,6 +19,7 @@ export declare class WebsocketRpcServer<C, S> {
16
19
  private readonly _params;
17
20
  private _server?;
18
21
  constructor(_params: WebsocketRpcServerParams<C, S>);
22
+ handleUpgrade(request: IncomingMessage, socket: Socket, head: Buffer): void;
19
23
  open(): Promise<void>;
20
24
  close(): Promise<void>;
21
25
  }
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/server.ts"],"names":[],"mappings":";AAIA,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,MAAM,CAAC;AAC5C,OAAO,SAAS,MAAM,eAAe,CAAC;AAGtC,OAAO,EAAsB,KAAK,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAE5F,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,eAAe,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,CAAC,EAAE,CAAC,IAAI;IACpC,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD,GAAG,IAAI,CAAC,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,GAAG,SAAS,GAAG,UAAU,CAAC,CAAC;AAE1E,MAAM,MAAM,wBAAwB,CAAC,CAAC,EAAE,CAAC,IAAI;IAC3C,YAAY,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;CAC1E,GAAG,SAAS,CAAC,aAAa,CAAC;AAE5B,qBAAa,kBAAkB,CAAC,CAAC,EAAE,CAAC;IAGtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,OAAO,CAAC,OAAO,CAAC,CAAmB;gBAEN,OAAO,EAAE,wBAAwB,CAAC,CAAC,EAAE,CAAC,CAAC;IAE9D,IAAI;IAqCJ,KAAK;CAGZ"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/server.ts"],"names":[],"mappings":";;;AAIA,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,MAAM,CAAC;AAC5C,OAAO,SAAS,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AAGvC,OAAO,EAAsB,KAAK,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAE5F,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,eAAe,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,CAAC,EAAE,CAAC,IAAI;IACpC,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD,GAAG,IAAI,CAAC,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,GAAG,SAAS,GAAG,UAAU,CAAC,CAAC;AAE1E,MAAM,MAAM,wBAAwB,CAAC,CAAC,EAAE,CAAC,IAAI;IAC3C,YAAY,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;CAC1E,GAAG,SAAS,CAAC,aAAa,CAAC;AAE5B,qBAAa,kBAAkB,CAAC,CAAC,EAAE,CAAC;IAGtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,OAAO,CAAC,OAAO,CAAC,CAAmB;gBAEN,OAAO,EAAE,wBAAwB,CAAC,CAAC,EAAE,CAAC,CAAC;IACpE,aAAa,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAM9D,IAAI;IAqCJ,KAAK;CAGZ"}
@@ -0,0 +1,8 @@
1
+ import { type IncomingMessage } from 'http';
2
+ import WebSocket from 'isomorphic-ws';
3
+ import { type Socket } from 'node:net';
4
+ export declare const authenticateRequestWithTokenAuth: (request: IncomingMessage, socket: Socket, upgradeHead: Buffer, token: string, cb: (request: IncomingMessage, socket: Socket, upgradeHead: Buffer) => void) => void;
5
+ export declare class WebSocketWithTokenAuth extends WebSocket {
6
+ constructor(url: string, token: string);
7
+ }
8
+ //# sourceMappingURL=token-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-auth.d.ts","sourceRoot":"","sources":["../../../src/token-auth.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,MAAM,CAAC;AAC5C,OAAO,SAAS,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AAQvC,eAAO,MAAM,gCAAgC,YAClC,eAAe,UAChB,MAAM,eACD,MAAM,SACZ,MAAM,gBACC,eAAe,UAAU,MAAM,eAAe,MAAM,KAAK,IAAI,SAwB5E,CAAC;AAEF,qBAAa,sBAAuB,SAAQ,SAAS;gBACvC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;CASvC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/websocket-rpc",
3
- "version": "0.4.10-main.fa5a270",
3
+ "version": "0.4.10-main.fd8ea31",
4
4
  "description": "websocket-rpc",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -19,15 +19,15 @@
19
19
  "dependencies": {
20
20
  "isomorphic-ws": "^4.0.1",
21
21
  "ws": "^8.14.2",
22
- "@dxos/codec-protobuf": "0.4.10-main.fa5a270",
23
- "@dxos/context": "0.4.10-main.fa5a270",
24
- "@dxos/async": "0.4.10-main.fa5a270",
25
- "@dxos/keys": "0.4.10-main.fa5a270",
26
- "@dxos/log": "0.4.10-main.fa5a270",
27
- "@dxos/protocols": "0.4.10-main.fa5a270",
28
- "@dxos/rpc": "0.4.10-main.fa5a270",
29
- "@dxos/node-std": "0.4.10-main.fa5a270",
30
- "@dxos/util": "0.4.10-main.fa5a270"
22
+ "@dxos/async": "0.4.10-main.fd8ea31",
23
+ "@dxos/codec-protobuf": "0.4.10-main.fd8ea31",
24
+ "@dxos/context": "0.4.10-main.fd8ea31",
25
+ "@dxos/keys": "0.4.10-main.fd8ea31",
26
+ "@dxos/node-std": "0.4.10-main.fd8ea31",
27
+ "@dxos/log": "0.4.10-main.fd8ea31",
28
+ "@dxos/protocols": "0.4.10-main.fd8ea31",
29
+ "@dxos/rpc": "0.4.10-main.fd8ea31",
30
+ "@dxos/util": "0.4.10-main.fd8ea31"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^18.11.9",
package/src/client.ts CHANGED
@@ -8,8 +8,11 @@ import { Event, Trigger } from '@dxos/async';
8
8
  import { log, logInfo } from '@dxos/log';
9
9
  import { createProtoRpcPeer, type ProtoRpcPeer, type ProtoRpcPeerOptions } from '@dxos/rpc';
10
10
 
11
+ import { WebSocketWithTokenAuth } from './token-auth';
12
+
11
13
  export type WebsocketRpcClientParams<C, S> = {
12
14
  url: string;
15
+ authenticationToken?: string;
13
16
  } & Pick<ProtoRpcPeerOptions<C, S>, 'requested' | 'exposed' | 'handlers'>;
14
17
 
15
18
  export class WebsocketRpcClient<C, S> {
@@ -49,8 +52,11 @@ export class WebsocketRpcClient<C, S> {
49
52
  }
50
53
 
51
54
  async open() {
52
- this._socket = new WebSocket(this._params.url);
53
-
55
+ if (this._params.authenticationToken) {
56
+ this._socket = new WebSocketWithTokenAuth(this._params.url, this._params.authenticationToken);
57
+ } else {
58
+ this._socket = new WebSocket(this._params.url);
59
+ }
54
60
  this._socket.onopen = async () => {
55
61
  log('Socket open');
56
62
  try {
@@ -70,6 +76,7 @@ export class WebsocketRpcClient<C, S> {
70
76
  };
71
77
 
72
78
  this._socket.onerror = (event: WebSocket.ErrorEvent) => {
79
+ // Browsers do not include the error message in the event object, so we cannot discern 401 errors from other errors.
73
80
  log.error(event.message ?? 'Socket error', { url: this._params.url });
74
81
  const error = event.error ?? new Error(event.message);
75
82
  this.error.emit(error);
package/src/index.ts CHANGED
@@ -4,3 +4,4 @@
4
4
 
5
5
  export * from './client';
6
6
  export * from './server';
7
+ export * from './token-auth';
package/src/server.ts CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  import { type IncomingMessage } from 'http';
6
6
  import WebSocket from 'isomorphic-ws';
7
+ import { type Socket } from 'node:net';
7
8
 
8
9
  import { log } from '@dxos/log';
9
10
  import { createProtoRpcPeer, type ProtoRpcPeer, type ProtoRpcPeerOptions } from '@dxos/rpc';
@@ -25,6 +26,11 @@ export class WebsocketRpcServer<C, S> {
25
26
  private _server?: WebSocket.Server;
26
27
 
27
28
  constructor(private readonly _params: WebsocketRpcServerParams<C, S>) {}
29
+ handleUpgrade(request: IncomingMessage, socket: Socket, head: Buffer) {
30
+ this._server?.handleUpgrade(request, socket, head, (ws) => {
31
+ this._server?.emit('connection', ws, request);
32
+ });
33
+ }
28
34
 
29
35
  async open() {
30
36
  this._server = new WebSocket.Server({
@@ -0,0 +1,59 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ // Implement WS header-based auth similar to https://github.com/kubernetes/kubernetes/commit/714f97d7baf4975ad3aa47735a868a81a984d1f0
6
+ // https://stackoverflow.com/questions/4361173/http-headers-in-websockets-client-api/77060459#77060459
7
+
8
+ import { type IncomingMessage } from 'http';
9
+ import WebSocket from 'isomorphic-ws';
10
+ import { type Socket } from 'node:net';
11
+
12
+ import { log } from '@dxos/log';
13
+
14
+ const PROTOCOL_TOKEN_PREFIX = 'base64url.bearer.authorization.dxos.org';
15
+
16
+ // TODO: implement middleware which removes the token from the request
17
+
18
+ export const authenticateRequestWithTokenAuth = (
19
+ request: IncomingMessage,
20
+ socket: Socket,
21
+ upgradeHead: Buffer,
22
+ token: string,
23
+ cb: (request: IncomingMessage, socket: Socket, upgradeHead: Buffer) => void,
24
+ ) => {
25
+ const protocolHeader = request.headers['sec-websocket-protocol'];
26
+
27
+ if (!protocolHeader) {
28
+ log('upgrade unauthorized, header missing', { header: request.headers['sec-websocket-protocol'] });
29
+ socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
30
+ socket.destroy();
31
+ return;
32
+ }
33
+
34
+ const tokenHeader = protocolHeader.replace(new RegExp(`^${PROTOCOL_TOKEN_PREFIX}.`), '');
35
+
36
+ // padding characters are not allowed as a websocket protocol.
37
+ const encodedToken = Buffer.from(token).toString('base64').replace(/=*$/, '');
38
+
39
+ if (tokenHeader !== encodedToken) {
40
+ log('upgrade unauthorized', { token, foo: encodedToken });
41
+ socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
42
+ socket.destroy();
43
+ return;
44
+ }
45
+
46
+ cb(request, socket, upgradeHead);
47
+ };
48
+
49
+ export class WebSocketWithTokenAuth extends WebSocket {
50
+ constructor(url: string, token: string) {
51
+ const encodedToken = Buffer.from(token).toString('base64').replace(/=*$/, '');
52
+
53
+ const wsProtocols = [`base64url.bearer.authorization.dxos.org.${encodedToken}`];
54
+ super(url, wsProtocols);
55
+ log('encodedToken', {
56
+ encodedToken,
57
+ });
58
+ }
59
+ }