dsh-multi-chat 0.5.0 → 0.5.1
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.
package/package.json
CHANGED
|
@@ -119,6 +119,27 @@ cache-control: no-store\r
|
|
|
119
119
|
socket.end();
|
|
120
120
|
}
|
|
121
121
|
/**
|
|
122
|
+
* Send a 302 that mints a session cookie and drops the `token` query param.
|
|
123
|
+
* A `?token=` URL authenticates exactly one request, so on first arrival the
|
|
124
|
+
* gateway exchanges it for an HMAC-signed cookie and redirects to the same
|
|
125
|
+
* path without `token=` (keeps the secret out of the address bar / history).
|
|
126
|
+
*/
|
|
127
|
+
function respondSessionRedirect(socket, parsed, cookie) {
|
|
128
|
+
let location = parsed.target;
|
|
129
|
+
try {
|
|
130
|
+
const url = new URL(parsed.target, "http://gateway.local");
|
|
131
|
+
url.searchParams.delete("token");
|
|
132
|
+
location = url.pathname + url.search;
|
|
133
|
+
} catch {}
|
|
134
|
+
socket.write(`HTTP/1.1 302 Found\r
|
|
135
|
+
location: ${location}\r\nset-cookie: ${cookie}\r\ncontent-length: 0\r
|
|
136
|
+
connection: close\r
|
|
137
|
+
cache-control: no-store\r
|
|
138
|
+
\r
|
|
139
|
+
`);
|
|
140
|
+
socket.end();
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
122
143
|
* Rebuild the request head for the loopback target. Host and Origin point at
|
|
123
144
|
* `127.0.0.1:<target-port>` so the DSH `/api` browser-trust fence sees a local
|
|
124
145
|
* request. Non-upgrade requests get `connection: close` (fresh connection per
|
|
@@ -158,17 +179,16 @@ function startGateway(options) {
|
|
|
158
179
|
return false;
|
|
159
180
|
}
|
|
160
181
|
};
|
|
161
|
-
const
|
|
182
|
+
const authKind = (parsed) => {
|
|
162
183
|
const cookie = parsed.map.cookie ?? "";
|
|
163
184
|
const session = /(?:^|;\s*)dsh_gw_session=([^;]+)/.exec(cookie);
|
|
164
|
-
if (session !== null && verifySession(session[1] ?? "")) return
|
|
185
|
+
if (session !== null && verifySession(session[1] ?? "")) return "cookie";
|
|
165
186
|
const bearer = parsed.map.authorization ?? "";
|
|
166
|
-
if (bearer.startsWith("Bearer ") && bearer.slice(7) === token) return
|
|
187
|
+
if (bearer.startsWith("Bearer ") && bearer.slice(7) === token) return "bearer";
|
|
167
188
|
try {
|
|
168
|
-
|
|
169
|
-
} catch {
|
|
170
|
-
|
|
171
|
-
}
|
|
189
|
+
if (new URL(parsed.target, "http://gateway.local").searchParams.get("token") === token) return "query";
|
|
190
|
+
} catch {}
|
|
191
|
+
return "none";
|
|
172
192
|
};
|
|
173
193
|
const attempts = /* @__PURE__ */ new Map();
|
|
174
194
|
const isBlocked = (ip) => {
|
|
@@ -262,10 +282,15 @@ connection: close\r
|
|
|
262
282
|
handleLogin(socket, parsed, buffer.subarray(parsed.headBytes));
|
|
263
283
|
return;
|
|
264
284
|
}
|
|
265
|
-
|
|
285
|
+
const kind = authKind(parsed);
|
|
286
|
+
if (kind === "none") {
|
|
266
287
|
respondLogin(socket, name, path === "/" ? "/" : path);
|
|
267
288
|
return;
|
|
268
289
|
}
|
|
290
|
+
if (kind === "query") {
|
|
291
|
+
respondSessionRedirect(socket, parsed, `dsh_gw_session=${signSession(Date.now() + maxAgeHours * 36e5)}; Path=/; HttpOnly; SameSite=Strict; Max-Age=${maxAgeHours * 3600}`);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
269
294
|
const target = connect(targetPort, "127.0.0.1");
|
|
270
295
|
const rest = buffer.subarray(parsed.headBytes);
|
|
271
296
|
let connected = false;
|