Всем привет!
Почти два дня ушло на то, чтобы переделать здесь форму авторизации. На самом деле она не только здесь, потому что компонент Auth находится в составе компонента @prisma-cms/front, а значит он автоматом обновится на всех сайтах на базе @prisma-cms.
Вообще я думал справлюсь быстрее, но по факту потратил часов 20. Слишком много оказалось на это завязано. Основная задача стояла так: улучшить механизм смены пароля пользователя. Ранее ведь было как: пользователь запрашивал смену пароля, пароль у этого пользователя сразу менялся, а на почту отправлялся новый пароль. Тут угрозы не много, если нет доступа к почте, то и пароль новый никто лишний не получит, но можно так скинуть пароль тому, к профилю которого человек вообще никакого отношения не имеет. А злоумышленник какой-нибудь и вовсе мог написать скрипт и сбросить вообще всем пользователям пароли. Не круто это. Короче, вот это я и решил улучшить. Но в итоге оказалось, что я открыл ящик Пандоры, который еле-еле закрыл...
Началось с малого: добавить поле ввода кода в форму авторизации. То есть сценарий предполагался следующий: пользователь находит свой профиль, жмет кнопку "Сменить пароль", ему на почту отправляется код, который он должен ввести. Соответственно, должно быть поле для ввода. А еще в авторизации должна быть постраничность для вывода списка найденных профилей.
Не знаю как кого, а меня дико бесит, когда заходишь на сайт, на котором давно не был, надо авторизоваться, но не помнишь какой логин или почта у тебя там. А еще отдельные умники для восстановления предлагают ввести только логин или только емейл. То есть если ты помнишь логин, но не помнишь емейл, а тебе предлагают для восстановления ввести именно емейл, то это просто ппц. При этому когда регистрируешься, тебе конечно же говорят "этот логин уже занят" или "этот емейл уже занят"... В общем, полно было уже статей написано, что лишние эти сложности при входе не добавляют никак безопасности, но сильно снижают удобство. Теперь здесь, даже если вы не помните ни логин ни емейл, но указывали хотя бы имя, вы можете в форме авторизации найти пользователя по любому полю и восстановить пароль (при условии, что у вас есть доступ к вашей почте). Лично мне такой функционал удобен. Если у кого есть свое мнение, высказывайтесь, интересно.
И вот, я решил не просто добавить эти поля, а переписать компонент Auth, потому что он писался очень давно и имел довольно много недостатков. Но как оказалось, логика значительно шире, чем кажется на первый взгляд, и я замучился писать его, чтобы он боле менее соответствовал желаемому :) Попробуйте сами, что скажете? Удобно или нет сделано?
Ладно, форма это только фронт. А вот надо же еще логику реализовать, чтобы код хранить, использовать его и т.п. Плюс к этому я еще хотел сделать так, чтобы можно было отправлять сразу несколько уведомлений (на почту и sms на телефон). Пришлось для этого еще дописать модуль @prisma-cms/user-module, дописав в него сущность ResetPassword она связана с сущностью Пользователь: http://joxi.ru/ZrJeOj1hw1ko3A. То есть мы создаем запись с кодом и связкой с Пользователем, и в дальнейшем надо получить этот код и ввести его. По коду будет получен пользователь и ему установится новый пароль. При этом пользователь будет сразу же авторизован, то есть как только вы вводите код и он успешно проходит, вы авторизуетесь. Это типа как одноразовый пароль (кстати, если где-то понадобятся одноразовые пароли, считай, что они есть). Логика сброса пароля при вводе кода прописана здесь. И вот на это ушл еще куча времени. Мой перфекционизм заставил меня написать больше, чем я хотел. Логика получилась довольно объемная:
1. Как я и планировал, можно использовать несколько способов отправки кода. В настоящий момент их два (почта и sms). Для отправки sms был еще немного допилен модуль @prisma-cms/sms-module, о котором я писал в прошлой заметке.
2. Проверка кода. Изначально я думал, что просто буду код проверять (отправлен код, если найдена запись, то ОК, сбрасываем). Но я посчитал, что это хоть и не ясные риски несет, но все же несет. Есть возможность заюзать перебор кодов. В итоге я сделал, что отправлять надо id пользователя и код. В таком случае перебор выполнять сложнее, то есть даже если инициировать сброс пароля у всех юзеров и начать делать перебор... И вот тут запинка... С учетом того, что сразу происходит авторизация, то злоумышленнику не надо знать какой новый пароль был установлен, ведь он сразу получает новый токен сессии, а это уже все, проникновение... Так что риски все-таки есть. Но на это у меня ответ есть. Как раз обдумывая схожие сценарии (пусть и не учтя этот момент). я ввел еще два момента: 1. задержка при обработке запроса, то есть когда отправляешь запрос на проверку кода и сброс пароля, выставляется таймаут 3 секунды, и только потом уже выполняется непосредственно проверка кода и отдается ответ. 2. В единицу времени можно выполнять только одну проверку кода для одного пользователя. То есть нельзя отправить 100500 отдельных запросов, запараллелить их. Как только получен один, сохраняется признак пользователя, и пока его запрос не будет обработан с задержкой 3 секунды, все остальные запросы игнорируются. Таким образом в час можно выполнить только 1200 запросов (время жизни кода выставлено час, но можно кастомно выставлять другие сроки, хоть 1 минуту). С учетом того, что код содержит 6 символов, включая числа, то есть это где-то 35 в 6-ой степени, то есть примерно 1838265625 вариантов, шанс за 1200 попыток подобрать верный вариант - совсем ничтожный. А если вам мало, то можно указать в генерации кода и заглавные буквы, и спецсимволи и т.п., так что тогда вообще без вариантов будет.
3. Если код был введен верно, вместе с ним из БД удаляются и все другие коды для этого пользователя, если такие имеются (на случай если пользователь несколько раз запросил смену и какой-нить злоумышленник перехватил один из них, но не успел воспользоваться).
Было с этой задачей еще куча всяких мелких и не очень задач, из-за чего сроки растянулись, но на выходе получился очень интересный компонент, как по мне.
А так чего-то часто получается: считаешь, что дел на час, глядь - третий день пошел:)))
Ага, именно так))
Кстати, надо улучшить: когда ввел код и авторизовался, не закрывать форму, а просить сразу ввести новый пароль, чтобы прям в этом окне было.