From d1e081cf98b0bcd9533ed96d1f7c45219fbac75f Mon Sep 17 00:00:00 2001 From: patrick-motsch Date: Sun, 1 Mar 2026 11:15:39 +0100 Subject: [PATCH] fix(teamsbot): unblock WS loop for MFA so mfaResolved can close the modal Made-with: Cursor --- .../features/teamsbot/routeFeatureTeamsbot.py | 1 + modules/features/teamsbot/service.py | 57 ++++++++++++------- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/modules/features/teamsbot/routeFeatureTeamsbot.py b/modules/features/teamsbot/routeFeatureTeamsbot.py index 660ea279..9d84f854 100644 --- a/modules/features/teamsbot/routeFeatureTeamsbot.py +++ b/modules/features/teamsbot/routeFeatureTeamsbot.py @@ -786,6 +786,7 @@ async def deleteUserAccount( # ========================================================================= _mfaCodeQueues: dict = {} +_mfaWaitTasks: dict = {} @router.post("/{instanceId}/sessions/{sessionId}/mfa") @limiter.limit("10/minute") diff --git a/modules/features/teamsbot/service.py b/modules/features/teamsbot/service.py index f0fca9eb..4e68a9d7 100644 --- a/modules/features/teamsbot/service.py +++ b/modules/features/teamsbot/service.py @@ -396,35 +396,48 @@ class TeamsbotService: "timestamp": getIsoTimestamp(), }) - from .routeFeatureTeamsbot import _mfaCodeQueues + from .routeFeatureTeamsbot import _mfaCodeQueues, _mfaWaitTasks mfaQueue = asyncio.Queue() _mfaCodeQueues[sessionId] = mfaQueue - try: - mfaResponse = await asyncio.wait_for(mfaQueue.get(), timeout=120.0) - logger.info(f"[WS] MFA response received for session {sessionId}: action={mfaResponse.get('action')}") - await websocket.send_text(json.dumps({ - "type": "mfaResponse", - "sessionId": sessionId, - "mfa": mfaResponse, - })) - except asyncio.TimeoutError: - logger.warning(f"[WS] MFA response timeout for session {sessionId}") - await websocket.send_text(json.dumps({ - "type": "mfaResponse", - "sessionId": sessionId, - "mfa": {"action": "timeout"}, - })) - await _emitSessionEvent(sessionId, "mfaChallenge", { - "mfaType": "timeout", - "prompt": "MFA-Zeitlimit ueberschritten. Bitte erneut versuchen.", - }) - finally: - _mfaCodeQueues.pop(sessionId, None) + async def _waitAndForwardMfa(sid, queue, ws): + try: + mfaResponse = await asyncio.wait_for(queue.get(), timeout=120.0) + logger.info(f"[WS] MFA response received for session {sid}: action={mfaResponse.get('action')}") + await ws.send_text(json.dumps({ + "type": "mfaResponse", + "sessionId": sid, + "mfa": mfaResponse, + })) + except asyncio.TimeoutError: + logger.warning(f"[WS] MFA response timeout for session {sid}") + await ws.send_text(json.dumps({ + "type": "mfaResponse", + "sessionId": sid, + "mfa": {"action": "timeout"}, + })) + await _emitSessionEvent(sid, "mfaChallenge", { + "mfaType": "timeout", + "prompt": "MFA-Zeitlimit ueberschritten. Bitte erneut versuchen.", + }) + except asyncio.CancelledError: + logger.info(f"[WS] MFA wait cancelled for session {sid} (resolved via page)") + finally: + _mfaCodeQueues.pop(sid, None) + _mfaWaitTasks.pop(sid, None) + + _mfaWaitTasks[sessionId] = asyncio.create_task( + _waitAndForwardMfa(sessionId, mfaQueue, websocket) + ) elif msgType == "mfaResolved": success = message.get("success", False) logger.info(f"[WS] MFA resolved: success={success}") + from .routeFeatureTeamsbot import _mfaCodeQueues, _mfaWaitTasks + task = _mfaWaitTasks.pop(sessionId, None) + if task and not task.done(): + task.cancel() + _mfaCodeQueues.pop(sessionId, None) await _emitSessionEvent(sessionId, "mfaResolved", { "success": success, "timestamp": getIsoTimestamp(),