Authorization and registration components in CMS 1C-Bitrix

In CMS 1C-Bitrix, the developers are presented with four system components for implementing authorization functions, changing the password and registering ordinary users of the system (system.auth. *), But there is no official documentation for them. In this article, you will learn the limitations and disadvantages of using these components, why you should use them, how to use them better, and perhaps make some conclusions about why there is no documentation.

Principle of operation


By default, old 1.0 components are used for authorization and registration, the use of which is undesirable. This can be fixed by checking the checkbox “Use Components 2.0 for authorization and registration” in the settings of the main module or by executing the code once:
COption::SetOptionString('main', 'auth_comp2', 'Y');

After reviewing the description of the page execution order, we see that at step 1.13 (even before the output of header.php from the site template), level 1 access permissions are checked and if it does not pass, an authorization form is displayed. Let's analyze it in more detail.

By invoking the registration form, the following actions are meant:
- the page execution order continues to be executed without changes up to point 3 “Page body”, including the execution of header.php,
- instead of the “Page body”, depending on the variables passed to $ _REQUEST, one of the system components is connected system.auth. * (specifically system.auth.authorize if $ _REQUEST is empty), to which special parameters are passed (more on that below),
- the page execution order continues to be executed without changes, including the execution of footer.php.
This allows you to implement your own design (template) for the system.auth.authorize component, which organically fits into the site template between the header and basement.

Checking "level 1" access rights initiates an authorization form call in the following cases:
- if the global constant NEED_AUTH is defined and equal to true,
- if the user (including unregistered) does not have sufficient rights to read the file requested by him.

The rights to read files and sections for different user groups are formed when editing them with the administrative panel in the service files .access.php. Each file describes access levels to files and directories located at the same level. It is possible to create a .access.php file for each directory in the public part of the site, each subsequent such file complements the access rules for the directory chain. An example of such a file in the root of the site:

Such a distribution of rights allows literally "in place" to request the username and password of the user in cases when he goes without active authorization to a directory whose functionality requires authorization. This often happens if the user keeps in a favorite link not to the root of the site, but to a specific section with which he works most. As a result, the user enters the username and password without leaving the landing page, they are sent to its URL, instead of the page, the system component processes it, processing the data received, either generates an error or authorizes and reloads the page. The user is satisfied, he does not need to make an extra click to go to the desired section. The programmer is satisfied, he does not need to think about redirects to / auth / and $ backurl.

I recommend using the global constant NEED_AUTH only in specific cases, one of which is the authorization page itself in the standard delivery of the demo site at /auth/index.php:
SetTitle(GetMessage("AUTH_TITLE"));
?>


This script transfers control to the Bitrix kernel by defining the NEED_AUTH constant before doing so. Bitrix, if the user is not authorized, connects the header + system component (authorization) + basement and dies, preventing the script from running beyond the second line. If the user is authorized, he will receive a welcome message and a link to the root of the site.

Other system components


Let me remind you that when initiating authorization, instead of the "Body of the page", the system.auth.authorize component will be connected by the Bitrix core only if the user has not explicitly requested any other system component from the list:
-? Forgot_password = yes will connect system.auth.forgotpasswd (sending password change password),
-? change_password = yes will enable system.auth.changepasswd (password change by password),
-? register = yes will enable system.auth.registration (registration),
-? confirm_registration = yes will connect system.auth .confirmation (registration confirmation).
This approach allows the user to recover a password or even register an account without leaving the landing page. You can manipulate the available set of these links in your system component templates, but you cannot prohibit calling them when clicking on these links, and this is a fat minus.

Using the authorization component inside the page body


Sometimes it is necessary to embed the authorization form not instead of the body of the page, but inside the body of the page, for example at the address /about/index.php in the middle of the content. And here a nuance arises : it turns out that the system.auth.authorize system component does absolutely nothing regarding direct authorization, and therefore does not know anything about the authorization result. Therefore, if we connect the component without parameters, it will correctly authorize users, but will not display authorization errors. In fact, the authorization mechanism occurs somewhere deep in the bowels of the kernel, after which the kernel connects the component, passing the “AUTH_RESULT” parameter to it, setting the value from $ APPLICATION-> arAuthResult, which we have to repeat on our pages:
SetTitle("О сайте");
?>
...
IsAuthorized()): ?>
	IncludeComponent('bitrix:system.auth.authorize', '', array('AUTH_RESULT' => $APPLICATION->arAuthResult)); ?>

