broker: don't require an end-user Bearer token for AISBF-relayed requests

Broker (AISBF) chat requests were rejected by the engine with 401 "Invalid API
key" — they're authenticated at the aisbf layer (registration token) and carry no
end-user Bearer, but the engine's BearerAuthMiddleware still demanded one, so every
brokered request came back as a constant ~472-byte error and never ran inference.

Fix (token auto-managed, secure):
- Front marks broker-relayed requests with x-coderai-broker-authed = the internal
  shared token (which the engine already trusts via CODERAI_INTERNAL_TOKEN). Added
  that header to _DROP_REQ so a client-supplied copy is always stripped first —
  unforgeable from outside.
- Engine BearerAuthMiddleware skips the Bearer check when x-coderai-broker-authed
  matches CODERAI_INTERNAL_TOKEN. This is NOT the plain internal token (which rides
  on every front→engine request, direct included), so DIRECT API requests still
  require a real Bearer token.
Co-Authored-By: 's avatarClaude Opus 4.8 <noreply@anthropic.com>
parent 067a797e
...@@ -21,6 +21,7 @@ Endpoints covered: ...@@ -21,6 +21,7 @@ Endpoints covered:
/v1/completions — legacy completions /v1/completions — legacy completions
""" """
import os
import time import time
import threading import threading
from collections import defaultdict from collections import defaultdict
...@@ -59,6 +60,18 @@ class BearerAuthMiddleware(BaseHTTPMiddleware): ...@@ -59,6 +60,18 @@ class BearerAuthMiddleware(BaseHTTPMiddleware):
if request.scope.get("server") == ("internal", 80): if request.scope.get("server") == ("internal", 80):
return await call_next(request) return await call_next(request)
# Requests the front relays on behalf of an ALREADY-authenticated caller —
# the AISBF broker authenticates at the aisbf layer (registration token), so
# those requests carry no end-user Bearer. The front marks them with the
# internal shared token in x-coderai-broker-authed. It's unforgeable by
# external clients (only the front knows the token, and the front strips any
# client-supplied copy), so trust it and skip the end-user Bearer check.
# NOTE: this is NOT the plain internal token, which rides on EVERY front→engine
# request (direct included) — so direct requests still require a real Bearer.
_int = os.environ.get("CODERAI_INTERNAL_TOKEN")
if _int and request.headers.get("x-coderai-broker-authed", "") == _int:
return await call_next(request)
from codai.admin import routes as _admin_routes from codai.admin import routes as _admin_routes
sm = _admin_routes.session_manager sm = _admin_routes.session_manager
if sm is None: if sm is None:
......
...@@ -36,7 +36,8 @@ _HOP_BY_HOP = { ...@@ -36,7 +36,8 @@ _HOP_BY_HOP = {
} }
# Also strip any client-supplied internal token so a caller can't spoof/override the # Also strip any client-supplied internal token so a caller can't spoof/override the
# real one the front injects — only the front's httpx default header reaches engines. # real one the front injects — only the front's httpx default header reaches engines.
_DROP_REQ = _HOP_BY_HOP | {"host", "content-length", "x-coderai-internal"} _DROP_REQ = _HOP_BY_HOP | {"host", "content-length", "x-coderai-internal",
"x-coderai-broker-authed"}
# Also drop date/server from relayed engine responses: the front's own ASGI server # Also drop date/server from relayed engine responses: the front's own ASGI server
# (uvicorn/starlette) adds its own Date and Server headers, so keeping the engine's # (uvicorn/starlette) adds its own Date and Server headers, so keeping the engine's
# too produces DUPLICATE header lines — which nginx logs as a warning on every # too produces DUPLICATE header lines — which nginx logs as a warning on every
...@@ -215,6 +216,11 @@ class FrontProxy: ...@@ -215,6 +216,11 @@ class FrontProxy:
"body": b'{"error":"No engine is ready yet."}'} "body": b'{"error":"No engine is ready yet."}'}
send_headers = {k: v for k, v in (headers or {}).items() send_headers = {k: v for k, v in (headers or {}).items()
if k.lower() not in _DROP_REQ} if k.lower() not in _DROP_REQ}
# Mark this as a broker-relayed, already-authenticated request so the engine
# skips its end-user Bearer check (the AISBF broker authenticated upstream).
# Signed with the internal token; the engine accepts it only if it matches.
if self.internal_token:
send_headers["x-coderai-broker-authed"] = self.internal_token
try: try:
r = await self._long.request(method, engine.url + path, r = await self._long.request(method, engine.url + path,
headers=send_headers, params=query or {}, headers=send_headers, params=query or {},
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment