Безопасность: bot-токены выделены в отдельный тип bot_login (этап 0)

- POST /api/bot/auth/login выдаёт bot_login/bot_login_refresh (audience workflow-bots, TTL 30d/90d)
- authenticateBot принимает bot_login и bot_service
- authenticateToken/authenticateFileToken отклоняют bot-токены (401) — закрыта коллизия bot.id с user.id
This commit is contained in:
2026-07-22 14:32:11 +03:00
parent 72de9a25fb
commit 1fae441a09
4 changed files with 99 additions and 18 deletions

View File

@@ -49,6 +49,12 @@ export const authenticateToken = async (
try {
const decoded = verifyAccessToken(token);
// Токены ботов (старые с appRole='bot' или любые с type='bot*') не принимаются
// на пользовательских ресурсах — это закрывает коллизию bot.id ↔ user.id.
const payloadType = (decoded as { type?: string }).type;
if (decoded.appRole === 'bot' || (typeof payloadType === 'string' && payloadType.startsWith('bot'))) {
return res.status(401).json({ error: 'Токены ботов не принимаются на этом ресурсе' });
}
// Use JWT organizationId to set tenant context for the users table lookup,
// preventing auth failure when FORCE RLS is active on the users table.
const user = await withTenant(decoded.organizationId, () =>
@@ -152,6 +158,11 @@ export const authenticateFileToken = async (
try {
const decoded = verifyAccessToken(token);
// Токены ботов не принимаются и на файловых ресурсах (та же коллизия id)
const payloadType = (decoded as { type?: string }).type;
if (decoded.appRole === 'bot' || (typeof payloadType === 'string' && payloadType.startsWith('bot'))) {
return res.status(401).json({ error: 'Токены ботов не принимаются на этом ресурсе' });
}
const user = await withTenant(decoded.organizationId, () =>
storage.getUserWithOrganization(decoded.userId)
);