...


If the need for authorization arises during the execution of the component, you should call the authorization form using the CMain :: AuthForm () method, which will allow the user to log in not only without leaving the landing page, but also save all the parameters in the URL:
$APPLICATION->AuthForm(array(
	"MESSAGE" => "Для работы с ... требуется авторизация.",
	"TYPE" => "OK",
));


Create your own templates


In order to customize the appearance of the system forms of authorization, registration, password change request and password change using the control line, you should copy the existing templates of these components (/bitrix/components/bitrix/system.auth.*/templates/.default) into the template site (/bitrix/templates/<site_pattern>/components/bitrix/system.auth.*/.default), and then work with them. You cannot change the names of the transmitted fields in these forms, but you can add any other fields, for example, “telephone” to the registration, although at this stage the system component will not understand what you want from it with these new fields. You can also completely change the design and adjust the display of links to neighboring system components. When processing these templates, pay attention to the message output by the global ShowMessage function, the input string or array. You can get rid of its use, but it is better to override the system.show_message component template.

Enhanced Registration Functionality


Unfortunately, today system components are implemented in a procedural style, which does not allow you to inherit your own components from them, redefining and supplementing the functionality, so you have to use Bitrix events to solve such problems. The two most interesting events generated during user registration are: OnBeforeUserRegister and OnAfterUserAdd . Ideally, the handlers of these events are recorded when the module is installed, and deleted when the module is removed, for example:
// при установке модуля:
RegisterModuleDependences("main", "OnAfterUserAdd", $this->MODULE_ID, "CWBroker", "OnAfterUserAdd");
RegisterModuleDependences("main", "OnBeforeUserRegister", $this->MODULE_ID, "CWBroker", "OnBeforeUserRegister");
// при удалении модуля:
UnRegisterModuleDependences("main", "OnAfterUserAdd", $this->MODULE_ID, "CWBroker", "OnAfterUserAdd");
UnRegisterModuleDependences("main", "OnBeforeUserRegister", $this->MODULE_ID, "CWBroker", "OnBeforeUserRegister");

The OnBeforeUserRegister event is noteworthy in that it is not triggered by the usual addition of a user, whether it be an API call or manual addition in the administrative part, which allows you to change the logic of registration of the guest user, imposing additional restrictions on the fields or processing additional fields, for example, control over the numbers on the phone , control over the availability of data in the name and surname.
class CWBroker {
	// ...
	public static function OnBeforeUserRegister(&$arArgs) {
		global $APPLICATION;
		$_REQUEST['USER_PERSONAL_PHONE'] = preg_replace('#[^0-9]#', '', $_REQUEST['USER_PERSONAL_PHONE']);
		$arArgs['LOGIN'] = $arArgs['EMAIL'];
		$arArgs['CONFIRM_PASSWORD'] = $arArgs['PASSWORD'];
		$arArgs['PERSONAL_PHONE'] = $_REQUEST['USER_PERSONAL_PHONE'];
		$arArgs['PERSONAL_STATE'] = $_REQUEST['USER_PERSONAL_STATE'];
		if (empty($arArgs['NAME'])) {
			$APPLICATION->ThrowException('Не указано имя');
			return false;
		}
		if (empty($arArgs['LAST_NAME'])) {
			$APPLICATION->ThrowException('Не указана фамилия');
			return false;
		}
		if (empty($arArgs['PERSONAL_PHONE'])) {
			$APPLICATION->ThrowException('Не указан мобильный телефон');
			return false;
		}
		if (empty($arArgs['PERSONAL_STATE'])) {
			$APPLICATION->ThrowException('Не указан регион');
			return false;
		}
		if ($_REQUEST['USER_AGREE_TERMS'] <> 'Y' || $_REQUEST['USER_AGREE_PHONE'] <> 'Y' || $_REQUEST['USER_AGREE_EMAIL'] <> 'Y') {
			$APPLICATION->ThrowException('Необходимо принять все условия');
			return false;
		}
		return $arArgs;
	}
	// ...
}

