From f089c557e72f88fe6ea6868ff26407fb44246b75 Mon Sep 17 00:00:00 2001 From: jubnl Date: Wed, 6 May 2026 11:05:06 +0200 Subject: [PATCH] =?UTF-8?q?fix(mcp):=20fix=20OAuth=20popup=20blank=20page?= =?UTF-8?q?=20=E2=80=94=20SW=20denylist=20and=20COOP=20header?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Service worker was intercepting /oauth/authorize navigate requests (not in denylist), serving index.html, and React Router's catch-all redirected to / instead of the SDK authorize handler. Helmet's default COOP: same-origin isolated the /oauth/consent popup from its cross-origin opener, making window.opener null and breaking the popup-based OAuth completion signal for ChatGPT and similar clients. --- client/vite.config.js | 2 +- server/src/app.ts | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/client/vite.config.js b/client/vite.config.js index f275fc8c..322e30b9 100644 --- a/client/vite.config.js +++ b/client/vite.config.js @@ -11,7 +11,7 @@ export default defineConfig({ maximumFileSizeToCacheInBytes: 10 * 1024 * 1024, globPatterns: ['**/*.{js,css,html,svg,png,woff,woff2,ttf}'], navigateFallback: 'index.html', - navigateFallbackDenylist: [/^\/api/, /^\/uploads/, /^\/mcp/], + navigateFallbackDenylist: [/^\/api/, /^\/uploads/, /^\/mcp/, /^\/oauth\//, /^\/.well-known\//], runtimeCaching: [ { // Carto map tiles (default provider) diff --git a/server/src/app.ts b/server/src/app.ts index ec855925..88bdd461 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -478,6 +478,12 @@ export function createApp(): express.Application { next(); }); + // Helmet's COOP: same-origin isolates the consent popup from its cross-origin opener (ChatGPT etc.), making window.opener null and breaking the OAuth flow. + app.use('/oauth/consent', (_req: Request, res: Response, next: NextFunction) => { + res.setHeader('Cross-Origin-Opener-Policy', 'unsafe-none'); + next(); + }); + // Production static file serving if (process.env.NODE_ENV === 'production') { const publicPath = path.join(__dirname, '../public');