Skip to content

Commit 4ca77f1

Browse files
authored
Harden msgraph webhook auth requirements (NousResearch#30169)
1 parent 3e78e35 commit 4ca77f1

4 files changed

Lines changed: 38 additions & 4 deletions

File tree

gateway/config.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,9 @@ def from_dict(cls, data: Dict[str, Any]) -> "StreamingConfig":
424424
Platform.SMS: lambda cfg: bool(os.getenv("TWILIO_ACCOUNT_SID")),
425425
Platform.API_SERVER: lambda cfg: True,
426426
Platform.WEBHOOK: lambda cfg: True,
427-
Platform.MSGRAPH_WEBHOOK: lambda cfg: True,
427+
Platform.MSGRAPH_WEBHOOK: lambda cfg: bool(
428+
str(cfg.extra.get("client_state") or "").strip()
429+
),
428430
Platform.FEISHU: lambda cfg: bool(cfg.extra.get("app_id")),
429431
Platform.WECOM: lambda cfg: bool(cfg.extra.get("bot_id")),
430432
Platform.WECOM_CALLBACK: lambda cfg: bool(

gateway/platforms/msgraph_webhook.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,12 @@ def set_notification_scheduler(self, scheduler: Optional[NotificationScheduler])
133133
self._notification_scheduler = scheduler
134134

135135
async def connect(self) -> bool:
136+
if self._client_state is None:
137+
logger.error(
138+
"[msgraph_webhook] Refusing to start without extra.client_state configured"
139+
)
140+
return False
141+
136142
app = web.Application()
137143
app.router.add_get(self._health_path, self._handle_health)
138144
app.router.add_get(self._webhook_path, self._handle_validation)
@@ -310,7 +316,7 @@ def _verify_client_state(self, notification: Dict[str, Any]) -> bool:
310316
"""
311317
expected = self._client_state
312318
if expected is None:
313-
return True
319+
return False
314320
provided = self._string_or_none(notification.get("clientState"))
315321
if provided is None:
316322
return False

tests/gateway/test_msgraph_webhook.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import pytest
77

88
from gateway.config import GatewayConfig, Platform, PlatformConfig, _apply_env_overrides
9-
from gateway.platforms.msgraph_webhook import MSGraphWebhookAdapter
9+
from gateway.platforms.msgraph_webhook import AIOHTTP_AVAILABLE, MSGraphWebhookAdapter
1010

1111

1212
def _make_adapter(**extra_overrides) -> MSGraphWebhookAdapter:
@@ -70,6 +70,15 @@ def test_env_overrides_apply_to_existing_msgraph_webhook_platform(self, monkeypa
7070

7171

7272
class TestMSGraphValidationHandshake:
73+
@pytest.mark.anyio
74+
async def test_connect_requires_client_state(self):
75+
if not AIOHTTP_AVAILABLE:
76+
pytest.skip("aiohttp not installed")
77+
adapter = MSGraphWebhookAdapter(PlatformConfig(enabled=True, extra={}))
78+
connected = await adapter.connect()
79+
assert connected is False
80+
assert adapter.is_connected() is False
81+
7382
@pytest.mark.anyio
7483
async def test_validation_token_echo_on_get(self):
7584
adapter = _make_adapter()
@@ -99,6 +108,22 @@ async def test_post_with_validation_token_still_echoes(self):
99108

100109

101110
class TestMSGraphNotifications:
111+
@pytest.mark.anyio
112+
async def test_missing_client_state_is_auth_rejected(self):
113+
adapter = _make_adapter(client_state=None)
114+
payload = {
115+
"value": [
116+
{
117+
"id": "notif-no-client-state",
118+
"subscriptionId": "sub-1",
119+
"changeType": "updated",
120+
"resource": "communications/onlineMeetings/meeting-1",
121+
}
122+
]
123+
}
124+
resp = await adapter._handle_notification(_FakeRequest(json_payload=payload))
125+
assert resp.status == 403
126+
102127
@pytest.mark.anyio
103128
async def test_valid_notification_accepted_and_scheduled(self):
104129
adapter = _make_adapter()

tests/gateway/test_platform_connected_checkers.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,11 @@ def test_checker_returns_true_when_configured(platform, checker, monkeypatch):
7979
elif platform in {
8080
Platform.API_SERVER,
8181
Platform.WEBHOOK,
82-
Platform.MSGRAPH_WEBHOOK,
8382
Platform.WHATSAPP,
8483
}:
8584
mock_config.extra = {}
85+
elif platform == Platform.MSGRAPH_WEBHOOK:
86+
mock_config.extra = {"client_state": "expected-client-state"}
8687
elif platform == Platform.FEISHU:
8788
mock_config.extra = {"app_id": "app"}
8889
elif platform == Platform.WECOM:

0 commit comments

Comments
 (0)