Как решить ошибку "Invalid object: the property 'grant_type' is missing" при POST запросе к OAuth API из 1С?

Программист 1С v8.3 (Управляемые формы)
← На главную

При попытке интеграции системы 1С с внешними сервисами, использующими протокол авторизации OAuth 2.0, разработчики часто сталкиваются с ошибками. Одна из таких распространенных ошибок — {"httpCode":"400","httpMessage":"Bad Request","moreInformation":"[JSV0002] Invalid object: the property 'grant_type' is missing."}. Эта ошибка указывает на то, что сервер авторизации не смог найти или правильно интерпретировать обязательный параметр grant_type в вашем запросе.

Давайте подробно разберем, почему возникает эта ошибка и как ее устранить, используя стандартные средства 1С и COM-объект WinHttp.WinHttpRequest.5.1.

Причина ошибки "property 'grant_type' is missing"

Основная причина этой ошибки кроется в некорректном способе передачи параметров grant_type и scope. Согласно стандарту OAuth 2.0, особенно для потока авторизации "Client Credentials" (учетные данные клиента), эти параметры должны передаваться в теле POST-запроса, а не в заголовках. При этом тип содержимого (Content-Type) такого запроса должен быть application/x-www-form-urlencoded.

Когда API-документация указывает, что параметры должны быть в "formData", это чаще всего означает именно формат application/x-www-form-urlencoded. В этом формате параметры передаются в виде пар "ключ=значение", разделенных амперсандами (&), аналогично параметрам в URL-строке.

Если вы пытаетесь передать grant_type в заголовках запроса или как часть JSON-тела, когда сервер ожидает application/x-www-form-urlencoded, он не сможет найти нужное свойство и вернет ошибку об его отсутствии. Также ошибка может возникнуть, если заголовок Content-Type установлен неверно (например, application/json), и сервер пытается разобрать тело запроса как JSON.

Решение проблемы с использованием стандартного объекта 1С: HTTPСоединение

Для корректной передачи параметров grant_type и scope в теле запроса с использованием объекта HTTPСоединение в 1С, вам необходимо выполнить следующие шаги:

  1. Определить адрес сервера и ресурса. Разделите полный URL на хост (адрес сервера) и путь к ресурсу.
  2. Создать объект HTTPСоединение. Для HTTPS-протокола используйте Новый ЗащищенноеСоединениеOpenSSL.
  3. Сформировать заголовки запроса. В заголовках обязательно укажите Content-Type: application/x-www-form-urlencoded, а также Authorization, Accept, RqUID и x-ibm-client-id, если это требуется вашим API.
  4. Сформировать тело запроса. Тело запроса должно быть обычной строкой, где параметры grant_type и scope указаны в формате "ключ=значение", разделенные амперсандами.
  5. Создать HTTPЗапрос и установить тело. Используйте метод УстановитьТелоИзСтроки для передачи сформированной строки в тело запроса.
  6. Отправить запрос и обработать ответ.

Рассмотрим пример кода:


// 1. Определяем адрес сервера и ресурса
АдресСервера = "dev.api.sberbank.ru"; // Ваш хост
АдресРесурса = "/ru/prod/tokens/v2/oauth"; // Путь к ресурсу API

// 2. Создаем объект HTTPСоединение
// Порт 443 используется для HTTPS
Соединение = Новый HTTPСоединение(АдресСервера, 443, , , , 30, Новый ЗащищенноеСоединениеOpenSSL);

// 3. Формируем заголовки запроса
Заголовки = Новый Соответствие;
Заголовки.Вставить("Accept", "application/json");
Заголовки.Вставить("Content-Type", "application/x-www-form-urlencoded");
// Заголовок Authorization: Basic client_id:client_secret, закодированный в Base64.
// ОБЯЗАТЕЛЬНО: client_id:client_secret должны быть закодированы в Base64.
// Например, если client_id = "myclientid" и client_secret = "myclientsecret",
// то строка "myclientid:myclientsecret" кодируется в Base64.
Заголовки.Вставить("Authorization", "Basic <ВАШ_BASE64_КОД>"); 
Заголовки.Вставить("RqUID", "e9FAE9fcee76276Ffdeb11b8A4Dde85e"); // Уникальный идентификатор запроса
Заголовки.Вставить("x-ibm-client-id", "<ВАШ_X-IBM-CLIENT-ID>"); // Клиентский ID, если требуется API

