App Store Connect API 401: как исправить JWT в 2026?

В документации Apple для App Store Connect API указано, что срок действия JWT ограничен двадцатью минутами; это означает, что даже корректно подписанный токен может стать непригодным из-за времени или задержки выполнения (требования Apple к генерации JWT).

Симптом → быстрый способ исправления

Локальный скрипт создаёт JWT, но удалённый Runner возвращает 401: не перевыпускайте ключ сразу, а сначала подтвердите, что обращаетесь именно к App Store Connect API, затем проверьте источник ключа, поля токена, часы, роль и передачу секретов.

Если минимальный запрос из обеих сред продолжает завершаться 401, сохраните request ID, код ошибки и обезличенный контекст выполнения и передайте их Apple. Массовая отмена ключей до такой проверки может только расширить простой автоматизации.

Эта статья предназначена для вас, если fastlane загружает сборки в TestFlight, но внезапно перестал проходить API-аутентификацию; если после переноса публикации на удалённый Mac локальный запуск успешен, а CI отвечает 401; либо если вы самостоятельно подписываете JWT для вызова App Store Connect API.

Граница проблемы

Ошибка App Store Connect API 401 не означает автоматически, что «сломался JWT». Один и тот же симптом может появиться на разных уровнях:

  • запрос отправлен не в тот сервис Apple;
  • .p8 создан для другой области доступа;
  • в заголовке JWT указан чужой Key ID;
  • Issuer ID не соответствует команде;
  • iat или exp не проходят проверку времени;
  • подпись создана не тем закрытым ключом;
  • роль ключа не разрешает требуемую операцию;
  • fastlane получил пустую переменную или повреждённые переносы строк;
  • удалённый Mac использует часы, отличающиеся от доверенного времени.

Отдельно фиксируйте адрес запроса, HTTP-статус, код Apple, request ID и инструмент, который инициировал обращение. Apple описывает структуру ошибок и диагностические признаки в официальном руководстве по обработке ошибок App Store Connect API.

Не смешивайте четыре разных действия:

  • вызов App Store Connect API;
  • вызов App Store Server API;
  • загрузку сборки через Transporter;
  • действие fastlane, которое может использовать API только на одном этапе, а на другом — отдельный механизм загрузки.

Например, успешное создание JWT ещё не подтверждает, что этот токен принят конкретным endpoint. Точно так же ошибка при загрузке TestFlight не доказывает, что проблема находится в подписи приложения: сбой мог произойти раньше, на этапе получения данных через API.

Проверка ключа и endpoint

Первый рабочий вопрос — где был создан ключ и к какому сервису относится операция. Имя файла, комментарий в секрет-хранилище и расширение .p8 не являются доказательством назначения. Проверяйте источник в интерфейсе Apple Developer и сопоставляйте его с фактическим URL запроса.

У App Store Connect API есть собственные настройки ключей, идентификаторы и области прав. Страница Apple с описанием ключей App Store Connect API должна быть вашей исходной точкой, а не старый файл из репозитория или переменная с названием вроде APPLE_PRIVATE_KEY.

Не используйте ключ In-App Purchase для вызова App Store Connect API только потому, что оба файла имеют формат .p8. Для диагностики выпишите в защищённую рабочую заметку:

  • обозначение сервиса, в котором создан ключ;
  • Key ID без публикации самого секрета;
  • Issuer ID команды;
  • endpoint;
  • требуемую операцию;
  • роль и область приложений;
  • среду выполнения: локальный терминал, SSH или CI Runner.

Key ID можно сверять с кабинетом, но закрытый ключ, полный JWT, App ID, Bundle ID и адрес хоста в журнал не записывайте. В статье, тикете или отчёте используйте явные обозначения <KEY_ID>, <ISSUER_ID>, <APP_ID> и <BUNDLE_ID>, чтобы обезличивание было заметно при чтении.

Различие аутентификации и авторизации

HTTP 401 относится к отказу в аутентификации, но граница между 401 и ошибкой прав должна подтверждаться ответом Apple, а не предположением. Если токен распознан, но роль не разрешает доступ к ресурсу, проблема уже находится в области авторизации. Именно поэтому полезно проверить не только публикацию и загрузку, но и минимальный разрешённый запрос.

В разделе Users and Access проверьте, активен ли доступ API у нужного пользователя или команды, а затем сопоставьте роль ключа с операцией. Не расширяйте права «на всякий случай»: это затрудняет аудит и скрывает настоящую причину.

Слои JWT и системное время

JWT нужно разбирать по трём независимым слоям: header, payload и подпись. Проверяйте их последовательно, не заменяя весь токен новым после каждой ошибки.

В header для App Store Connect API должны совпадать алгоритм и идентификатор ключа. Apple указывает использование ES256; kid должен ссылаться на тот ключ, которым фактически подписан токен. Если закрытый файл был заменён, переменная указывает на старый путь или секрет обрезан при передаче, внешний вид payload может оставаться правильным, но подпись будет неприменима.

