🔌 feat: Revoke MCP OAuth Credentials (#9464)

* revocation metadata fields

* store metadata

* get client info and meta

* revoke oauth tokens

* delete flow

* uninstall oauth mcp

* revoke button

* revoke oauth refactor, add comments, test

* adjust for clarity

* test deleteFlow

* handle metadata type

* no mutation

* adjust for clarity

* styling

* restructure for clarity

* move token-specific stuff

* use mcpmanager's oauth servers

* fix typo

* fix addressing of oauth prop

* log prefix

* remove debug log
This commit is contained in:
Federico Ruggi 2025-09-11 00:53:34 +02:00 committed by GitHub
parent 5667cc9702
commit 04c3a5a861
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 725 additions and 6 deletions

View file

@ -149,4 +149,36 @@ describe('FlowStateManager', () => {
await expect(flowPromise).rejects.toThrow('failure');
}, 15000);
});
describe('deleteFlow', () => {
const flowId = 'test-flow-123';
const type = 'test-type';
const flowKey = `${type}:${flowId}`;
it('deletes an existing flow', async () => {
await store.set(flowKey, { type, status: 'PENDING', metadata: {}, createdAt: Date.now() });
expect(await store.get(flowKey)).toBeDefined();
const result = await flowManager.deleteFlow(flowId, type);
expect(result).toBe(true);
expect(await store.get(flowKey)).toBeUndefined();
});
it('returns false if the deletion errors', async () => {
jest.spyOn(store, 'delete').mockRejectedValue(new Error('Deletion failed'));
const result = await flowManager.deleteFlow(flowId, type);
expect(result).toBe(false);
});
it('does nothing if the flow does not exist', async () => {
expect(await store.get(flowKey)).toBeUndefined();
const result = await flowManager.deleteFlow(flowId, type);
expect(result).toBe(true);
});
});
});

View file

@ -241,4 +241,19 @@ export class FlowStateManager<T = unknown> {
throw error;
}
}
/**
* Deletes a flow state
*/
async deleteFlow(flowId: string, type: string): Promise<boolean> {
const flowKey = this.getFlowKey(flowId, type);
try {
await this.keyv.delete(flowKey);
logger.debug(`[${flowKey}] Flow deleted`);
return true;
} catch (error) {
logger.error(`[${flowKey}] Error deleting flow:`, error);
return false;
}
}
}