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');