0ddeaadeee
api_key-принципал стал полноправным REST-принципалом → мог начеканить 24-ч
COLLAB-токен, который AuthenticationExtension не сверял с api_keys, и ревокация
не доставала websocket-редактирование. Блокировать /collab-token для api_key
нельзя (это правки агентов). Решение fail-closed:
- JwtCollabPayload расширен полем principal ('session'|'api_key') + apiKeyId.
generateCollabToken штампует его в КАЖДЫЙ токен: 'api_key'+apiKeyId, когда
чеканит api-key-принципал (внешний MCP-агент), иначе 'session'. Дискриминатор
ключуется на ОРИГИНЕ (наличии api-key), НЕ на actor:'agent' — внутренний
session-backed AI-агент остаётся 'session' и на no-check пути.
- /auth/collab-token читает подписью-выведенные req.raw.authType/apiKeyId
(не тело) и прокидывает origin через getCollabToken → generateCollabToken.
- doAuthenticate: при principal='api_key' на connect прогоняет
ApiKeyService.validate (отозванный ключ → новых collab-подключений нет; инфра
пробрасывается). api_key без apiKeyId — reject. Claimless-токен доверяется
только В ПРЕДЕЛАХ 24-ч grace-окна роллаута (легаси session-токен, api_key его
начеканить не мог); после окна всякий валидный токен обязан нести
дискриминатор, поэтому claimless — баг и отвергается (не молча-доверие 24ч).
CollaborationModule импортирует ApiKeyModule.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>