fix: create new flows on invalid_grant errors

This commit is contained in:
Dustin Healy 2025-12-11 14:22:27 -08:00
parent 7ea9cc06f2
commit 00c320ccae
3 changed files with 25 additions and 5 deletions

View file

@ -272,16 +272,28 @@ export class FlowStateManager<T = unknown> {
): Promise<T> {
const flowKey = this.getFlowKey(flowId, type);
let existingState = (await this.keyv.get(flowKey)) as FlowState<T> | undefined;
if (existingState) {
logger.debug(`[${flowKey}] Flow already exists`);
const hasAccessTokenExpired =
existingState?.result &&
typeof existingState.result === 'object' &&
'expires_at' in existingState.result &&
typeof existingState.result.expires_at === 'number' &&
existingState.result.expires_at < Date.now();
if (existingState && !hasAccessTokenExpired) {
logger.debug(`[${flowKey}] Flow already exists with valid token`);
return this.monitorFlow(flowKey, type, signal);
}
await new Promise((resolve) => setTimeout(resolve, 250));
existingState = (await this.keyv.get(flowKey)) as FlowState<T> | undefined;
if (existingState) {
logger.debug(`[${flowKey}] Flow exists on 2nd check`);
const hasAccessTokenExpiredRecheck =
existingState?.result &&
typeof existingState.result === 'object' &&
'expires_at' in existingState.result &&
typeof existingState.result.expires_at === 'number' &&
existingState.result.expires_at < Date.now();
if (existingState && !hasAccessTokenExpiredRecheck) {
logger.debug(`[${flowKey}] Flow exists on 2nd check with valid token`);
return this.monitorFlow(flowKey, type, signal);
}

View file

@ -167,6 +167,10 @@ export class MCPConnectionFactory {
config?.oauth,
);
// Delete any existing flow state to ensure we start fresh
// This prevents stale codeVerifier issues when re-authenticating
await this.flowManager!.deleteFlow(flowId, 'mcp_oauth');
// Create the flow state so the OAuth callback can find it
// We spawn this in the background without waiting for it
this.flowManager!.createFlow(flowId, 'mcp_oauth', flowMetadata).catch(() => {

View file

@ -615,7 +615,7 @@ export class MCPConnection extends EventEmitter {
}
// Check if it's an OAuth authentication error
if (errorCode === 401 || errorCode === 403) {
if (this.isOAuthError(error)) {
logger.warn(`${this.getLogPrefix()} OAuth authentication error detected`);
this.emit('oauthError', error);
}
@ -778,6 +778,10 @@ export class MCPConnection extends EventEmitter {
if (message.includes('invalid_token')) {
return true;
}
// Check for invalid_grant (OAuth servers return this for expired/revoked grants)
if (message.includes('invalid_grant')) {
return true;
}
// Check for authentication required
if (message.includes('authentication required') || message.includes('unauthorized')) {
return true;