@@ -410,19 +410,51 @@ def _get_agent_orchestrator() -> AgentOrchestrator:
410410 return _AGENT_ORCHESTRATOR
411411
412412
413- def _is_authorized (request : Request ) -> bool :
414- """Validate shared API secret."""
415- if not settings .api_shared_secret :
416- logger .error ("Rejecting request: API_SHARED_SECRET is not configured" )
413+ def _is_authorized_with_secret (
414+ request : Request ,
415+ * ,
416+ configured_secret : str | None ,
417+ setting_name : str ,
418+ ) -> bool :
419+ """Validate an X-API-Secret header against one configured secret."""
420+ secret = (configured_secret or "" ).strip ()
421+ if not secret :
422+ logger .error ("Rejecting request: %s is not configured" , setting_name )
417423 return False
418424
419425 provided_secret = request .headers .get ("X-API-Secret" , "" )
420- if secrets .compare_digest (provided_secret , settings . api_shared_secret ):
426+ if secrets .compare_digest (provided_secret , secret ):
421427 return True
422- logger .warning ("Rejecting request: invalid X-API-Secret" )
428+ logger .warning ("Rejecting request: invalid X-API-Secret for %s" , setting_name )
423429 return False
424430
425431
432+ def _is_authorized (request : Request ) -> bool :
433+ """Validate the internal shared API secret."""
434+ return _is_authorized_with_secret (
435+ request ,
436+ configured_secret = settings .api_shared_secret ,
437+ setting_name = "API_SHARED_SECRET" ,
438+ )
439+
440+
441+ def _is_webhook_authorized (request : Request ) -> bool :
442+ """Validate the external webhook secret, with legacy API secret fallback."""
443+ webhook_secret = (settings .webhook_shared_secret or "" ).strip ()
444+ if webhook_secret :
445+ return _is_authorized_with_secret (
446+ request ,
447+ configured_secret = webhook_secret ,
448+ setting_name = "WEBHOOK_SHARED_SECRET" ,
449+ )
450+
451+ return _is_authorized_with_secret (
452+ request ,
453+ configured_secret = settings .api_shared_secret ,
454+ setting_name = "API_SHARED_SECRET" ,
455+ )
456+
457+
426458def _agent_request_rate_limited (discord_user_id : str ) -> bool :
427459 now = time .monotonic ()
428460 window_start = now - _AGENT_REQUEST_RATE_LIMIT_WINDOW_SECONDS
@@ -2411,7 +2443,7 @@ async def health_handler(request: Request) -> JSONResponse:
24112443
24122444async def ingest_handler (request : Request , source : str ) -> JSONResponse :
24132445 """Validate and enqueue incoming webhook payloads."""
2414- if not _is_authorized (request ):
2446+ if not _is_webhook_authorized (request ):
24152447 return JSONResponse ({"error" : "unauthorized" }, status_code = 401 )
24162448
24172449 try :
@@ -2446,7 +2478,7 @@ async def ingest_handler(request: Request, source: str) -> JSONResponse:
24462478
24472479async def espocrm_webhook_handler (request : Request ) -> JSONResponse :
24482480 """Validate EspoCRM webhook payload and enqueue per-contact jobs."""
2449- if not _is_authorized (request ):
2481+ if not _is_webhook_authorized (request ):
24502482 return JSONResponse ({"error" : "unauthorized" }, status_code = 401 )
24512483
24522484 try :
@@ -5856,7 +5888,7 @@ async def dashboard_sync_people_handler(request: Request) -> JSONResponse:
58565888
58575889async def espocrm_people_sync_webhook_handler (request : Request ) -> JSONResponse :
58585890 """Queue per-contact people cache sync jobs from CRM webhook events."""
5859- if not _is_authorized (request ):
5891+ if not _is_webhook_authorized (request ):
58605892 return JSONResponse ({"error" : "unauthorized" }, status_code = 401 )
58615893
58625894 try :
@@ -5910,7 +5942,7 @@ async def docuseal_webhook_handler(request: Request) -> JSONResponse:
59105942 Job payload contract for the queue is:
59115943 completed_at = "YYYY-MM-DD HH:mm:ss" in UTC.
59125944 """
5913- if not _is_authorized (request ):
5945+ if not _is_webhook_authorized (request ):
59145946 return JSONResponse ({"error" : "unauthorized" }, status_code = 401 )
59155947
59165948 try :
@@ -6027,7 +6059,7 @@ async def docuseal_webhook_handler(request: Request) -> JSONResponse:
60276059
60286060async def google_forms_intake_webhook_handler (request : Request ) -> JSONResponse :
60296061 """Validate a Google Forms intake submission and enqueue a processing job."""
6030- if not _is_authorized (request ):
6062+ if not _is_webhook_authorized (request ):
60316063 return JSONResponse ({"error" : "unauthorized" }, status_code = 401 )
60326064
60336065 try :
0 commit comments