The task of the event handler is to adjust the incoming $ arArgs data array or to report an error using the CMain :: ThrowException () method, returning false instead of the data array. Unfortunately, it is impossible to get the fields of interest to us through $ arArgs, so we have to analyze $ _REQUEST. The listing can be improved by grouping the errors found, but unfortunately, even if the handler succeeds, further errors may occur in the kernel during the registration process, which we cannot influence. The user is not satisfied receiving various errors during the registration process. The programmer is looking for alternatives.

The OnAfterUserAdd event is raised not only after successful self-registration of the user, but also when API calls are added or the user is added to the administrative part, but the data array passed to the handlers of this event does not affect anything except the handlers themselves, therefore it is practically useless to throw exceptions, but instead of this, you can perform some common logic for all new users, such as: add the user to the mailing list, create a budget for him in the online store, or assign any custom property.
class CWBroker {
	// ...
	public static function OnAfterUserAdd(&$arFields) {
		if (CModule::IncludeModule('inquiries')) {
			$CUser = new CUser();
			$CUser->Update($arFields['ID'], array('UF_TARIFF' => 1));
		}
		if (CModule::IncludeModule('sale')) {
			$sale_account = CSaleUserAccount::GetByUserID($arFields['ID'], 'RUB');
			if (empty($sale_account)) {
				CSaleUserAccount::Add(array(
					'USER_ID'		 => $arFields['ID'],
					'CURRENT_BUDGET' => 0,
					'CURRENCY'		 => 'RUB',
				));
			}
		}
		if (CModule::IncludeModule('subscribe')) {
			$CDBResult = CRubric::GetList(array("SORT" => "ASC"), array("ACTIVE" => "Y"));
			$rubrics = array();
			while ($rubric = $CDBResult->Fetch()) {
				$rubrics[] = $rubric['ID'];
			}
			$CSubscription = new CSubscription();
			$CSubscription->Add(array(
				'USER_ID'		 => $arFields['ID'],
				'EMAIL'			 => $arFields['EMAIL'],
				'FORMAT'		 => 'html',
				'CONFIRMED'		 => 'Y',
				'SEND_CONFIRM'	 => 'N',
				'RUB_ID'		 => $rubrics,
			));
		}
	}
	// ...
}


Alternatives


If you have the courage to crawl into the source code of system components (authorization), then any desire to work with them will suddenly disappear. As an obvious alternative, the first thing that comes to mind is the creation of its own components of authorization, registration and other things. The benefit of the API allows you to implement the same thing. However, in this alternative, I see the following fat cons:
- the cost of re-implementing the logic (in only one authorization component: verification captcha, secure key authorization and authorization through external services),
- refusal to update the system components under consideration, which is especially true with gradual the transition of the kernel to D7 ,
- the inability to connect your own authorization component to the user's landing page if he does not have enough rights to view the page; instead, he has to implement his own logic of accessibility of certain pages and sections.

Another alternative is the bitrix component : main.register , which has a wide range of parameters. However, in order for this component to be displayed and fulfilled on the user's landing page, the programmer has to trick: insert a call to this component into the template of the system component of registration (bitrix: system.auth.register), which entails the following disadvantages:
- the same set of component parameters is stored in at least two places: in the public part on the authorization page and in the site template,
- when bitrix: main.register is called through the bitrix: system.auth.register component template, the code of the latter is wasted.

Finally


It's no secret that the Bitrix platform is far from ideal. Every time I encounter such things that distort my poker interface, I first call them strange , then I try to figure out and understand why this was done and in most cases I find an acceptable answer. But in this case, I did not find an answer, so I decided to write down all my thoughts about it. I hope someone comes in handy.

Also popular now: