Авторизация приложений в изолированной коробке Битрикс24
Выберите инструмент для разработки с AI-агентом:
- используйте Битрикс24 Вайбкод, чтобы создать приложение для Битрикс24 по описанию задачи без знания языков программирования. Агент напишет код и разместит приложение на сервере без ручной настройки хостинга
- используйте MCP-сервер, чтобы разрабатывать интеграцию через REST API в своем проекте. Агент будет обращаться к официальной REST-документации
Политика безопасности компании может ограничивать доступ к внутренним и внешним сетевым ресурсам. Из-за этого приложения на REST для Битрикс24 не всегда могут подключиться к коробочному Битрикс24 или внешним облачным сервисам. Альтернативная схема авторизации позволяет разрабатывать приложения на стандартном REST API для Битрикс24 в изолированной инфраструктуре.
Описанное ниже решение исключает сервер oauth.bitrix24.tech из процесса авторизации. Используйте его только в крайнем случае: вы самостоятельно отвечаете за безопасность приложений, хранение секретов и управление авторизацией
Решение подходит для коробочной версии Битрикс24, если:
- администратор имеет доступ к файлам Битрикс24 на сервере
- можно создать собственный модуль в папке
local/modules/ - приложение должно работать только для заранее разрешенных
client_id - секреты и токены хранятся в защищенном хранилище, а не в системных файлах продукта
Обращения к внешним ресурсам
Во время работы приложения на REST обращения из Битрикс24 к внешним ресурсам выполняют три компонента:
- валидатор авторизации
- провайдер событий
- провайдер авторизации

