fix(cors): proper CORS with credentials for Tailscale IPs

- Disable Vite built-in CORS (sets wildcard * which fails with credentials)
- Add custom CORS middleware that echoes request origin
- Handle OPTIONS preflight explicitly
- Proxy uses changeOrigin: false to preserve request origin
- API's trusted origin detection handles Tailscale IPs
This commit is contained in:
Ingo Wagner 2026-09-22 16:45:01 +00:00
parent cce793151e
commit 8ad8040d60

View File

@ -272,35 +272,49 @@ export default defineConfig(({ mode }) => {
configureServer: (server) => attachNovncProxy(server, screenProxySecret(), api),
configurePreviewServer: (server) => attachNovncProxy(server, screenProxySecret(), api),
},
{
// Replace Vite's built-in CORS with origin-echoing CORS for credentials support
name: "rakazo-cors",
configureServer: (server) => {
// Vite's cors middleware runs before our middleware.
// We handle CORS ourselves and let Vite's cors be a no-op.
// Since cors: false disables Vite's CORS, we handle everything here.
server.middlewares.use((req, res, next) => {
const origin = req.headers.origin;
if (origin && typeof origin === "string") {
res.setHeader("access-control-allow-origin", origin);
res.setHeader("access-control-allow-credentials", "true");
res.setHeader("access-control-allow-methods", "GET, HEAD, PUT, PATCH, POST, DELETE, OPTIONS");
res.setHeader("access-control-allow-headers", "content-type, x-rakazo-space-id, authorization");
res.setHeader("access-control-max-age", "86400");
if (req.method === "OPTIONS") {
res.writeHead(204);
res.end();
return;
}
}
next();
});
},
},
],
server: {
host: "0.0.0.0",
port: webPort,
strictPort: true,
allowedHosts: true,
// Disable Vite built-in CORS — our middleware handles it
cors: false,
proxy: {
"/api": {
target: api,
changeOrigin: true,
configure: (proxy) => {
proxy.on("proxyRes", (res) => {
res.headers["access-control-allow-origin"] = "*";
res.headers["access-control-allow-credentials"] = "true";
});
},
},
"/rpc": {
target: api,
changeOrigin: true,
configure: (proxy) => {
proxy.on("proxyRes", (res) => {
res.headers["access-control-allow-origin"] = "*";
res.headers["access-control-allow-credentials"] = "true";
});
},
changeOrigin: false,
},
"/api": {
target: api,
changeOrigin: false,
},
},
cors: true,
},
preview: {
host: "0.0.0.0",