В payload проверьте:

  • iss — Issuer ID нужной команды;
  • iat — момент выпуска;
  • exp — момент окончания действия;
  • aud — назначение токена;
  • отсутствие лишних изменений при сериализации.

По требованиям Apple, срок между временными полями ограничен двадцатью минутами. Поэтому токен, созданный заранее в одном задании и использованный позже в другом, может стать недействительным. Не переносите готовый JWT между этапами CI, если его можно выпустить непосредственно перед запросом.

Системные часы особенно важны для удалённого Mac. Сравните время в локальной среде, SSH-сессии и Runner, но не публикуйте полный вывод, если в нём присутствуют адреса и идентификаторы. Проверьте синхронизацию времени, состояние после сна, восстановление снимка виртуальной среды и ручные изменения часов. Ошибка может проявляться только на удалённой машине: локальная подпись будет выглядеть корректно, а Apple получит токен с будущим iat или уже истёкшим exp.

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

Права и область доступа

Проверка роли должна идти после проверки ключа и временных полей, но до реальной загрузки. Иначе вы можете принять отказ в доступе к приложению за неисправную подпись.

Сопоставьте операцию с ролью: чтение сведений о приложении, работа с TestFlight, управление пользователями и публикация могут требовать разных разрешений. Подробная матрица находится в справочнике Apple по ролям и разрешениям. Используйте её как источник для конкретной операции, а не как основание выдать ключу максимальные права.

Для изоляции причины начните с минимального чтения, которое разрешено данной роли. Если оно проходит, а целевая операция нет, JWT и endpoint, вероятно, уже работают, а искать нужно область ресурсов или полномочия. Если не проходит даже минимальная проверка, возвращайтесь к ключу, iss, kid, подписи и времени.

Статус команды также имеет значение. Договоры, доступ пользователя, состояние организации и назначение приложения могут влиять на операции, связанные с App Store Connect. Такой случай нельзя описывать как системный сбой Apple только на основании нескольких сообщений разработчиков: форумы полезны для поиска симптомов, но не подтверждают общую неисправность платформы.

fastlane и удалённый Mac

На удалённом Mac чаще всего ломается не сам JWT, а путь доставки секрета. fastlane может получать параметры из key_id, issuer_id, key_filepath или key_content; точный вариант зависит от вашей конфигурации и действия. Сверяйте его с официальной документацией fastlane по App Store Connect API.

Выполните диагностику в шести шагах.

  • [ ] Зафиксируйте endpoint, HTTP-статус, код Apple, request ID и название fastlane action, не сохраняя полный токен.
  • [ ] Сверьте сервис создания .p8, Key ID и Issuer ID с защищённой записью команды.
  • [ ] Проверьте, что выбран ES256, а закрытый ключ соответствует указанному kid.
  • [ ] Сравните iat, exp, iss и aud, затем проверьте часы локальной и удалённой среды.
  • [ ] Запустите обезличенный минимальный запрос отдельно в интерактивном терминале, SSH-сессии и CI-задаче.
  • [ ] Только после успешной проверки авторизации выполните реальную загрузку TestFlight.

В интерактивном терминале переменные могут быть доступны из профиля оболочки, а в SSH — отсутствовать. В CI значение может существовать только для конкретной ветки, окружения или шага. Рабочий каталог тоже меняет результат: относительный key_filepath способен указывать на другой файл, когда Runner запускает fastlane не из каталога проекта.

Если используется key_content, проверьте обработку переносов строк. Base64-представление должно декодироваться только в памяти или во временный защищённый файл с контролируемыми правами доступа. Не выводите содержимое после декодирования и не добавляйте .p8 в артефакты сборки.

Разделяйте три типа секретов:

  1. API-ключ для запросов App Store Connect.
  2. Сертификаты и закрытые ключи для подписи приложения.
  3. Данные загрузчика или Transporter, если конкретный процесс их использует.

Замена одного секрета не исправляет другой. Она также не должна выполняться автоматически при каждом 401: перед отзывом ключа проверьте, что новый ключ создан, безопасно передан в тестовую среду и успешно прошёл минимальный запрос. Старый ключ отзывайте только после подтверждения замены и оценки всех задач, которые ещё его используют.

Для хранения секретов и журналов заранее определите права доступа и срок хранения. В сценариях удалённой работы KVMFLUX можно оценить, подходит ли вам постоянно доступная macOS-среда для такого Runner, но сама аренда не заменяет проверку API-прав и конфигурации fastlane.

Независимая проверка исправления

Признаком исправления считается не строка «JWT создан», а успешное прохождение всей цепочки. Сначала выполните минимальный запрос с тем же ключом, тем же endpoint и тем же способом передачи секрета, который использует автоматизация.

Затем повторите его в том же окружении, где обычно запускается публикация:

  • с тем же пользователем SSH или системным агентом;
  • с теми же переменными CI;
  • из того же рабочего каталога;
  • с тем же fastlane action;
  • при тех же настройках времени и доступа к сети.