Ниже показано, как заменить эти обращения локальными обработчиками для одного конкретного приложения, которому нужно обойти основную цепочку.
Валидатор авторизации
Создайте валидатор авторизации. Он проверяет запрос по параметру secret_word и авторизует пользователя во время текущего запроса.
<?php
namespace Demo\AuthProvider;
class AuthSimple
{
const AUTH_TYPE = 'demo_simple';
const AUTH_PARAM_NAME = 'secret_word';
const AUTH_PARAM_VALUE = 'change_this_secret';
public static function onRestCheckAuth(array $query, $scope, &$res)
{
if(array_key_exists(static::AUTH_PARAM_NAME, $query))
{
if($query[static::AUTH_PARAM_NAME] === static::AUTH_PARAM_VALUE)
{
$error = false;
$res = array(
'user_id' => 1,
'scope' => implode(',', \CRestUtil::getScopeList()),
'parameters_clear' => array(static::AUTH_PARAM_NAME),
'auth_type' => static::AUTH_TYPE,
);
if(!\CRestUtil::makeAuth($res))
{
$res = array(
'error' => 'authorization_error',
'error_description' => 'Unable to authorize user'
);
$error = true;
}
return !$error;
}
$res = array(
'error' => 'INVALID_CREDENTIALS',
'error_description' => 'Invalid request credentials'
);
return false;
}
return null;
}
}
Валидатор получает все данные запроса приложения. Если в запросе нет параметра secret_word, он возвращает return null, чтобы запрос проверил другой валидатор. Если параметр есть, обработчик проверяет его значение.
Если значение не совпадает с сохраненным, валидатор возвращает ошибку INVALID_CREDENTIALS. Если значение верное, он передает ID пользователя, список доступных скоупов, параметры, которые нужно удалить из запроса, и идентификатор типа авторизации. Тип авторизации нужен методам, которые ограничивают доступ по способу авторизации.
В примере значение AUTH_PARAM_VALUE указано в коде для демонстрации. В рабочем модуле храните секрет в защищенном хранилище и не передавайте его в репозиторий.
После этого обработчик вызывает метод модуля REST, который авторизует пользователя во время текущего запроса. Если авторизация прошла успешно, возвращается true.
Зарегистрируйте валидатор при установке модуля:
\Bitrix\Main\EventManager::getInstance()->registerEventHandler(
'rest',
'onRestCheckAuth',
'demo.authprovider',
'\\Demo\\AuthProvider\\AuthSimple',
'onRestCheckAuth',
80
);
Провайдер событий
Создайте класс провайдера событий. Он наследует стандартный провайдер Bitrix\Rest\Event\ProviderOAuth и реализует интерфейс Bitrix\Rest\Event\ProviderInterface. В примере переопределяется метод PHP-класса send: вместо обращения к внешней очереди событий он выполняет прямой HTTP-запрос к обработчику приложения через $http->post(...).
<?php
namespace Demo\AuthProvider;
use Bitrix\Rest\Event\ProviderInterface;
use Bitrix\Rest\Event\ProviderOAuth;
use Bitrix\Rest\Event\Sender;
class EventProvider extends ProviderOAuth implements ProviderInterface
{
public static function onEventManagerInitialize()
{
Sender::setProvider(static::instance());
}
public function send(array $queryData)
{
$http = new \Bitrix\Main\Web\HttpClient();
foreach($queryData as $key => $item)
{
if($this->checkItem($item))
{
if($item['additional']['sendAuth'])
{
$item['query']['QUERY_DATA']['auth'] = AuthProvider::instance()->get(
$item['client_id'],
'',
$item['auth'],
$item['auth'][AuthFull::PARAM_LOCAL_USER]
);
}
$http->post($item['query']['QUERY_URL'], $item['query']['QUERY_DATA']);
unset($queryData[$key]);
}
}
if(count($queryData) > 0)
{
parent::send(array_values($queryData));
}
}
protected function checkItem(array $item)
{
return AuthProvider::instance()->checkClient($item['client_id']);
}
}
Провайдер проверяет каждый элемент массива событий. Если событие относится к разрешенному приложению, провайдер добавляет данные авторизации и выполняет POST-запрос к обработчику приложения. Если событие относится к другому приложению, запрос передается стандартному провайдеру.
Провайдер авторизации
Создайте класс провайдера авторизации. Он наследует стандартный провайдер Bitrix\Rest\OAuth\Provider и реализует интерфейс Bitrix\Rest\AuthProviderInterface.
<?php
namespace Demo\AuthProvider;
use Bitrix\Main\Context;
use Bitrix\Main\NotImplementedException;
use Bitrix\Main\ObjectNotFoundException;
use Bitrix\Main\Security\Random;
use Bitrix\Rest\Application;
use Bitrix\Rest\AppTable;
use Bitrix\Rest\AuthProviderInterface;
use Bitrix\Rest\AuthStorageInterface;
use Bitrix\Rest\OAuth\Provider;
use Bitrix\Rest\RestException;
class AuthProvider extends Provider implements AuthProviderInterface
{
const TOKEN_TTL = 3600;
const TOKEN_PREFIX = 'demo.';
protected $applicationList = array();
/**
* @var AuthProvider
*/
protected static $instance = null;
/**
* @var AuthStorageInterface
*/
protected $storage;
/**
* @return AuthProvider
*/
public static function instance()
{
if(static::$instance === null)
{
static::$instance = new static();
}
return static::$instance;
}
public static function onApplicationManagerInitialize()
{
Application::setAuthProvider(static::instance());
}
public function get($clientId, $scope, $additionalParams, $userId)
{
if(!$this->checkClient($clientId))
{
return parent::get($clientId, $scope, $additionalParams, $userId);
}
if($userId > 0)
{
$applicationData = AppTable::getByClientId($clientId);
if($applicationData)
{
$authResult = array(
'access_token' => $this->generateToken(),
'user_id' => $userId,
'client_id' => $clientId,
'expires' => time() + static::TOKEN_TTL,
'expires_in' => static::TOKEN_TTL,
'scope' => $applicationData['SCOPE'],
'domain' => Context::getCurrent()->getServer()->getHttpHost(),
'status' => AppTable::STATUS_LOCAL,
'client_endpoint' => \CRestUtil::getEndpoint(),
'member_id' => \CRestUtil::getMemberId(),
);
$this->store($authResult);
return $authResult;
}
else
{
$authResult = array('error' => RestException::ERROR_OAUTH, 'Application not installed');
}
return $authResult;
}
return false;
}
public function authorizeClient($clientId, $userId, $state = '')
{
if(!$this->checkClient($clientId))
{
return parent::authorizeClient($clientId, $userId, $state);
}
throw new NotImplementedException('Full OAuth authorization is not implemented in this demo');
}
public function checkClient($clientId)
{
return in_array($clientId, $this->applicationList);
}
protected function store(array $authResult)
{
$this->getStorage()->store($authResult);
}
public function checkToken($token)
{
return substr($token, 0, strlen(static::TOKEN_PREFIX)) === static::TOKEN_PREFIX;
}
protected function generateToken()
{
return static::TOKEN_PREFIX.Random::getString(32);
}
/**
* @return AuthStorageInterface
* @throws ObjectNotFoundException
*/
public function getStorage()
{
if($this->storage === null)
{
throw new ObjectNotFoundException('No token storage set. Use '.__CLASS__.'::instance()->setStorage().');
}
return $this->storage;
}
/**
* @param AuthStorageInterface $storage
* @return AuthProvider
*/
public function setStorage(AuthStorageInterface $storage)
{
$this->storage = $storage;
return $this;
}
/**
* @param string $clientId
* @return AuthProvider
*/
public function addApplication($clientId)
{
$this->applicationList[] = $clientId;
return $this;
}
}
Основной метод PHP-класса — get. Он выдает авторизационные данные приложению. Метод получает client_id, проверяет, входит ли приложение в список разрешенных, получает данные приложения и формирует структуру, похожую на ответ стандартного OAuth-сервера Битрикс24. В массиве авторизации:
access_token— созданный токенuser_id— пользователь, для которого нужно дать авторизациюclient_id— приложение. В провайдере можно указать любое время жизни токена, а не только час, который используется по умолчанию в обычной авторизацииexpires— дата истечения токенаscope— требуемые скоупыdomain— адрес Битрикс24status— статус локального приложенияclient_endpoint— адрес REST endpointmember_id— идентификатор Битрикс24
Далее эти данные сохраняются в хранилище токенов. Сформированная структура возвращается приложению.
Для сохранения и восстановления токенов нужен класс хранилища. Он должен реализовывать интерфейс Bitrix\Rest\AuthStorageInterface. Методы PHP-класса хранилища выполняют такие действия: store сохраняет новый токен, rewrite обновляет параметры существующего токена, restore возвращает сохраненные данные по access_token.
<?php
namespace Demo\AuthProvider;
use Bitrix\Main\Application;
use Bitrix\Rest\AuthStorageInterface;
class AuthStorage implements AuthStorageInterface
{
const CACHE_TTL = 3600;
const CACHE_PREFIX = 'demo_auth_';
public function store(array $authResult)
{
$cache = $this->getCache();
$cache->read(static::CACHE_TTL, $this->getCacheId($authResult['access_token']));
$cache->set($this->getCacheId($authResult['access_token']), $authResult);
}
public function rewrite(array $authResult)
{
$cache = $this->getCache();
$cache->clean($this->getCacheId($authResult['access_token']));
$cache->read(static::CACHE_TTL, $this->getCacheId($authResult['access_token']));
$cache->set($this->getCacheId($authResult['access_token']), $authResult);
}
public function restore($accessToken)
{
$cache = $this->getCache();
if($cache->read(static::CACHE_TTL, $this->getCacheId($accessToken)))
{
return $cache->get($this->getCacheId($accessToken));
}
return false;
}
protected function getCacheId($accessToken)
{
return static::CACHE_PREFIX.$accessToken;
}
protected function getCache()
{
return Application::getInstance()->getManagedCache();
}
}
Перед выдачей токена передайте хранилище провайдеру:
AuthProvider::instance()
->setStorage(new AuthStorage())
->addApplication('local.demo.application');
Дополнительные методы
Метод сохранения данных.
protected function store(array $authResult)
{
$this->getStorage()->store($authResult);
}
Метод генерации токена добавляет префикс к случайной строке из 32 символов.
protected function generateToken()
{
return static::TOKEN_PREFIX.Random::getString(32);
}
Метод проверки токена. Проверяется наличие префикса.
public function checkToken($token)
{
return substr(
$token,
0,
strlen(static::TOKEN_PREFIX)
) === static::TOKEN_PREFIX;
}
Метод checkClient проверяет, что client_id приложения есть в списке разрешенных приложений.
public function checkClient($clientId)
{
return in_array(
$clientId,
$this->applicationList
);
}
Зарегистрируйте провайдер как текущий провайдер авторизации:
\Bitrix\Rest\Application::setAuthProvider(
Demo\AuthProvider\AuthProvider::instance()
);
После регистрации провайдер становится текущим провайдером авторизации для разрешенного приложения.
Создайте валидатор полного токена.
<?php
namespace Demo\AuthProvider;
use Bitrix\Rest\OAuth\Auth;
class AuthFull extends Auth
{
protected static function check($accessToken)
{
if(!AuthProvider::instance()->checkToken($accessToken))
{
return parent::check($accessToken);
}
$authResult = AuthProvider::instance()->getStorage()->restore($accessToken);
if($authResult === false)
{
$authResult = array(
'error' => 'invalid_token',
'error_description' => 'Token expired or invalid'
);
}
return $authResult;
}
}
В валидаторе нужно наследовать стандартный валидатор авторизации и переопределить метод PHP-класса check. Метод проверяет accessToken: если токен создан вашим провайдером, данные приложения восстанавливаются из хранилища. Затем зарегистрируйте обработчик события при установке модуля:
\Bitrix\Main\EventManager::getInstance()
->registerEventHandler(
"rest",
"onRestCheckAuth",
"demo.authprovider",
"\\Demo\\AuthProvider\\AuthFull",
"onRestCheckAuth",
90
);
Последний параметр — сортировка. Значение 90 позволяет выполнить ваш обработчик раньше стандартного обработчика.
После регистрации полного валидатора выполните запрос с авторизационным токеном и вызовите метод app.info. Битрикс24 вернет данные приложения. Обработчик события также получит структуру авторизации, которую добавляет EventProvider.
Array
(
[install] => 0
[DOMAIN] => example.bitrix24.ru
[PROTOCOL] => 1
[LANG] => ru
[APP_SID] => [redacted]
[AUTH_ID] => demo.[redacted]
[AUTH_EXPIRES] => 3600
[REFRESH_ID] =>
[member_id] => [redacted]
[status] => L
[PLACEMENT] => DEFAULT
)
Код провайдера событий выполняется непосредственно в обработчике события. Пример содержит POST-запрос к стороннему серверу. При медленном ответе стороннего сервера выполнение события в Битрикс24 замедляется. При массовых операциях, например импорте данных в CRM, такой код может заметно увеличить время обработки.
Снизить риск замедления можно двумя способами:
- Построить очередь. Вместо отправки POST-запроса сохранять данные в таблицу и обрабатывать их отдельным агентом или фоновым процессом
- Использовать механизм офлайн событий
Где хранить код
Разместите код в собственном модуле, а не в системных файлах продукта. Так изменения не будут потеряны при обновлении Битрикс24. Ниже приведен пример раскладки для модуля demo.authprovider в папке local/modules/demo.authprovider/:
local/
`-- modules/
`-- demo.authprovider/
|-- include.php
|-- install/
| `-- index.php
`-- lib/
|-- authprovider.php
|-- authstorage.php
|-- authsimple.php
|-- authfull.php
`-- eventprovider.php
Назначение файлов:
| Файл | Что хранит |
|---|---|
local/modules/demo.authprovider/include.php |
Подключает модуль и настраивает провайдер: AuthProvider::instance()->setStorage(new AuthStorage())->addApplication('local.demo.application') |
local/modules/demo.authprovider/lib/authprovider.php |
Класс Demo\AuthProvider\AuthProvider, который реализует Bitrix\Rest\AuthProviderInterface и выдает авторизационные данные приложению |
local/modules/demo.authprovider/lib/authstorage.php |
Класс Demo\AuthProvider\AuthStorage, который реализует Bitrix\Rest\AuthStorageInterface и хранит токены |
local/modules/demo.authprovider/lib/authsimple.php |
Класс Demo\AuthProvider\AuthSimple для проверки запроса по параметру secret_word |
local/modules/demo.authprovider/lib/authfull.php |
Класс Demo\AuthProvider\AuthFull, который наследует Bitrix\Rest\OAuth\Auth и восстанавливает данные приложения по токену |
local/modules/demo.authprovider/lib/eventprovider.php |
Класс Demo\AuthProvider\EventProvider, который наследует Bitrix\Rest\Event\ProviderOAuth и отправляет события без внешней очереди OAuth |
В include.php подключите модуль REST и настройте провайдер:
<?php
use Bitrix\Main\Loader;
use Demo\AuthProvider\AuthProvider;
use Demo\AuthProvider\AuthStorage;
if(Loader::includeModule('rest'))
{
AuthProvider::instance()
->setStorage(new AuthStorage())
->addApplication('local.demo.application');
}
Замените local.demo.application на client_id приложения, которому разрешен обход стандартной цепочки авторизации.
Регистрацию обработчиков выполняйте при установке модуля в local/modules/demo.authprovider/install/index.php. Добавьте вызовы в метод установки модуля после RegisterModule('demo.authprovider'):
\Bitrix\Main\EventManager::getInstance()->registerEventHandler(
'rest',
'onRestCheckAuth',
'demo.authprovider',
'\\Demo\\AuthProvider\\AuthSimple',
'onRestCheckAuth',
80
);
\Bitrix\Main\EventManager::getInstance()->registerEventHandler(
'rest',
'onRestCheckAuth',
'demo.authprovider',
'\\Demo\\AuthProvider\\AuthFull',
'onRestCheckAuth',
90
);
\Bitrix\Main\EventManager::getInstance()->registerEventHandler(
'rest',
'onApplicationManagerInitialize',
'demo.authprovider',
'\\Demo\\AuthProvider\\AuthProvider',
'onApplicationManagerInitialize'
);
\Bitrix\Main\EventManager::getInstance()->registerEventHandler(
'rest',
'onEventManagerInitialize',
'demo.authprovider',
'\\Demo\\AuthProvider\\EventProvider',
'onEventManagerInitialize'
);
Подключайте код в таком порядке:
- Создайте модуль
demo.authproviderв папкеlocal/modules/demo.authprovider/ - Разложите классы по файлам в папке
lib/ - Настройте провайдер и список разрешенных
client_idвinclude.php - Зарегистрируйте обработчики
AuthSimple::onRestCheckAuth,AuthFull::onRestCheckAuth,onApplicationManagerInitializeиonEventManagerInitializeвinstall/index.php - Установите модуль в административной части Битрикс24
- Выполните запрос с авторизационным токеном и проверьте результат методом
app.info
Если код не оформлен как модуль, его нужно подключать вручную до обращения к REST. Для рабочей коробочной установки оформите код как модуль: он дает автозагрузку классов из lib/ и сохраняет регистрацию обработчиков после обновлений.