[auth] Omit UNSIGNED agent token at every agent->API call site

The orchestrator injects ROBOCO_AGENT_TOKEN=UNSIGNED when the HMAC secret
is unset at spawn. The API middleware rejects a presented-but-unverifiable
token with 401 'signature mismatch' even in dev mode (auth not required),
so forwarding UNSIGNED turned every flow/do/SDK/secretary/git verb into a
401 — the live pr_reviewer/i_am_idle signature-mismatch loop. Omit the
header when the token is the UNSIGNED sentinel at all five agent-side
header builders; dev accepts a missing token, prod 401s with 'Missing
X-Agent-Token' (the clear respawn-with-secret signal). Add a structlog
diagnostic on the middleware reject path so the next mismatch logs the
exact (id, role, team, token_unsigned, auth_required) inputs.
This commit is contained in:
Renn F
2026-07-06 05:43:02 +02:00
parent 2e938d20d6
commit 2b8bc10d8d
8 changed files with 86 additions and 5 deletions
+25
View File
@@ -119,6 +119,31 @@ def test_build_headers_carries_auth_token_and_team(
assert "X-Correlation-ID" in headers
def test_build_headers_omits_unsigned_token(monkeypatch: pytest.MonkeyPatch) -> None:
"""The orchestrator injects ROBOCO_AGENT_TOKEN=UNSIGNED when the HMAC
secret is unset at spawn. The middleware rejects a presented-but-unverifiable
token with 401 "signature mismatch" even in dev mode, so forwarding UNSIGNED
turns every do verb into a 401. Omit the header so dev (auth not required)
accepts the call; prod 401s with "Missing X-Agent-Token" instead."""
import importlib
be_dev_1 = "00000000-0000-0000-0001-000000000001"
manifest = Path(tempfile.mkdtemp()) / "tool-manifest.json"
manifest.write_text(json.dumps({**_DO_TEST_MANIFEST, "agent_id": be_dev_1}))
monkeypatch.setenv("ROBOCO_AGENT_ID", be_dev_1)
monkeypatch.setenv("ROBOCO_AGENT_ROLE", "developer")
monkeypatch.setenv("ROBOCO_AGENT_TOKEN", "UNSIGNED")
monkeypatch.setenv("ROBOCO_ORCHESTRATOR_URL", "http://test-orchestrator:8000")
monkeypatch.setenv("ROBOCO_TOOL_MANIFEST_PATH", str(manifest))
import roboco.mcp.do_server as srv
importlib.reload(srv)
headers = srv._build_headers()
assert "X-Agent-Token" not in headers
def test_dm_posts_all_fields(do_module: Any) -> None:
fake_client = MagicMock()
fake_client.__enter__.return_value = fake_client