spexcode 0.1.5 → 0.1.6
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 +1 -1
- package/spec-cli/src/gateway.ts +11 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spexcode",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "SpexCode — a spec-driven, self-developing dev tool. The `spex` CLI + spec server reads the .spec tree and its git history, and serves the dashboard.",
|
|
6
6
|
"license": "MIT",
|
package/spec-cli/src/gateway.ts
CHANGED
|
@@ -99,8 +99,8 @@ function cookieOf(header: string | undefined, name: string): string | null {
|
|
|
99
99
|
}
|
|
100
100
|
return null
|
|
101
101
|
}
|
|
102
|
-
function isAuthed(req: http.IncomingMessage, token: string): boolean {
|
|
103
|
-
const c = cookieOf(req.headers.cookie,
|
|
102
|
+
function isAuthed(req: http.IncomingMessage, token: string, cookieName: string): boolean {
|
|
103
|
+
const c = cookieOf(req.headers.cookie, cookieName)
|
|
104
104
|
return c != null && constEq(c, token)
|
|
105
105
|
}
|
|
106
106
|
|
|
@@ -124,7 +124,12 @@ export function startGateway(opts: GatewayOpts): void {
|
|
|
124
124
|
const gated = !!opts.password
|
|
125
125
|
const token = gated ? authToken(opts.password) : ''
|
|
126
126
|
const secure = !!opts.tls
|
|
127
|
-
|
|
127
|
+
// the auth cookie is HOST-scoped (RFC 6265 ignores the port), so two gateways on one IP would share a
|
|
128
|
+
// single 'spex_auth' jar entry and clobber each other's login. Key the name by the public port — the
|
|
129
|
+
// unique discriminator on a host, exactly what the user's two URLs differ by — so same-host instances
|
|
130
|
+
// (e.g. :8787 and :8788) stay logged in concurrently and a logout clears only its own.
|
|
131
|
+
const cookieName = `${COOKIE}_${opts.publicPort}`
|
|
132
|
+
const setCookie = `${cookieName}=${token}; HttpOnly; Path=/; SameSite=Lax; Max-Age=2592000${secure ? '; Secure' : ''}`
|
|
128
133
|
|
|
129
134
|
const handler = (req: http.IncomingMessage, res: http.ServerResponse) => {
|
|
130
135
|
const url = (req.url || '/').split('?')[0]
|
|
@@ -132,8 +137,8 @@ export function startGateway(opts: GatewayOpts): void {
|
|
|
132
137
|
// login surface — the only routes reachable without a cookie. Absent entirely when ungated.
|
|
133
138
|
if (url === '/login' && req.method === 'POST') return doLogin(req, res, opts.password, setCookie)
|
|
134
139
|
if (url === '/login') return sendHtml(res, 200, loginPage())
|
|
135
|
-
if (url === '/logout') { res.writeHead(302, { 'Set-Cookie': `${
|
|
136
|
-
if (!isAuthed(req, token)) {
|
|
140
|
+
if (url === '/logout') { res.writeHead(302, { 'Set-Cookie': `${cookieName}=; Path=/; Max-Age=0`, Location: '/login' }); return res.end() }
|
|
141
|
+
if (!isAuthed(req, token, cookieName)) {
|
|
137
142
|
if (url.startsWith('/api')) { res.writeHead(401, { 'Content-Type': 'application/json' }); return res.end('{"error":"authentication required"}') }
|
|
138
143
|
res.writeHead(302, { Location: '/login' }); return res.end()
|
|
139
144
|
}
|
|
@@ -148,7 +153,7 @@ export function startGateway(opts: GatewayOpts): void {
|
|
|
148
153
|
// it on the same-origin handshake), then raw-pipe to the loopback supervisor, replaying the buffered
|
|
149
154
|
// upgrade request so the child completes the WebSocket handshake. Mirrors supervise.ts's byte pipe.
|
|
150
155
|
server.on('upgrade', (req, socket, head) => {
|
|
151
|
-
if (gated && !isAuthed(req, token)) { socket.destroy(); return }
|
|
156
|
+
if (gated && !isAuthed(req, token, cookieName)) { socket.destroy(); return }
|
|
152
157
|
const up = net.connect(opts.upstreamPort, '127.0.0.1', () => {
|
|
153
158
|
up.write(`${req.method} ${req.url} HTTP/1.1\r\n` + rawHeaders(req))
|
|
154
159
|
if (head && head.length) up.write(head)
|