29 ноября 2023 г.

Авторизация / регистрация в MODx

Как это работает?

Авторизация в modx работает на основе сессий. Встроенный механизм жуток и непонятен даже посвященным. Мы будет ее делать свой вариант на допольнительных полях и куках.

Авторизация:

  1. Если пользователь найден
  2. Если у него нет доп. поля acess_token, то создаем и сохраняем
  3. Пишем в куки токен
  4. Возвращаем поля пользователя и токен
  5. Токен сохраняем в локальном хранилище в браузере

Регистрация:

  1. Если пользователя с такой почтой не существует
  2. Регаем его, создаем email_verify_token, сохраняем в доп. (extended) поля
  3. Отправляем письмо на почту с ссылкой на созданную страничку, где мы проверим токен
  4. Если среди пользователей такой токен есть, то мы пользователя активируем
  5. email_verify_token удаляем из доп. полей

Получение инфы от авторизованного пользователя на бэкенде:

  1. После авторизации в куках у нас должен быть токен
  2. По токену ищем пользователя
  3. Возвращаем нужные данные или что-то модифицируем на сервере (если у пользователя есть персональная скидка на товар, например)

Получение инфы по юзеру по запросу с фронта xmlHttpRequest

  1. Из локального хранилища берем токен
  2. Добавляем в body
  3. На стороне сервера дергаем сниппет с функцией поиска юзера по токену
  4. Если пользователь найден, возвращаем его данные

Нюансы:

Это не совсем JWT-токен. Хотя можно использовать и эту библиотечку и генерить 2 токена access и refresh и определить время "протухания" accessToken-а. Либо воспользоваться функцией рандомной строки и сделать тоже самое, но руками

Токены хранятся в расширенных (дополнительных, extended) полях пользователя, точнее в профиле. Так же там можно хранить вообще че угодно, т.к. они хранятся в виде json-объекта. Но они не индексируются, поэтому на больших объемах делать поиск будет вероятно долго.

Подробнее с примерами кода

Проверка, существует ли пользователь:

Предполагается, что фронт будет передавать логин/пароль и action через xmlHttpRequest. По запросу, например, на эндпоинт "/api/user". В modx будут созданы соотвествующие ресурсы. В соотв. ресурсе с помощью fenom и switch мы будем перебирать action, и дергать соотв. сниппет, передавая ему тело запроса post

	
	{switch $.post.action}
		{case 'login'}
			{'login' | snippet : ['data' => $.post]}
	{/switch}	
	

Сниппет login:

	

	$data = json_decode($data['data'],true);

	$data = array(
  		'username' => $data['username'],
  		'password' => $data['password'],
  		'rememberme' => 1,
  		'login_context' => 'web');

	$response = $modx->runProcessor('/security/login', $data);

	if ($response->isError()) {

		$arr = [
			"success" => false,
			"message" => $response->getMessage()
		];

		header("HTTP/1.1 422 Неверный логин или пароль");
		return json_encode($arr);

	}

	$user = $modx->getObject('modUser', array('username' => $data['username']));

	
Сниппет поиска пользователя по токену: Принимает $token в качестве параметра
	

	$users = $modx->getCollection('modUser');

	foreach($users as $user){

		if(!$user) return false;

		$u = $user->toArray();

		$user = $modx->getObject('modUser', array('id' => $u['id']));
		$profile = $user->getOne('Profile');

		$fields = $profile->get('extended');

		if($fields['access_token'] == $token){
			return $user;
		}
	}

	return false;
	

Установка куки при успешной авторизации:

setcookie("access_token",'your_access_token', time()+3600,"/");
Удаление куки при логауте:

	setcookie('access_token', '', 1,'/');
	$modx->user = $modx->getObject('modUser', 0);