fix: improve error handling for workspace switch and support --host 0.0.0.0

- Add safeParseJson() helper with content-type validation and proxy error detection
- Allow token acquisition from any interface when server binds to 0.0.0.0 or ::
- Provide clear error messages when proxy intercepts localhost requests
This commit is contained in:
catlog22
2026-03-04 13:59:56 +08:00
parent 91fa594578
commit fb0f56bfc0
3 changed files with 76 additions and 6 deletions

View File

@@ -47,11 +47,31 @@ export function extractAuthToken(req: IncomingMessage): string | null {
return null;
}
export function isLocalhostRequest(req: IncomingMessage): boolean {
/**
* Check if request is from localhost
* @param req - The incoming request
* @param allowAllInterfaces - When true (server bound to 0.0.0.0), allows requests from any interface
*/
export function isLocalhostRequest(req: IncomingMessage, allowAllInterfaces: boolean = false): boolean {
// When server is bound to 0.0.0.0, allow token acquisition from any interface
// This is safe because the token endpoint only provides auth for the dashboard
if (allowAllInterfaces) {
return true;
}
const remote = req.socket?.remoteAddress ?? '';
return remote === '127.0.0.1' || remote === '::1' || remote === '::ffff:127.0.0.1';
}
/**
* Check if host binding allows all network interfaces
* @param host - The host address the server is bound to
*/
export function isWildcardHost(host: string | undefined): boolean {
if (!host) return false;
return host === '0.0.0.0' || host === '::' || host === '';
}
export function setAuthCookie(res: ServerResponse, token: string, expiresAt: Date): void {
const maxAgeSeconds = Math.max(0, Math.floor((expiresAt.getTime() - Date.now()) / 1000));

View File

@@ -51,7 +51,7 @@ import { handleSpecRoutes } from './routes/spec-routes.js';
import { handleWebSocketUpgrade, broadcastToClients, extractSessionIdFromPath } from './websocket.js';
import { getTokenManager } from './auth/token-manager.js';
import { authMiddleware, isLocalhostRequest, setAuthCookie } from './auth/middleware.js';
import { authMiddleware, isLocalhostRequest, isWildcardHost, setAuthCookie } from './auth/middleware.js';
import { getCorsOrigin } from './cors.js';
import { csrfValidation } from './auth/csrf-middleware.js';
import { getCsrfTokenManager } from './auth/csrf-manager.js';
@@ -418,9 +418,11 @@ export async function startServer(options: ServerOptions = {}): Promise<http.Ser
server
};
// Token acquisition endpoint (localhost-only)
// Token acquisition endpoint (localhost-only, or any interface when bound to 0.0.0.0)
if (pathname === '/api/auth/token') {
if (!isLocalhostRequest(req)) {
// Allow from any interface when server is bound to 0.0.0.0 or ::
const allowAllInterfaces = isWildcardHost(host);
if (!isLocalhostRequest(req, allowAllInterfaces)) {
res.writeHead(403, { 'Content-Type': 'application/json; charset=utf-8' });
res.end(JSON.stringify({ error: 'Forbidden' }));
return;