Ошибка invalid_grant при авторизации через Apple ID (iOS, бекенд)
Реализовывал я вход через Apple ID в одном приложении на Flutter, а бекенд был на Laravel. Научили приложение генерировать токен, отправляли токен в бекенд, бекенд проверял его у Эпл, а Эпл в ответ присылал invalid_grant
.
0. Вводная, настройка входа через Apple ID
Вся настройка занимает несколько шагов, после которых мы получаем настройки и используем их в приложении и в коде бекенда. Что за шаги? Поехали:
- Генерируем
App ID
- Генерируем
Service ID
- Создаём приватный ключ
Private Key
- Генерируем
clientSecret
на основе данных из шагов 1−3
Подробное описание шагов доступно, например, в репозитории одной из библиотек для Laravel.
На выходе у нас есть самое важное: clientId
и clientSecret
, которые мы используем и внутри приложения, и внутри нашего бекенда.
С подготовкой закончили. Теперь вернёмся к нашей ошибке invalid_grant
.
1. Смотрим документацию
Документация Эпл говорит следующее:
The authorization grant or refresh token is invalid, typically due to a mismatched or invalid client identifier, invalid code (expired or previously used authorization code), or invalid refresh token.
То есть что-то не так с токеном, которое нам отдало приложение. Идём проверять, действительно ли что-то не так с этим кодом.
2. Проверяем явные ошибки: повторные запросы, «протухание» кода
Когда Эпл отдаёт нам в приложении authCode, то мы отправляем его в бекенд, а бекенд уже с этим кодом запрашивает у Эпл данные о пользователе. Тут может возникнуть несколько проблем:
- Вы как-то не так отправляете код в бекенд: он как-нибудь конкатенируется или парсится из json криво, приходит не в том формате. Обычно это редкость, но всё же стоит проверить.
- Токен успевает протухнуть. Это редкость, потому что токен живёт 5 минут, вряд ли у вас запрос между приложением и бекендом ходит больше 5 минут. Но имейте ввиду.
- Проблема почаще: наличие повторных запросов. Например, вы в коде через провайдер получаете данные о пользователе, а потом логините его через этот же провайдер. В обоих методах провайдер шлёт запрос в Эпл, и на второй запрос authCode протухает.
Исключаем всё это, но ошибка всё ещё появляется. Что делать? Мы попробовали перегенерировать clientId
и clientSecret
, но это не помогло. Продолжаем гуглить.
3. Пробуем заменить Service ID на App ID
Находим кое-что! Удалось найти интересное описание проблемы в одном из репозиториев node. js-библиотек для входа через Apple ID:
invalid_grant
when authorization code is generated by iOS App
Fix: If the authorizationCode was generated by your app, you should use your App ID as your clientId and not your service one. Discussion: https://github.com/ananay/apple-auth/issues/13
То есть если мы генерируем токен в приложении, то в качестве clientId
нужно использовать App ID, а не Service ID. При этом почти везде все пишут, что в качестве clientId
нужно использовать именно Service ID.
Выходит, если у нас меняется clientId
, то нужно перегенрировать и clientSecret
, потому что он напрямую зависит от clientId
. Генерируем заново. Прописываем новый clientId
в приложении, а также новые clientId
и clientSecret
в бекенде.
Это сработало! 🎉