fix(mcp): clear OAuth callback timeout on all completion paths

The 5-minute timeout set in startCallbackServer was never cleared when
the OAuth flow completed. The timer would fire later, calling reject()
on a settled promise and server.close() on an already-closed server.
Now clears the timeout before every resolve() and reject() call.
This commit is contained in:
chinesepowered 2026-04-04 14:50:02 -07:00
parent 3bce84d5da
commit 86447064ca

View file

@ -261,6 +261,10 @@ export class MCPOAuthProvider {
</html> </html>
`); `);
activeCallbackServer = null; activeCallbackServer = null;
if (activeCallbackTimeout) {
clearTimeout(activeCallbackTimeout);
activeCallbackTimeout = null;
}
server.close(); server.close();
reject(new Error(`OAuth error: ${error}`)); reject(new Error(`OAuth error: ${error}`));
return; return;
@ -276,6 +280,10 @@ export class MCPOAuthProvider {
res.writeHead(400); res.writeHead(400);
res.end('Invalid state parameter'); res.end('Invalid state parameter');
activeCallbackServer = null; activeCallbackServer = null;
if (activeCallbackTimeout) {
clearTimeout(activeCallbackTimeout);
activeCallbackTimeout = null;
}
server.close(); server.close();
reject(new Error('State mismatch - possible CSRF attack')); reject(new Error('State mismatch - possible CSRF attack'));
return; return;
@ -294,10 +302,18 @@ export class MCPOAuthProvider {
`); `);
activeCallbackServer = null; activeCallbackServer = null;
if (activeCallbackTimeout) {
clearTimeout(activeCallbackTimeout);
activeCallbackTimeout = null;
}
server.close(); server.close();
resolve({ code, state }); resolve({ code, state });
} catch (error) { } catch (error) {
activeCallbackServer = null; activeCallbackServer = null;
if (activeCallbackTimeout) {
clearTimeout(activeCallbackTimeout);
activeCallbackTimeout = null;
}
server.close(); server.close();
reject(error); reject(error);
} }