security: fail closed on bearer endpoint credentials

This commit is contained in:
RaresKeY
2026-08-30 22:27:35 +00:00
parent c473240131
commit aee6655fbd
8 changed files with 194 additions and 26 deletions
+7 -4
View File
@@ -683,7 +683,7 @@ def _validate_bearer_session_model(sess, owner: str | None = None) -> Optional[s
Direct API-key sessions intentionally have no ``ModelEndpoint`` row and
retain their documented compatibility behavior. Registered endpoint
sessions, including provider-auth-backed rows, must use the visible
server-owned inventory and never trigger a provider lookup here.
server-owned inventory and never trigger a live provider lookup here.
"""
# Lightweight in-memory test doubles from older route tests do not carry
# durable provenance fields. They cannot represent a persisted bearer
@@ -737,9 +737,9 @@ def _validate_bearer_session_model(sess, owner: str | None = None) -> Optional[s
validated = _validate_bearer_model_selection(ep, requested)
# A session may outlive an endpoint-key rotation. For bearer calls,
# use the current static key for this exact endpoint and never trust a
# stale persisted Authorization header. Provider-auth rows remain
# request-local and are intentionally empty in cache-only mode.
# use the current exact-endpoint credentials and never trust a stale
# persisted Authorization header. Provider-auth credentials are
# owner-scoped, request-local, and cache-only in this boundary.
try:
from src.endpoint_resolver import build_headers, resolve_endpoint_runtime
@@ -752,6 +752,9 @@ def _validate_bearer_session_model(sess, owner: str | None = None) -> Optional[s
except Exception as exc:
logger.warning("Could not refresh bearer session endpoint auth: %s", exc)
sess.headers = {}
if getattr(ep, "provider_auth_id", None):
raise HTTPException(401, "Registered provider credentials are unavailable") from exc
raise HTTPException(400, "Registered endpoint credentials are unavailable") from exc
sess.model = validated
return validated
+8
View File
@@ -997,6 +997,14 @@ def setup_session_routes(
rag: str = Form(None)
):
require_chat_scope(request)
# This legacy alias uses the server-owned OPENAI_API_KEY rather than a
# caller-selected, owner-visible ModelEndpoint. A bearer must not turn
# that credential into an owner-attributed session whose provenance
# cannot be represented as either a registered endpoint or a direct
# caller-supplied key. Registered bearer chat remains available through
# POST /api/session with endpoint_id.
if is_bearer_principal(request):
raise HTTPException(403, "Bearer callers must choose a registered model endpoint")
if not OPENAI_API_KEY:
raise HTTPException(400, "Server missing OPENAI_API_KEY")
sid = str(uuid.uuid4())