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(),