При работе с HTTP-сервисами на платформе 1С:Предприятие часто возникает задача обеспечить безопасный и современный способ авторизации. Одним из таких стандартов является OAuth 2.0. В отличие от традиционной Basic-аутентификации, OAuth 2.0 предоставляет гибкий фреймворк для делегирования полномочий, позволяя клиентам получать доступ к защищенным ресурсам без прямого использования учетных данных пользователя. Рассмотрим, как реализовать OAuth 2.0 авторизацию, в частности, поток Client Credentials, для вашего HTTP-сервиса на 1С, а также как можно задействовать возможности веб-серверов Apache или Nginx для этой цели.
Цель нашей задачи: Мы хотим, чтобы клиент, имея только свои идентификаторы client_id и client_secret, мог получить от нас токен доступа. Все последующие запросы к защищенным ресурсам должны сопровождаться этим токеном для авторизации. Поскольку 1С напрямую не поддерживает OAuth 2.0 как "протокол" в привычном смысле, нам потребуется реализовать логику работы с токенами на уровне приложения.
Прежде чем погружаться в детали реализации, давайте кратко вспомним основные принципы OAuth 2.0. Это не протокол аутентификации, а фреймворк авторизации, который позволяет одному приложению (клиенту) получать ограниченный доступ к защищенным ресурсам, размещенным на другом сервере (сервере ресурсов), от имени владельца ресурса. В нашем случае, поскольку клиент сам является владельцем ресурса (мы даем доступ его приложению), наиболее подходящим для нас будет Client Credentials Flow (поток учетных данных клиента).
В этом потоке существуют следующие роли:
client_id и client_secret.client_id и client_secret клиента и выдавать токен доступа.Важно понимать, что работа с временным токеном, его генерация и последующая проверка, реализуется на уровне логики нашего приложения, то есть средствами 1С, а не на уровне протокола, который 1С поддерживает по умолчанию (Basic аутентификация).
Первый и наиболее прямой способ реализации OAuth 2.0 — это полностью построить логику выдачи и проверки токенов внутри 1С. Рассмотрим шаги, которые мы предпримем для этого. В качестве готовой основы можно применить готовую конфигурацию для реализации аутентификации по JWT-токенам.
Нам необходимо создать отдельный HTTP-сервис или шаблон URL внутри существующего HTTP-сервиса, который будет отвечать за выдачу токенов. Согласно нашему сценарию, клиент будет отправлять POST-запрос на этот эндпоинт, передавая свои client_id и client_secret.
Предположим, мы создадим шаблон POST /security/oauth/token. В теле запроса клиент будет передавать параметры client_id и client_secret. В обработчике этого запроса мы будем выполнять следующие действия:
client_id и client_secret из запроса. Их можно хранить в каком-либо регистре сведений 1С или справочнике, связанном с контрагентами или пользователями. Важно проверять соответствие переданных данных нашим хранимым.Пример структуры HTTP-сервиса в 1С:
Мы создадим новый HTTP-сервис, назовем его, например, OAuthService. В нем создадим шаблон URL:
/oauth/tokenPOSTПолучитьТокенВ модуле HTTP-сервиса OAuthService напишем функцию ПолучитьТокен:
Функция ПолучитьТокен(Запрос)
Ответ = Новый HTTPСервисОтвет(200);
Ответ.УстановитьТелоИзСтроки("{""error"":""invalid_client""}", КодировкаТекста.UTF8, "application/json"); // По умолчанию ошибка
Попытка
ТелоЗапроса = Запрос.ПолучитьТелоКакСтроку();
JSON = Новый ЧтениеJSON;
JSON.УстановитьСтроку(ТелоЗапроса);
Параметры = JSON.Прочитать();
ClientSecret = Параметры.client_secret;
ClientId = Параметры.client_id;
// Здесь мы должны проверить ClientId и ClientSecret
// Например, найти их в регистре сведений "УчетныеДанныеOAuth"
Если Не ПроверитьУчетныеДанные(ClientId, ClientSecret) Тогда
Ответ.КодСостояния = 401; // Unauthorized
Ответ.УстановитьТелоИзСтроки("{""error"":""invalid_client""}", КодировкаТекста.UTF8, "application/json");
Возврат Ответ;
КонецЕсли;
// Генерируем JWT-токен
// С версии 8.3.21 доступен объект AccessToken
Если ОбщегоНазначения.ВерсияПлатформы("8.3.21") Тогда
ТокенДоступа = Новый AccessToken();
ТокенДоступа.УстановитьПользователя("oauth_user"); // Условный пользователь
ТокенДоступа.СрокДействия = 3600; // 1 час
ТокенДоступа.Издатель = "https://your-1c-server.com";
ТокенДоступа.АлгоритмПодписи = "HS256";
ТокенДоступа.КлючПодписи = "ваш_очень_секретный_ключ"; // Секретный ключ для подписи JWT
ТокенДоступа.ИдентификаторКлиента = ClientId; // Добавляем client_id в полезную нагрузку JWT
ТекстТокена = ТокенДоступа.ПолучитьТокенJWT();
Иначе
// Для более старых версий или более сложной логики JWT
// потребуется реализация на основе внешних компонент или библиотек
// или ручная генерация (что сложнее и менее безопасно)
ТекстТокена = СгенерироватьJWTВручную(ClientId, "ваш_очень_секретный_ключ", 3600);
КонецЕсли;
Ответ.КодСостояния = 200;
Ответ.УстановитьТелоИзСтроки(
JSON.Записать(Новый Структура("access_token, token_type, expires_in", ТекстТокена, "Bearer", 3600)),
КодировкаТекста.UTF8,
"application/json"
);
Исключение
Ответ.КодСостояния = 400; // Bad Request
Ответ.УстановитьТелоИзСтроки("{""error"":""invalid_request""}", КодировкаТекста.UTF8, "application/json");
КонецПопытки;
Возврат Ответ;
КонецФункции
// Функция для проверки учетных данных клиента
Функция ПроверитьУчетныеДанные(ClientId, ClientSecret)
// Здесь должна быть ваша логика проверки.
// Например, запрос к регистру сведений:
// Запрос = Новый Запрос;
// Запрос.Текст = "ВЫБРАТЬ 1 ИЗ РегистрСведений.УчетныеДанныеOAuth ГДЕ ИдентификаторКлиента = &ClientId И СекретКлиента = &ClientSecret";
// Запрос.УстановитьПараметр("ClientId", ClientId);
// Запрос.УстановитьПараметр("ClientSecret", ClientSecret);
// Возврат Не Запрос.Выполнить().Пустой();
Возврат (ClientId = "myclient" И ClientSecret = "mysecret"); // Пример для демонстрации
КонецФункции
// Пример реализации для версий 1С младше 8.3.21 или если требуется кастомная логика
Функция СгенерироватьJWTВручную(ClientId, СекретныйКлюч, СрокДействия)
// Здесь потребуется сложная логика кодирования Base64, хеширования HMAC-SHA256.
// Платформа 1С 8.3.10+ умеет работать с HMAC-SHA256, но создание заголовков, полезной нагрузки,
// их Base64-кодирование и объединение требует ручной работы.
// Этот пример упрощен и не является полноценной реализацией JWT, а лишь демонстрирует концепцию.
Заголовок = Новый Структура("alg, typ", "HS256", "JWT");
ПолезнаяНагрузка = Новый Структура("sub, exp, iat, client_id", "oauth_user", ТекущаяДата() + СрокДействия, ТекущаяДата(), ClientId);
ЧтениеЗаголовка = Новый ЗаписьJSON;
ЧтениеЗаголовка.УстановитьСтроку();
ЧтениеЗаголовка.Записать(Заголовок);
ЗаголовокBase64 = База64Кодировать(ЧтениеЗаголовка.Закрыть());
ЧтениеПолезнойНагрузки = Новый ЗаписьJSON;
ЧтениеПолезнойНагрузки.УстановитьСтроку();
ЧтениеПолезнойНагрузки.Записать(ПолезнаяНагрузка);
ПолезнаяНагрузкаBase64 = База64Кодировать(ЧтениеПолезнойНагрузки.Закрыть());
// Для реальной реализации подписи потребуется функция хеширования с ключом (HMAC-SHA256)
// и ее Base64-кодирование. Это выходит за рамки простого примера.
// Вместо этого используем заглушку.
Подпись = "НЕПОДЛИННАЯ_ПОДПИСЬ_ДЛЯ_ПРИМЕРА";
Возврат ЗаголовокBase64 + "." + ПолезнаяНагрузкаBase64 + "." + Подпись;
КонецФункции
// Вспомогательная функция для Base64 кодирования (требует реализации)
Функция База64Кодировать(Строка)
// Реализация кодирования в Base64. В 1С есть для двоичных данных,
// для строк нужна функция преобразования строки в двоичные данные.
// Например: ДвоичныеДанные = Новый ДвоичныеДанные(Строка, КодировкаТекста.UTF8);
// Возврат Base64Строка(ДвоичныеДанные);
Возврат "Base64(" + Строка + ")"; // Заглушка
КонецФункции
Важно: Для версий 1С до 8.3.21, где объект AccessToken недоступен, реализация JWT вручную гораздо сложнее, так как требует корректного Base64-кодирования и использования криптографических функций для подписи (например, HMAC-SHA256, которые 1С поддерживает с версии 8.3.10). Мы рекомендуем по возможности использовать более новые версии платформы.
После того как клиент получил токен, он будет прикреплять его к каждому запросу к защищенным ресурсам, обычно в заголовке Authorization в формате Bearer {token}.
В наших остальных HTTP-сервисах 1С (которые предоставляют защищенные данные) мы будем реализовывать логику проверки этого токена:
Authorization из входящего HTTP-запроса.exp (expiration time) в полезной нагрузке JWT. Если токен истек, он недействителен.client_id, scope (области доступа), iss (издатель) и другие, чтобы убедиться, что токен подходит для запрашиваемого ресурса.Пример функции проверки токена в 1С:
Функция ПроверитьТокенДоступа(Запрос)
ЗаголовокАвторизации = Запрос.Заголовки.Получить("Authorization");
Если Не ЗначениеЗаполнено(ЗаголовокАвторизации) Или Не СтрНачинаетсяС(ЗаголовокАвторизации, "Bearer ") Тогда
Возврат Неопределено; // Токен отсутствует или неверный формат
КонецЕсли;
ТекстТокена = СтрЗаменить(ЗаголовокАвторизации, "Bearer ", "");
// С версии 8.3.21 используем AccessToken для валидации
Если ОбщегоНазначения.ВерсияПлатформы("8.3.21") Тогда
ТокенДоступа = Новый AccessToken();
ТокенДоступа.УстановитьТокенJWT(ТекстТокена);
ТокенДоступа.КлючПодписи = "ваш_очень_секретный_ключ"; // Тот же ключ, что и при генерации
Попытка
ТокенДоступа.Проверить(); // Выполняет проверку подписи, срока действия и т.д.
Возврат ТокенДоступа; // Токен действителен, возвращаем объект с его данными
Исключение
Возврат Неопределено; // Токен недействителен
КонецПопытки;
Иначе
// Ручная валидация для более старых версий
// Этот процесс включает:
// 1. Декодирование Base64 Заголовка и ПолезнойНагрузки.
// 2. Проверку срока действия (exp) в ПолезнойНагрузке.
// 3. Воссоздание подписи с использованием того же ключа и сравнение с подписью из токена.
// Это сложная логика, требующая тщательной реализации.
// Для простоты примера предположим, что есть функция:
Возврат ПроверитьJWTВручную(ТекстТокена, "ваш_очень_секретный_ключ");
КонецЕсли;
КонецФункции
// Вспомогательная функция для ручной проверки JWT (очень упрощенная заглушка)
Функция ПроверитьJWTВручную(ТекстТокена, СекретныйКлюч)
ЧастиТокена = СтрРазделить(ТекстТокена, ".");
Если ЧастиТокена.Количество() <> 3 Тогда
Возврат Неопределено;
КонецЕсли;
// Здесь должна быть логика декодирования, проверки срока действия,
// и самое главное - проверка подписи.
// Возврат действительного объекта-описания токена, если он валиден.
Возврат Новый Структура("client_id", "myclient"); // Заглушка, если токен кажется "валидным"
КонецФункции
// Пример использования в обработчике HTTP-сервиса
Функция ПолучитьДанныеКлиента(Запрос)
ПроверенныйТокен = ПроверитьТокенДоступа(Запрос);
Если ПроверенныйТокен = Неопределено Тогда
Ответ = Новый HTTPСервисОтвет(401); // Unauthorized
Ответ.УстановитьТелоИзСтроки("{""error"":""invalid_token""}", КодировкаТекста.UTF8, "application/json");
Возврат Ответ;
КонецЕсли;
// Теперь мы знаем, что токен действителен.
// Можно получить client_id или другие данные из ПроверенныйТокен
ИдентификаторКлиента = ПроверенныйТокен.ИдентификаторКлиента; // Если используем AccessToken
// ... или ИдентификаторКлиента = ПроверенныйТокен.client_id; если ручная реализация
// Возвращаем запрашиваемые данные
Ответ = Новый HTTPСервисОтвет(200);
Ответ.УстановитьТелоИзСтроки("{""message"":""Доступ к данным для клиента " + ИдентификаторКлиента + """, ""data"": [...]}", КодировкаТекста.UTF8, "application/json");
Возврат Ответ;
КонецФункции
Изначально в сообщении поднимался вопрос о Basic-авторизации. Если мы хотим еще больше усилить безопасность запроса токена, мы можем настроить Basic-аутентификацию для самого эндпоинта выдачи токенов. Для этого:
Authorization: Basic {base64(login:password)}. Как верно указано в обсуждении, логин и пароль передаются в заголовке запроса, а не в строке URL.Однако, в контексте Client Credentials Flow, когда client_id и client_secret передаются в теле запроса, отдельная Basic-аутентификация для этого же запроса может быть избыточной, если client_secret считается достаточно надежным.
Как мы уже обсудили, использование JSON Web Token (JWT) для токенов доступа является предпочтительным. Давайте рассмотрим подробнее возможности 1С по работе с ними.
Преимущества JWT:
Структура JWT:
JWT состоит из трех частей, разделенных точками (.):
JWT) и используемый алгоритм подписи (например, HS256).sub (субъект), exp (срок действия), iat (время выдачи), а также наши кастомные данные, например, client_id.Возможности платформы 1С для работы с JWT:
AccessToken. Этот объект значительно упрощает работу с JWT, предоставляя методы для создания, разбора и валидации токенов. Мы уже использовали его в наших примерах выше, и это настоятельно рекомендуемый способ работы с JWT в 1С, если версия платформы это позволяет.Если нам необходимо создать JWT для версий 1С до 8.3.21, нам придется реализовывать логику кодирования в Base64 и хеширования HMAC-SHA256 вручную, используя доступные в 1С функции работы с двоичными данными и криптографией. Это сложная задача, требующая глубокого понимания стандартов и аккуратности в реализации. Для этой задачи можно использовать обработку для генерации и проверки JWT-токенов с подписью HS256.
Реализация всей логики OAuth 2.0 (особенно валидации токенов) внутри 1С может быть сложной и ресурсоемкой, особенно для старых версий платформы. Современные веб-серверы, такие как Apache и Nginx, обладают расширенными возможностями, которые позволяют им выступать в роли обратного прокси и брать на себя часть функций сервера ресурсов по проверке токенов.
Этот подход позволяет нам:
В этом сценарии, 1С по-прежнему может выступать в роли сервера авторизации (выдавать токены, как мы рассмотрели выше), но валидация этих токенов будет происходить на уровне веб-сервера до того, как запрос достигнет 1С.
NGINX — очень мощный обратный прокси, который с помощью специальных модулей может выполнять валидацию JWT-токенов:
auth_jwt (для NGINX Plus): Это специализированный модуль, который позволяет NGINX выполнять офлайн-валидацию JWT. Мы можем указать NGINX публичный ключ (или секретный ключ для симметричных алгоритмов), которым были подписаны токены. NGINX будет автоматически проверять подпись, срок действия и другие параметры токена. Если токен недействителен, NGINX отклонит запрос, не пропуская его к 1С.auth_request в сочетании с NGINX JavaScript (njs): Для более гибкой логики или для "непрозрачных" токенов (которые не являются JWT или требуют онлайн-интроспекции), мы можем использовать auth_request. Этот модуль позволяет NGINX выполнять субзапрос к нашему внутреннему сервису (например, к отдельному микросервису на 1С или на другой платформе), который будет отвечать за валидацию токена. Результат этого субзапроса определяет, пропускать основной запрос или нет. С помощью njs мы можем написать логику для интроспекции токенов (RFC 7662) прямо в конфигурации NGINX.NGINX может извлекать данные из успешно проверенного токена (например, client_id) и передавать их в заголовках запроса к 1С, например, X-Client-ID. Таким образом, в 1С мы просто получаем уже доверенный client_id из заголовка и не выполняем никакой логики валидации токена.
Apache HTTP Server также предоставляет модули для интеграции с OAuth 2.0 и OpenID Connect:
mod_oauth2: Этот модуль разработан специально для валидации токенов доступа OAuth 2.0. Он позволяет настроить Apache как сервер ресурсов, который проверяет входящие токены доступа, отправляя их серверу авторизации (или используя локальную валидацию JWT, если он поддерживает такие функции).mod_auth_openidc: Хотя этот модуль предназначен для OpenID Connect (который построен на OAuth 2.0), он может быть использован для выполнения аутентификации и авторизации на основе токенов. Он умеет работать с JWT ID-токенами и токенами доступа, перехватывать запросы, обмениваться токенами с провайдером идентификации (IdP) и прикреплять проверенные данные идентификации к заголовкам запросов перед их пересылкой на бэкенд (в нашем случае — в 1С).Подобно NGINX, Apache, после успешной валидации токена, может передавать соответствующие данные (например, идентификатор клиента) в виде заголовков HTTP-запроса к 1С, что упрощает логику в самой 1С.
Вне зависимости от выбранного подхода, безопасность реализации OAuth 2.0 критически важна. Мы должны учитывать следующие аспекты:
client_secret или токенов по незащищенному каналу недопустима.client_id и client_secret (а также секретный ключ для подписи JWT) — это конфиденциальные данные. Никогда не хардкодьте их в клиентском коде и храните их в безопасном месте на сервере (например, в защищенных конфигурационных файлах или менеджере секретов).client_id и client_secret в теле запроса, можно использовать более надежные методы аутентификации клиента, такие как mTLS (взаимная TLS-аутентификация) или подписание запроса с помощью private_key_jwt.client_secret) и ключи для подписи JWT.Таким образом, для вашей задачи по реализации OAuth 2.0 авторизации для HTTP-сервера на 1С, мы можем выбрать один из двух основных подходов: либо полностью реализовать логику выдачи и проверки JWT-токенов средствами 1С (особенно актуально для версии 8.3.21+ с объектом AccessToken), либо использовать веб-сервер (Apache или Nginx) в качестве обратного прокси, который возьмет на себя функцию валидации токенов, значительно упростив и обезопасив логику на стороне 1С.