Из спецификации видим, что параметры client_id и client_secret передаются в теле запроса в кодировке application/x-www-form-urlencoded.
Итак, нужен сервер аутентификации, для того чтобы принять запрос от клиента с ключом и секретом, проверить их и в случае успеха – выдать токен. В качестве сервера аутентификации я сделал микро-конфигурацию с одним http-сервисом.
Обработчик для свойства POST:
Функция tokenPOST(Запрос)
Перем client_id, client_secret;
Ошибки = Новый Массив;
// Получаем параметры из тела запроса application/x-www-form-urlencoded
ПараметрыЗапроса = x_www_form_urlencoded_ВСтруктуру(Запрос.ПолучитьТелоКакСтроку());
ПараметрыЗапроса.Свойство("client_id" , client_id);
ПараметрыЗапроса.Свойство("client_secret", client_secret);
Если client_id = Неопределено Тогда
ДобавитьОшибку(Ошибки, "Необходимо в теле запроса application/x-www-form-urlencoded: client_id.");
КонецЕсли;
Если client_secret = Неопределено Тогда
ДобавитьОшибку(Ошибки, "Необходимо в теле запроса application/x-www-form-urlencoded: client_secret.");
КонецЕсли;
Если client_id <> Неопределено И client_secret <> Неопределено
И НЕ Справочники.Клиенты.Существует(client_id, client_secret) Тогда
ДобавитьОшибку(Ошибки, "Ошибка аутентификации, проверьте client_id и client_secret.");
КонецЕсли;
Если Ошибки.Количество() > 0 Тогда
Возврат ОтветHTTP(400, Ошибки);
КонецЕсли;
// Создаем токен
ТокенДоступа = Новый ТокенДоступа;
ТокенДоступа.Заголовки.Вставить("alg", Строка(АлгоритмПодписиТокенаДоступа.HS256));
ТокенДоступа.Эмитент = "ssl";
МассивПолучателей = Новый Массив;
МассивПолучателей.Добавить("YandexEda"); // имя http-сервиса из default.vrd
ТокенДоступа.Получатели = МассивПолучателей;
ТокенДоступа.КлючСопоставленияПользователя = "tokenuser"; // имя пользователя 1С, у которого должна стоять галочка "Аутентификация токеном доступа"
ТокенДоступа.ВремяСоздания = ТекущаяУниверсальнаяДата() - Дата(1970,1,1,0,0,0);
ТокенДоступа.ВремяЖизни = 3600;
ТокенДоступа.Идентификатор = Новый УникальныйИдентификатор;
ТокенДоступа.Подписать(АлгоритмПодписиТокенаДоступа.HS256, Константы.КлючПодписи.Получить()); // ключ, он должен быть указан в default.vrd
access_token = Строка(ТокенДоступа); // получим токен строкой
Возврат ОтветHTTP(200, Новый Структура("access_token", access_token));
КонецФункции
Остальные обработчики модуля:
Процедура ДобавитьОшибку(МассивОшибок, ОписаниеОшибки)
Ошибка = Новый Структура;
Ошибка.Вставить("description", ОписаниеОшибки);
МассивОшибок.Добавить(Ошибка);
КонецПроцедуры
Функция СформироватьЗаголовки()
Заголовки = Новый Соответствие;
Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
Заголовки.Вставить("Cache-Control", "private, max-age=0, no-cache, no-store");
Заголовки.Вставить("Expires" , Формат(ТекущаяУниверсальнаяДата(), "Л=en_US; ДФ='ddd, dd MMM yyyy HH:mm:ss'")+" GMT");
Заголовки.Вставить("ETag" , Новый УникальныйИдентификатор);
Заголовки.Вставить("Vary" , "User-Agent");
Заголовки.Вставить("Pragma" , "no-cache");
Возврат Заголовки;
КонецФункции
Функция ОтветHTTP(Код, Структура, ДобавитьЗаголовки = Истина)
Ответ = Новый HTTPСервисОтвет(Код);
СтрокаJSON = СформироватьJSON(Структура);
Ответ.УстановитьТелоИзСтроки(СтрокаJSON);
Если ДобавитьЗаголовки Тогда
Ответ.Заголовки = СформироватьЗаголовки();
КонецЕсли;
Возврат Ответ;
КонецФункции
Функция СформироватьJSON(Значение)
ПараметрыЗаписиJSON = Новый ПараметрыЗаписиJSON();
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписиJSON);
НастройкиСериализацииJSON = Новый НастройкиСериализацииJSON;
НастройкиСериализацииJSON.ВариантЗаписиДаты = ВариантЗаписиДатыJSON.ЛокальнаяДатаСоСмещением;
НастройкиСериализацииJSON.ФорматСериализацииДаты = ФорматДатыJSON.ISO;
ЗаписатьJSON(ЗаписьJSON, Значение, НастройкиСериализацииJSON);
Возврат ЗаписьJSON.Закрыть();
КонецФункции
Функция x_www_form_urlencoded_ВСтруктуру(ТелоЗапроса)
Структура = Новый Структура;
МассивСтрок = СтрРазделить(ТелоЗапроса, "&");
Для Каждого ТекСтрока Из МассивСтрок Цикл
Мас = СтрРазделить(ТекСтрока, "=");
Структура.Вставить(Мас[0], Мас[1]);
КонецЦикла;
Возврат Структура;
КонецФункции
При публикации http-сервиса аутентификации формируется файл default.vrd, в котором нужно прописать имя пользователя и пароль для входа в базу. Это нужно сделать потому, что в запросе Яндекса отсутствует Basic-аутентификация, поэтому мы пропускаем запрос без аутентификации и потом уже в коде проверяем client_id и client_secret, полученные из тела запроса в кодировке application/x-www-form-urlencoded.
Вот строка из файла default.vrd базы аутентификации, в которой нужно прописать имя пользователя (UserName) и пароль (Password) для входа в базу.
ib="File="D:\1C\Base\HTTP_YandexEda_Auth";Usr="UserName";Pwd="Password""
Теперь о самом главном, о токене.
В базе, в которую клиент будет входить по токену, создаем пользователя для аутентификации токеном.
В моем случае это отдельная база и конфигурация. Т.е. у меня есть база http-сервиса аутентификации, которая только выдает токен и основная база http-сервиса обработки заказов "Яндекс Еды", в которую и будет выполняться вход по токену.
После публикации этой базы, необходимо внести изменения в файл default.vrd, для того, чтобы входить в эту базу используя токен доступа.
Вот содержимое файла default.vrd базы http-сервиса обработки заказов "Яндекс Еда":
<?xml version="1.0" encoding="UTF-8"?>
<point xmlns="http://v8.1c.ru/8.2/virtual-resource-system"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
base="/goldfishyayeda"
ib="File="D:\1C\Base\HTTP_YandexEda";"
enable="false">
<ws enable="false"
pointEnableCommon="false"/>
<httpServices publishByDefault="false">
<service name="YandexEda"
rootUrl="v1"
enable="true"
reuseSessions="autouse"
sessionMaxAge="30"
poolSize="10"
poolTimeout="5">
<accessTokenAuthentication>
<accessTokenRecepientName>YandexEda</accessTokenRecepientName>
<issuers>
<issuer name="ssl"
authenticationClaimName="sub"
authenticationUserPropertyName="tokenuser"
keyInformation="OTVjMDNkOWM2N2QyNDE1ZjgyNDMwZGI0ZTg1Y2VlNWQ="/>
</issuers>
</accessTokenAuthentication>
</service>
</httpServices>
<standardOdata enable="false"
reuseSessions="autouse"
sessionMaxAge="20"
poolSize="10"
poolTimeout="5"/>
<analytics enable="false"/>
</point>
Ключ подписи - это любой набор символов закодированный в Base64 строку.
Вот пример функции для генерации ключей подписи токена:
Функция СоздатьКлючПодписи()
УИД = Новый УникальныйИдентификатор;
УИД = СтрЗаменить(УИД, "-", "");
Возврат Base64Строка(ПолучитьДвоичныеДанныеИзСтроки(УИД));
КонецФункции
Общая схема настройки:
Выводы:
При формировании токена необходимо указать:
- получателя токена, это имя http-сервиса, оно будет указано в файле default.vrd;
МассивПолучателей = Новый Массив;
МассивПолучателей.Добавить("YandexEda"); // имя http-сервиса из default.vrd
ТокенДоступа.Получатели = МассивПолучателей;
- ключ сопоставления пользователя, это имя пользователя 1с, у которого установлена галочка "Аутентификация токеном доступа", также это параметр authenticationUserPropertyName в файле default.vrd;
ТокенДоступа.КлючСопоставленияПользователя = "tokenuser"; // имя пользователя 1С, у которого должна стоять галочка "Аутентификация токеном доступа"
- подписать токен используя ключ, который необходимо указать в файле default.vrd в параметре keyInformation
ТокенДоступа.Подписать(АлгоритмПодписиТокенаДоступа.HS256, Константы.КлючПодписи.Получить()); // ключ, он должен быть указан в default.vrd