Интеграция работает после авторизации, а через час начинает получать отказ. Разработчик вручную подставляет новый токен, и всё снова оживает. Это не нестабильный REST. Приложение не завершило OAuth-цикл: обновило access_token, но потеряло новую пару или столкнулось само с собой в двух worker.
Сначала найдите место, где хранится expires_at. Если приложение сохраняет только строку токена, причина уже сильно сузилась.
Что хранить
| Поле | Зачем | Правило |
|---|---|---|
| access_token | REST-запросы | Шифровать, не логировать |
| refresh_token | Получить новую пару | Заменять вместе с access |
| expires_at | Обновиться заранее | Считать на сервере |
| portal host | Маршрутизация | Проверять после смены адреса |
Алгоритм без гонки
- Перед REST-запросом сравните время с
expires_at. - Если срок близок, возьмите lock на установку.
- После lock перечитайте токены: другой worker мог обновить их.
- Отправьте refresh-запрос на официальный endpoint.
- В одной транзакции сохраните новую пару и срок.
- Освободите lock и повторите бизнес-запрос один раз.
POST https://oauth.bitrix24.tech/oauth/token/
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&client_id=...&client_secret=...&refresh_token=...Многоточия здесь обязательны: реальные секреты не попадают в статью, git или журнал. На production логируйте код ответа, время и идентификатор установки, но маскируйте credential.
Почему два worker теряют доступ
Worker A и B видят почти истёкший токен. Оба читают один refresh_token. A получает новую пару и сохраняет её. B отправляет старое значение позже и перезаписывает состояние или получает отказ. Исправление — single-flight: refresh одной установки выполняет один процесс.
PASS: при одновременном тесте endpoint вызывается один раз, оба worker читают одну новую пару. FAIL: в логах два refresh-запроса с миллисекундной разницей.
Диагностика по ответу
- 401 после часа: проверьте вызов refresh и сохранение результата.
- invalid_grant: прекратите бесконечный retry; установка требует отдельной проверки.
- 403 только на методе: это может быть scope или права пользователя.
- 429: не путайте ротацию с лимитом запросов.
Частые ошибки
Сохраняют только access_token. Следующее обновление использует старый refresh_token.
Ждут первого отказа. Пользовательский запрос запускает обслуживание credential.
Логируют body. Журнал становится копией секретов.
Повторяют бесконечно. Ошибка авторизации не лечится backoff.
Чек-лист перед запуском
- Срок хранится отдельно.
- Пара заменяется атомарно.
- Есть lock на установку.
- Секреты маскируются.
- После refresh один retry.
- invalid_grant открывает инцидент.
- Тест двух worker пройден.
Как проверить ротацию до production
Сделайте тестовую установку и сократите внутренний порог обновления так, чтобы не ждать реального истечения. Запустите два экземпляра worker одновременно. В журнале должен появиться один захват lock, один запрос к OAuth endpoint и одна атомарная запись. Второй процесс после ожидания перечитывает хранилище и продолжает с новой парой.
Отдельно симулируйте падение после ответа OAuth, но до сохранения. Это неприятное окно: внешний сервис уже выдал новую пару, а приложение её не записало. Транзакция базы не может откатить внешний вызов, поэтому нужен журнал попытки и понятный путь повторной авторизации. Не пытайтесь скрыть такой случай бесконечными повторами.
Какие поля оставлять в журнале
Достаточно идентификатора установки, домена портала, времени начала и конца, HTTP-кода, типа ошибки и correlation id. Полный access_token, refresh_token и client_secret должны проходить redaction до записи. Проверьте не только обычный log, но и error tracker: исключение часто захватывает request body автоматически.
После успешного теста включите метрику времени до истечения и счётчик refresh-ошибок. Первый показывает застрявшую ротацию до отказа пользователей, второй помогает отличить единичную сеть от сломанной установки.
Проверьте аварийные ветки
Отключите сеть после успешного refresh и до исходного REST-запроса. Новый токен уже должен быть в хранилище, а бизнес-задача — вернуться в очередь с ограниченным повтором. Затем оборвите сеть до ответа OAuth: состояние токенов не меняется, а попытка получает отдельный статус unknown outcome.
Проверьте перезапуск worker во время lock. У блокировки должен быть срок и владелец; вечный lock остановит интеграцию не хуже истёкшего токена. Слишком короткий срок тоже опасен: второй процесс войдёт в refresh, пока первый ещё ждёт сеть. Выберите значение по измеренному времени ответа с запасом, а не по привычке.
После повторной авторизации убедитесь, что старые задачи используют актуальную установку. Не кладите копию токена в payload очереди: сообщение может пролежать дольше его срока. Worker получает credential непосредственно перед вызовом REST.
Контрольный критерий после выпуска: интеграция проходит два срока access_token без ручного вмешательства, а метрика refresh показывает ожидаемые вызовы. Проверьте системные часы всех worker; сильный дрейф времени заставляет один процесс считать токен действующим, когда другой уже запускает ротацию.
Что делать, если не помогло
Отделите OAuth от бизнес-логики. Выполните один refresh в тестовой утилите из доверенного окружения, сохраните только код и время ответа. Затем проверьте права по инструкции про 403 в REST Битрикс24. После смены адреса используйте проверку URL портала. CTR1 может разобрать интеграцию Битрикс24 и убрать ручную замену токенов.



