Если несколько организаций используют один токен интеграции, сообщения могут уходить не в тот 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.

Сначала остановите риск утечки

  1. Отключите массовые рассылки и фоновые задания, которые используют сомнительный общий токен.
  2. Не удаляйте записи и не отзывайте токен до определения всех зависимых организаций.
  3. Сохраните время, внутренний id клиента, внешний workspace или tenant, адрес назначения и результат последних запросов.
  4. Проверьте, отправлялись ли данные в чужие каналы, и действуйте по принятому плану реагирования на инциденты.
  5. Ограничьте доступ к журналам: токены, 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 и тип разрешений.
  • Срок действия, время последнего обновления и состояние подключения.
  • Кто и когда установил интеграцию, а также дата отзыва доступа.
  • Версия настроек и служебные поля для безопасной ротации.

Для активных установок полезен уникальный индекс по провайдеру и внешнему идентификатору с учетом допустимой бизнес-модели. Он не заменяет проверки доступа, но не даст незаметно создать две конфликтующие записи.

Как выбирать токен при отправке

  1. Событие создается внутри конкретной организации и получает неизменяемый organization_id.
  2. Сервис уведомлений находит активную установку этой организации и нужного провайдера.
  3. Перед отправкой проверяется соответствие сохраненного внешнего workspace или tenant ожидаемому назначению.
  4. Краткоживущий access token обновляется в контексте этой установки, а не глобального приложения.
  5. Запрос отправляется в разрешенный канал или команду, связанную с той же установкой.
  6. В журнал записываются безопасные идентификаторы и код ответа без значения токена.

Если установка отсутствует, отключена или не соответствует назначению, операция должна завершаться контролируемой ошибкой. Подстановка любого другого рабочего токена ради успешной доставки недопустима.

Проверка входящих webhook и событий

Входящий запрос нельзя связывать с клиентом только по URL обработчика или имени приложения. Сначала проверьте подпись, временную метку и защиту от повторной доставки, затем извлеките подтвержденный внешний идентификатор и найдите соответствующую установку.

  • Для Slack проверяйте подпись запроса и учитывайте team id или enterprise id из подтвержденного payload.
  • Для Microsoft проверяйте токен или механизм валидации конкретного типа подписки и сверяйте tenant-контекст.
  • Не доверяйте organization_id, который клиент может свободно передать в query-параметре.
  • Храните внешний subscription id рядом с установкой и проверяйте его принадлежность.
  • Обрабатывайте повторные события идемпотентно в рамках организации и провайдера.

Безопасное хранение и обновление токенов

  • Не храните токены в исходном коде, открытых настройках админки и обычных журналах.
  • Шифруйте секреты в базе или используйте специализированное хранилище с разграничением доступа.
  • Отделяйте идентификатор секрета от его значения, чтобы журналировать выбор без раскрытия данных.
  • Запрашивайте минимально необходимые scopes и регулярно проверяйте неиспользуемые разрешения.
  • Обновляйте краткоживущие токены с блокировкой, чтобы параллельные процессы не перезаписывали результат.
  • При отзыве доступа отключайте только соответствующую установку и связанные задания.
  • Планируйте ротацию так, чтобы старое значение переставало использоваться после подтверждения нового.

Как перейти с общего токена без остановки всех уведомлений

  1. Составьте список организаций, каналов, фоновых заданий и внешних пространств, использующих интеграцию.
  2. Добавьте таблицу установок и новые связи, пока старый механизм еще доступен только для чтения.
  3. Определите владельца общего токена по API провайдера и привяжите его только к подтвержденной организации.
  4. Для остальных компаний запустите отдельную повторную OAuth-авторизацию или установку приложения.
  5. Перенесите маршруты уведомлений и подписки на id конкретных установок.
  6. Включайте новый выбор токена по организациям, контролируя ошибки и адреса доставки.
  7. После миграции запретите глобальный 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, разделить установки, настроить безопасное хранение и выполнить миграцию без массовой остановки уведомлений.