// 4. Формируем тело запроса в формате application/x-www-form-urlencoded
// Параметры grant_type и scope передаются в виде строки "ключ=значение&ключ2=значение2"
ДанныеТелаСтрокой = "grant_type=client_credentials&scope=<ВАШ_SCOPE>"; 
// Например: "grant_type=client_credentials&scope=read write" или "grant_type=client_credentials&scope="
// Убедитесь, что значение scope соответствует документации API.
// Если scope пустой, убедитесь, что это допустимо API.

// 5. Создаем HTTPЗапрос и устанавливаем тело из строки
Запрос = Новый HTTPЗапрос(АдресРесурса, Заголовки);
Запрос.УстановитьТелоИзСтроки(ДанныеТелаСтрокой, КодировкаТекста.UTF8); // Важно указать кодировку

// 6. Отправляем запрос и получаем ответ
ОтветСервера = Соединение.ОтправитьДляОбработки(Запрос);

// Получаем тело ответа как строку
ТелоОтветаJSON = ОтветСервера.ПолучитьТелоКакСтроку();
Сообщить(ТелоОтветаJSON);

// Очищаем соединение
Соединение = Неопределено;

Важные моменты:

Решение проблемы с использованием COM-объекта WinHttp.WinHttpRequest.5.1

В некоторых случаях, особенно при возникновении трудностей со стандартным объектом HTTPСоединение (например, из-за специфических требований к заголовкам или проблемам с TLS-соединением на устаревших ОС), может помочь использование COM-объекта WinHttp.WinHttpRequest.5.1. Этот объект предоставляет более низкоуровневый контроль над HTTP-запросами.

Давайте посмотрим, как адаптировать код для использования WinHttp.WinHttpRequest.5.1, корректно передавая grant_type и scope в теле запроса.

  1. Создать COM-объект.
  2. Настроить параметры (опционально). Например, кодировку.
  3. Открыть соединение. Метод Open принимает тип запроса, URL и асинхронность.
  4. Установить заголовки. Метод SetRequestHeader.
  5. Отправить запрос с телом. Метод Send принимает строку тела запроса.
  6. Получить ответ.

Пример кода:


JSONДанные = Новый Структура("ВыполненоСОшибками, ОтветОтСервера");

// Полный URL для запроса токена. Включает хост и путь.
СтрокаСоединения = "https://dev.api.sberbank.ru/ru/prod/tokens/v2/oauth"; 

Попытка
    WinHttp = Новый COMОбъект("WinHttp.WinHttpRequest.5.1");
    WinHttp.Option(2, "utf-8"); // Устанавливаем кодировку для ответа

    // Открываем соединение: POST-запрос по указанной СтрокеСоединения, синхронно (0)
    WinHttp.Open("POST", СтрокаСоединения, 0); 

    // Устанавливаем заголовки запроса
    WinHttp.SetRequestHeader("Accept-Language" , "ru"); // Язык ответа
    WinHttp.SetRequestHeader("Accept-Charset" , "utf-8"); // Кодировка символов
    WinHttp.SetRequestHeader("Content-Language", "ru"); // Язык содержимого
    WinHttp.SetRequestHeader("Content-Charset" , "utf-8"); // Кодировка содержимого
    WinHttp.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8"); // Важно!
    
    // Заголовок Authorization, закодированный в Base64
    WinHttp.SetRequestHeader("Authorization", "Basic <ВАШ_BASE64_КОД>"); 
    WinHttp.SetRequestHeader("Accept", "application/json");
    WinHttp.SetRequestHeader("RqUID", "e9FAE9fcee76276Ffdeb11b8A4Dde85e");
    WinHttp.SetRequestHeader("x-ibm-client-id", "<ВАШ_X-IBM-CLIENT-ID>");

    // Формируем тело запроса как строку в формате application/x-www-form-urlencoded
    ЗапросСтрокаТела = "grant_type=client_credentials&scope=<ВАШ_SCOPE>"; 
    // Например: "grant_type=client_credentials&scope=read write"

    // Отправляем запрос, передавая ЗапросСтрокаТела в теле запроса
    WinHttp.Send(ЗапросСтрокаТела); 

    ТекстОтвета = WinHttp.ResponseText; // Получаем текст ответа от сервера
    
    // Обработка JSON-данных ответа
    ЧтениеJSON = Новый ЧтениеJSON;
    ЧтениеJSON.УстановитьСтроку(ТекстОтвета);
    JSONДанные.ВыполненоСОшибками = Ложь;
    JSONДанные.ОтветОтСервера = ПрочитатьJSON(ЧтениеJSON, Ложь);