Только после этого запустите реальную загрузку TestFlight. Запишите обезличенный пакет доказательств: время запуска, среду, исполнителя, Key ID в сокращённом виде, тип ключа, endpoint, стадию сбоя, request ID и результат. Полный JWT и приватный ключ в такой пакет не входят.

Если новый ключ, другая сеть и повторный запуск продолжают возвращать 401 на официально разрешённом минимальном запросе, остановите бесконечную ротацию. Передайте Apple request ID, код ответа, время, endpoint и описание проверок без секретов. Это полезнее, чем несколько последовательно отозванных ключей, которые усложняют восстановление публикации.

Решение по среде запуска

Вариант запуска Что проверять в первую очередь Когда выбирать
Локальный Mac Файл .p8, профиль оболочки, часы и права пользователя Когда публикация выполняется вручную и среда стабильна
SSH-сеанс Переменные окружения, рабочий каталог, права файла и профиль входа Когда вы обслуживаете Mac удалённо, но запускаете команды вручную
CI Runner на удалённом Mac Область секретов, передачу переносов строк, синхронизацию времени и журналы Когда TestFlight должен загружаться без постоянного ручного входа
Новый ключ после проверки Замена всех зависимых задач и безопасное подтверждение Только если ключ скомпрометирован, отозван или действительно неприменим

Если вам нужен Mac лишь для разовой проверки цепочки, сравните стоимость временного доступа с покупкой отдельной машины; варианты и условия аренды можно посмотреть на странице планов KVMFLUX. Для постоянной публикации важнее не только цена, но и возможность сохранить состояние Runner, правила доступа, журналирование и процедуру восстановления.

На практике удалённый Mac не исправит неверный iss, неподходящую роль или перепутанный ключ. Его преимущество проявляется в другом: вы можете держать macOS-инструменты, fastlane и рабочее окружение доступными для повторяемого задания, не перенося секреты между несколькими личными компьютерами. Перед выбором такой схемы изучите ответы KVMFLUX по удалённому доступу и заранее определите, кто отвечает за секреты и восстановление.

Если сейчас публикация выполняется на разных локальных машинах или в нестабильном CI, у текущего подхода есть несколько конкретных недостатков: переменные окружения расходятся между разработчиками, часы и версии инструментов отличаются, рабочий каталог меняется, а диагностика 401 теряет воспроизводимость. Постоянно доступный удалённый Mac не отменяет эти риски, но позволяет закрепить один Runner, единый способ инъекции секретов и понятный журнал выполнения. Поэтому разумно сначала провести на KVMFLUX настоящую загрузку TestFlight, а затем решить, достаточно ли аренды на неделю для теста или вам нужен месячный режим как постоянная машина публикации.

Часто задаваемые вопросы

Почему App Store Connect API отвечает 401 NOT_AUTHORIZED, хотя запрос выглядит правильным?

Сначала проверьте сам endpoint: ключ для App Store Connect API нельзя автоматически считать подходящим для другого сервиса Apple. Затем сравните Key ID, Issuer ID, алгоритм ES256, временные поля JWT и права ключа. Только после этого сопоставляйте запрос с документацией об ошибках и сохраняйте request ID для диагностики.

Почему локальная проверка JWT проходит, а Apple всё равно отклоняет запрос?

Локальная проверка обычно подтверждает лишь структуру и подпись токена. Она не доказывает, что Apple видит правильный ключ, что Issuer ID относится к нужной команде, что часы удалённого Runner синхронизированы и что роль разрешает операцию. Сравните полный путь выполнения, а не только результат генерации JWT.

Почему fastlane не принимает API Key при загрузке сборки в TestFlight?

Проверьте, какие значения fastlane получает фактически: key_id, issuer_id, key_filepath или key_content. В SSH и CI переменные могут иметь другой набор, формат переносов строк или рабочий каталог. Отдельно убедитесь, что вы не перепутали JWT для API с сертификатом подписи приложения и учётными данными загрузчика.

Можно ли использовать App Store Connect API Key вместо ключа In-App Purchase?

Нет, одинаковое расширение .p8 не доказывает взаимозаменяемость. Ключи создаются в разных разделах и имеют разные области применения. Сверьте источник ключа в кабинете Apple, endpoint и требуемую операцию. Если назначение не совпадает, исправление полей JWT не сделает такой ключ подходящим для другого API.

Запускайте автоматизацию на удалённом Mac с KVMFLUX

Арендуйте удалённый Mac для сборки приложений, подписания релизов и загрузки тестовых версий. Работайте в отдельной среде с постоянным доступом и ресурсами для задач разработки. Храните ключи и настройки автоматизации в контролируемом окружении без привязки к личному компьютеру. Выберите подходящую конфигурацию KVMFLUX и начните работу с удалённым Mac по мере необходимости.

Mac Mini M4 · 16GB / 256GB
Сутки$19.3 /сутки
Неделя$52.2 /нед.
Месяц$96.7 /мес.
Квартал$263 /квартал