Если несколько организаций используют один токен интеграции, сообщения могут уходить не в тот Slack workspace или Microsoft Teams tenant, настройки начинают перезаписывать друг друга, а отзыв доступа одной компании останавливает работу остальных. Это не только функциональная ошибка, но и риск утечки данных между клиентами.
Правильная схема строится вокруг отдельных установок интеграции. Для каждой организации нужно хранить собственный контекст подключения, идентификатор внешнего пространства, выданные разрешения и учетные данные. Один экземпляр приложения может обслуживать много компаний, но один общий пользовательский или бот-токен не должен подменять их отдельные установки.
Как проявляется смешивание организаций
- Уведомление клиента A появляется в канале клиента B.
- После повторной авторизации одной компании интеграция перестает работать у другой.
- Список каналов в настройках меняется в зависимости от того, кто подключался последним.
- Webhook принимается корректно, но событие связывается с неверной записью клиента.
- Удаление приложения из одного workspace вызывает массовые ошибки авторизации.
- В журнале запросов постоянно используется один team id, tenant id или access token.
- Тестовая организация получает реальные уведомления рабочего клиента.
Почему один токен оказывается общим
- Токен сохранен в общем файле конфигурации или переменной окружения как единственное значение.
- Результат последней OAuth-авторизации обновляет одну глобальную строку в базе.
- Ключ кэша не содержит внутренний идентификатор организации и возвращает чужое подключение.
- Фоновая задача выбирает первый активный токен вместо подключения владельца задания.
- Вебхук определяется только по типу провайдера, без проверки workspace или tenant.
- Разработчик использовал тестовый токен при переносе интеграции в production.
- Модель данных связывает канал с пользователем, но не связывает его с организацией и установкой.
Чем отличаются Slack и Microsoft Teams
В Slack приложение устанавливается в конкретный workspace. Результат OAuth содержит идентификаторы команды и установки, а выданный токен относится к определенному контексту. Даже если одно приложение доступно многим workspace, каждую установку необходимо хранить отдельно и выбирать по team id либо enterprise id с учетом модели приложения.
В Microsoft Teams интеграция обычно работает через Microsoft Entra ID и Microsoft Graph. Контекст определяется tenant id, учетной записью или service principal, типом разрешений и установленным приложением. Access token имеет ограниченный срок жизни; его нельзя считать постоянным глобальным секретом. Для multi-tenant приложения авторизация и обновление доступа должны выполняться в контексте конкретного tenant.
Сначала остановите риск утечки
- Отключите массовые рассылки и фоновые задания, которые используют сомнительный общий токен.
- Не удаляйте записи и не отзывайте токен до определения всех зависимых организаций.
- Сохраните время, внутренний id клиента, внешний workspace или tenant, адрес назначения и результат последних запросов.
- Проверьте, отправлялись ли данные в чужие каналы, и действуйте по принятому плану реагирования на инциденты.
- Ограничьте доступ к журналам: токены, authorization headers и коды OAuth не должны попадать в открытый лог.
Как проверить текущую архитектуру
Проследите полный путь одного уведомления: от бизнес-события до вызова API провайдера. На каждом шаге должен сохраняться внутренний идентификатор организации. Если он теряется в очереди, задаче cron, webhook-обработчике или сервисе уведомлений, токен будет невозможно выбрать надежно.
- Найдите все места, где читаются access token, refresh token, bot token и webhook URL.
- Проверьте таблицы подключений: есть ли organization_id, provider, external_tenant_id и статус установки.
- Сравните внешний идентификатор в ответе OAuth с тем, который хранится в базе.
- Проверьте ключи кэша, дедупликации и очередей: в них должен участвовать id организации или установки.
- Убедитесь, что фоновая задача получает organization_id из полезной нагрузки, а не из текущей веб-сессии.
- Проверьте все fallback: автоматический выбор первого подключения опаснее явной ошибки «интеграция не настроена».
Какая модель данных нужна
Создайте отдельную сущность установки интеграции. Она связывает внутреннюю организацию с внешним пространством и хранит только относящиеся к этой установке настройки. Каналы, команды, подписки и маршруты уведомлений должны ссылаться на id установки, а не только на название провайдера.
- Внутренний organization_id или account_id.
- Провайдер и тип подключения: Slack, Microsoft Graph, Teams webhook или другой вариант.
- Внешний team id, enterprise id либо tenant id.
- Зашифрованные учетные данные или ссылка на секрет в защищенном хранилище.
- Набор выданных scopes и тип разрешений.
- Срок действия, время последнего обновления и состояние подключения.
- Кто и когда установил интеграцию, а также дата отзыва доступа.
- Версия настроек и служебные поля для безопасной ротации.
Для активных установок полезен уникальный индекс по провайдеру и внешнему идентификатору с учетом допустимой бизнес-модели. Он не заменяет проверки доступа, но не даст незаметно создать две конфликтующие записи.
Как выбирать токен при отправке
- Событие создается внутри конкретной организации и получает неизменяемый organization_id.
- Сервис уведомлений находит активную установку этой организации и нужного провайдера.
- Перед отправкой проверяется соответствие сохраненного внешнего workspace или tenant ожидаемому назначению.
- Краткоживущий access token обновляется в контексте этой установки, а не глобального приложения.
- Запрос отправляется в разрешенный канал или команду, связанную с той же установкой.
- В журнал записываются безопасные идентификаторы и код ответа без значения токена.
Если установка отсутствует, отключена или не соответствует назначению, операция должна завершаться контролируемой ошибкой. Подстановка любого другого рабочего токена ради успешной доставки недопустима.
Проверка входящих webhook и событий
Входящий запрос нельзя связывать с клиентом только по URL обработчика или имени приложения. Сначала проверьте подпись, временную метку и защиту от повторной доставки, затем извлеките подтвержденный внешний идентификатор и найдите соответствующую установку.
- Для Slack проверяйте подпись запроса и учитывайте team id или enterprise id из подтвержденного payload.
- Для Microsoft проверяйте токен или механизм валидации конкретного типа подписки и сверяйте tenant-контекст.
- Не доверяйте organization_id, который клиент может свободно передать в query-параметре.
- Храните внешний subscription id рядом с установкой и проверяйте его принадлежность.
- Обрабатывайте повторные события идемпотентно в рамках организации и провайдера.
Безопасное хранение и обновление токенов
- Не храните токены в исходном коде, открытых настройках админки и обычных журналах.
- Шифруйте секреты в базе или используйте специализированное хранилище с разграничением доступа.
- Отделяйте идентификатор секрета от его значения, чтобы журналировать выбор без раскрытия данных.
- Запрашивайте минимально необходимые scopes и регулярно проверяйте неиспользуемые разрешения.
- Обновляйте краткоживущие токены с блокировкой, чтобы параллельные процессы не перезаписывали результат.
- При отзыве доступа отключайте только соответствующую установку и связанные задания.
- Планируйте ротацию так, чтобы старое значение переставало использоваться после подтверждения нового.
Как перейти с общего токена без остановки всех уведомлений
- Составьте список организаций, каналов, фоновых заданий и внешних пространств, использующих интеграцию.
- Добавьте таблицу установок и новые связи, пока старый механизм еще доступен только для чтения.
- Определите владельца общего токена по API провайдера и привяжите его только к подтвержденной организации.
- Для остальных компаний запустите отдельную повторную OAuth-авторизацию или установку приложения.
- Перенесите маршруты уведомлений и подписки на id конкретных установок.
- Включайте новый выбор токена по организациям, контролируя ошибки и адреса доставки.
- После миграции запретите глобальный fallback, отзовите старые лишние секреты и удалите их из конфигурации.
Тесты изоляции организаций
- Создайте две тестовые организации с разными workspace или tenant и разными каналами.
- Отправьте одинаковое событие каждой компании и убедитесь, что назначения не пересекаются.
- Переустановите интеграцию у первой организации: токен и настройки второй не должны измениться.
- Отзовите доступ у одной компании и проверьте, что вторая продолжает работать.
- Повторите webhook с внешним id другой организации: обработчик обязан отклонить несоответствие.
- Запустите параллельное обновление токенов и проверьте отсутствие гонки и перезаписи.
- Убедитесь, что пользователь одной организации не видит каналы, установки и журналы другой.
Типичные неправильные исправления
- Добавить несколько токенов в конфигурационный файл и выбирать их по названию компании.
- Использовать email администратора как единственный идентификатор организации.
- Сохранить один refresh token и получать из него доступ для разных tenant.
- При ошибке авторизации автоматически переключаться на любой доступный токен.
- Передавать organization_id в URL webhook без проверки подписи и внешнего контекста.
- Показывать полный токен в админке для удобства отладки.
- Отозвать общий токен до инвентаризации и одновременно остановить все действующие подключения.
Как предотвратить повторение
- Сделайте tenant-контекст обязательным аргументом сервисов интеграции и фоновых заданий.
- Добавьте автоматические тесты на перекрестный доступ между двумя организациями.
- Контролируйте уникальность внешней установки и аудит всех операций с секретами.
- Проверяйте scopes, статус и внешний id перед каждой чувствительной операцией.
- Мониторьте рост ошибок 401, 403 и несоответствий workspace или tenant отдельно по установкам.
- Документируйте процедуру повторной авторизации, отзыва доступа и удаления организации.
Итог
Один общий токен не подходит для обслуживания нескольких независимых организаций. Безопасная интеграция хранит отдельную установку для каждого клиента, сохраняет tenant-контекст на всем пути события, проверяет входящие запросы и не подставляет чужие учетные данные при ошибке.
Если токены Slack или Microsoft Teams уже смешались, я могу провести аудит схемы подключений, найти места потери organization_id, разделить установки, настроить безопасное хранение и выполнить миграцию без массовой остановки уведомлений.