Исключение
    Сообщить("Произошла ошибка при выполнении HTTP-запроса: " + ОписаниеОшибки());
    JSONДанные.ВыполненоСОшибками = Истина;
КонецПопытки;

Сообщить(JSONДанные.ОтветОтСервера);

Обратите внимание, что в этом примере ЗапросСтрокаТела содержит параметры grant_type и scope в формате application/x-www-form-urlencoded, и она передается непосредственно в метод WinHttp.Send(). Это ключевое отличие от некорректной передачи этих параметров в заголовках.

Устранение ошибки "Client id not registered." (httpCode: 401 Unauthorized)

После того, как вы успешно решили проблему с отсутствующим grant_type, вы можете столкнуться с новой ошибкой: {"httpCode":"401","httpMessage":"Unauthorized","moreInformation":"Client id not registered."}.

Эта ошибка означает, что сервер авторизации успешно принял ваш запрос и обнаружил параметры grant_type и scope, но не смог авторизовать вас из-за проблем с идентификационными данными клиента. Давайте выясним причины и способы их устранения:

  1. Неправильное кодирование client_id:client_secret в Base64.

    Для заголовка Authorization: Basic требуется, чтобы строка client_id:client_secret (включая двоеточие) была закодирована в Base64. Даже небольшая ошибка в кодировании или использование неверных значений приведет к ошибке авторизации. Обязательно перепроверьте:

    • Правильность написания client_id.
    • Правильность написания client_secret.
    • Наличие двоеточия (:) между ними.
    • Корректность Base64-кодирования всей этой строки. Для проверки вы можете использовать онлайн-кодировщики Base64 или реализовать кодирование в 1С.
  2. Client id не зарегистрирован или не активирован для данного API.

    Иногда даже правильные учетные данные могут быть отклонены, если client_id не имеет необходимых прав доступа или не был активирован на стороне API-провайдера для работы с конкретным API. В этом случае вам необходимо:

    • Обратиться к документации API для получения информации о процессе регистрации или активации client_id.
    • Связаться со службой поддержки API-провайдера (например, СберБанка), чтобы уточнить статус вашего client_id и проверить, есть ли у него необходимые разрешения для получения токена.
    • Убедиться, что вы используете client_id, который предназначен для используемой вами среды (например, тестовая или продуктивная).
  3. Неверное значение scope.

    Хотя ошибка "Client id not registered" обычно указывает на проблему с учетными данными, иногда некорректный или несуществующий scope также может быть причиной отказа в авторизации, если сервер не может предоставить запрашиваемые разрешения для данного client_id. Перепроверьте точное значение scope в документации API.

Общие рекомендации при работе с API

В ходе интеграции с любым внешним API крайне важно внимательно изучать его документацию. Документация является вашим основным источником информации о:

Если документация неполная или непонятная, не стесняйтесь обращаться в службу поддержки API-провайдера. Они могут предоставить более подробную информацию, примеры или помочь с диагностикой.

Мы видим, что проблема "Invalid object: the property 'grant_type' is missing" часто является результатом недопонимания того, как параметры OAuth 2.0 должны передаваться в HTTP-запросе. Следуя рекомендациям по передаче grant_type и scope в теле запроса в формате application/x-www-form-urlencoded, вы сможете успешно преодолеть эту преграду. После этого, сосредоточьтесь на корректности ваших идентификационных данных для устранения ошибок авторизации.

← На главную