fix: gate BYOK 503 behavior behind litellm.require_byok_credential_store flag

Add a feature flag `litellm.require_byok_credential_store` (default True)
that controls whether _get_byok_credential and _check_byok_credential raise
HTTP 503 or fall through to legacy behavior when prisma_client is None.

Default True preserves the new secure behavior (503 = infra problem, distinct
from 401 = missing credential). Set to False to restore legacy silent-bypass
for stateless/no-database deployments.
This commit is contained in:
Ishaan Jaffer 2026-03-07 10:18:20 -08:00
parent 9c653fba9c
commit 2db33a10e5
2 changed files with 35 additions and 21 deletions

View file

@ -160,6 +160,11 @@ langfuse_default_tags: Optional[List[str]] = None
langsmith_batch_size: Optional[int] = None
prometheus_initialize_budget_metrics: Optional[bool] = False
require_auth_for_metrics_endpoint: Optional[bool] = False
# When True (default), BYOK MCP servers raise HTTP 503 if the credential store
# (Prisma DB) is unavailable, rather than silently bypassing the BYOK check.
# Set to False to restore pre-v1.x legacy behaviour for stateless deployments
# that do not use a database.
require_byok_credential_store: Optional[bool] = True
argilla_batch_size: Optional[int] = None
datadog_use_v1: Optional[bool] = False # if you want to use v1 datadog logged payload.
gcs_pub_sub_use_v1: Optional[bool] = (

View file

@ -28,6 +28,7 @@ from starlette.requests import Request as StarletteRequest
from starlette.responses import JSONResponse
from starlette.types import Receive, Scope, Send
import litellm
from litellm._logging import verbose_logger
from litellm.constants import MAXIMUM_TRACEBACK_LINES_TO_LOG
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
@ -1586,16 +1587,21 @@ if MCP_AVAILABLE:
if prisma_client is None:
# Without a database we cannot fetch the per-user credential.
# Return a 503 (infrastructure problem) so callers can distinguish
# "no credential" (401) from "credential store unavailable" (503).
raise HTTPException(
status_code=503,
detail={
"error": "byok_store_unavailable",
"server_id": mcp_server.server_id,
"message": "Credential store is not available; cannot fetch BYOK credential.",
},
)
# When require_byok_credential_store is True (default) we raise 503
# so callers can distinguish "no credential" (401) from
# "credential store unavailable" (503).
# Set litellm.require_byok_credential_store = False to restore the
# legacy silent-bypass behaviour for stateless deployments.
if litellm.require_byok_credential_store:
raise HTTPException(
status_code=503,
detail={
"error": "byok_store_unavailable",
"server_id": mcp_server.server_id,
"message": "Credential store is not available; cannot fetch BYOK credential.",
},
)
return None
raw = await get_user_credential(
prisma_client=prisma_client,
user_id=user_id,
@ -1666,17 +1672,20 @@ if MCP_AVAILABLE:
if prisma_client is None:
# Without a database we cannot verify whether the user has a stored
# credential, so we must deny access rather than silently bypass the
# BYOK check. A 503 signals a configuration/infrastructure problem,
# not an auth failure, so clients can distinguish the two cases.
raise HTTPException(
status_code=503,
detail={
"error": "byok_store_unavailable",
"server_id": mcp_server.server_id,
"message": "Credential store is not available; cannot verify BYOK access.",
},
)
# credential. When require_byok_credential_store is True (default)
# we raise 503 to distinguish infra failures from missing credentials.
# Set litellm.require_byok_credential_store = False to restore the
# legacy silent-bypass behaviour for stateless deployments.
if litellm.require_byok_credential_store:
raise HTTPException(
status_code=503,
detail={
"error": "byok_store_unavailable",
"server_id": mcp_server.server_id,
"message": "Credential store is not available; cannot verify BYOK access.",
},
)
return
raw_credential = await get_user_credential(
prisma_client=prisma_client,