Magic link или история о том, как мы упростили жизнь пользователю

    В статье я поделюсь личным опытом разработки и реализации “волшебных ссылок”. Расскажу зачем они нужны в нашем проекте, как функционируют и в конце даже всплакну над тем, что отсутствие такого функционала в больших системах используемых мною заставляет меня гневно рвать на себе волосы.

    Код будет только в одном месте, т.к. основная цель статьи — рассказать про саму идею, а не показать то, насколько красив код.



    Вступительное бла бла бла


    Порой прочитать и изучить чужой опыт гораздо проще, чем часами ломать голову себе и своим соклавишникам в реализации нового “уникального” функционала. Уникальное в наше время редко где встретишь. Зачастую любой функционал уже кем-либо использовался и тебе, кнопкодав, остаётся только интегрировать его в свою рабочую область.

    В проекте, над которым я работаю, любой пользователь может зайти задать вопрос юристам. Это его основная задача, которую в статье я и буду рассматривать.

    Какой у меня пароль?


    Приходя на нашу платформу, пользователю надо только одно — помощь. Он не хочет регистрироваться, а затем авторизовываться только для того, чтобы задать вопрос. Ему нужен ответ здесь и сейчас.

    Изначально у нас нельзя было задать вопрос не будучи залогиненным. Пользователи к этому никаких претензий не предъявляли. В форме регистрации ввести ящик и пароль не оказалось проблемой. Изначально юристы реагировали медленно, т.к. вопросов было не много, а потому клиент даже успевал забыть, что такой вопрос у него есть. Получив ответ в системе и уведомление на свою почту, пришло время клиенту узнать что же там юрист такого написал. И тут наступает коллапс: бОльшая часть пользователей не осилила авторизацию, т.к. банально забыли пароль. И не то, чтобы у нас не было формы восстановления пароля, но она не сильно помогала. Мы стали терять клиентов…

    Хочу просто задать вопрос


    Приняли решение, что можем открыть возможность создания вопроса для гостей. Клиент в форме создания сущности дополнительно вводит почтовый ящик и всё. Нет шага регистрации. Клиент захотел задать вопрос — он его задал.

    Юрист, взяв в работу заданный гостем вопрос, даёт раскрытый обширный ответ с учётом всех кейсов… кхм… пишет пару строчек умных слов и всё — ответ дан.

    нашпроект.ком алахомора!


    Система отправляет клиенту уведомление о том, что на его вопрос был дан ответ. Но в письме появляется некая ссылка — magic link, при клике на которую происходит авторизация пользователя и он с довольным лицом оказывается на странице своего вопроса. Всё.

    Тут и сказочке конец, а кто слушал молодец.

    Теперь я расскажу о технической стороне этого “прогресса”.

    Запили мне хэш


    Проект состоит из 2х частей: фронт и бэк. На бэке был разработан сервис, который отвечает за работу системы magic link.

    Изначально система генерирует рандомный набор из 6 символов (буквы + цифры) — hash, который сохраняет в базу. На уникальность hash, конечно же, проверяется.

    У каждого хэша есть своё время жизни, которое зависит от роли пользователя, для которого он генерируется (т.к. позже такие ссылки стали приходить и юристам, у которых память на пароли ещё дырявее) и типа операции, в результате которого он был сгенерирован (ответ на вопрос, создание вопроса, комментарий и т.п.). Пользователей у нас в базе порядка 14 000, т.е. мало и 6-символьный хэш, как показывает практика, пока себя вполне адекватно ведёт. Хэши, у которых истёк срок действия удаляются. Мы решили, что 6 символов — идеальный вариант для запоминания или самостоятельного ввода в адресную строку, которая, к слову выглядит весьма красиво — /hash/haSH12.

    Каждый хэш привязан к тому пользователю для которого он генерируется. Также он привязан к сущности (ID + тип), операция над которой вызвала генерацию хэша.

    Переходя по magic link фронт отправляет на бэк хэш и ждёт в ответе указания что делать дальше. Если хэш валиден — происходит авторизация пользователя и последующий его редирект на детальный просмотр сущности. Если хэш не валиден — фронт просит пользователя авторизоваться.

    Со временем развития платформы прилетело требование о том, что в случае операции комментирования пользователь, переходя по хэшу, должен скроллиться к новому комментарию. Таким образом, хэш получил дополнительную настройку, которую мы назвали “локация” и в которой храним роут фронта с дополнительными параметрами (в данном случае ID комментария), на который происходит редирект пользователя после авторизации.

    Следующее развитие платформы потребовало возможность выполнения ряда операций перед тем, как пользователь будет авторизован. Например, верифицировать профиль или запустить какой-то таймер дедлайна ответа на вопрос. Это подарило хэшу настройку с названием “алиас”. Система обработки хэшей, находя нужный хэш в базе, смотрит на наличие алиаса — обычная строка, которая хранит текстовую константу. Если алиас есть, по нему система создаёт вызов метода, в котором и выполняются необходимые операции до того, как пользователь будет авторизован

    public function executeAlias()
    {
        if(!$this->hash->alias){
    	return $this;
        }
    
        //camelCase alias
        $aliasMethod = Str::camel($this->hash->alias);
    
        if(!method_exists($this, $aliasMethod)){
    	return $this;
        }
    
        return $this->{$aliasMethod}();
    }
    

    Данная система по управлению хэшами настолько хорошо себя зарекомендовала, что мы её распространили на доступ к файлам. Ситуация простая: в вопросе клиент загружает файл, бэк сохраняет его у себя где-то там в тёмных недрах сервера, но пользователи (что клиент, что юрист) видят красивую ссылку /storage/hash12/some_CODE. Система по управлению файловыми хэшами при загрузке файла сразу генерирует пару хэш-код и к ним дополнительно привязывает путь к файлу и всё это сохраняет в базе.

    Код мы планируем интегрировать и для magic link, чтобы в будущем использовать его для двухфакторной авторизации. Пользователь, переходя по magic link будет получать на телефон код и вводить его в предложенной фронтом форме.

    Данная система позволила нам настроить удобный и гибкий доступ пользователя к системе. Анализируя ситуацию, мы понимаем, что ошибки с неправильным вводом пароля и количество писем с напоминанием пароля почти сошли на “нет”. Мы можем изменять время жизни каждого хэша отдельно или генерировать его для какого-то отдельно взятого пользователя по его запросу.

    Плак, плак


    И в конце всплакиваю. Всплакиваю от того, что сайты\платформы, которыми иногда пользуюсь такую систему не используют или в качестве хэша выступает строка из 40 символов, т.к. иногда есть необходимость не переходить по ссылке из письма, а ввести её вручную в адресной строке. Хотя, не отрицаю того, что с ростом нашего проекта когда-то и мы придём к длине такого хэша…

    Спасибо за внимание.

    P.S. напоминаю, что это личный опыт и я оставляю свой камушек в большом огороде информации.
    Поделиться публикацией
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 13
      +3
      Приняли решение, что можем открыть возможность создания вопроса для гостей. Клиент в форме создания сущности дополнительно вводит почтовый ящик и всё. Нет шага регистрации. Клиент захотел задать вопрос — он его задал.
      Я бы добавил в пост информацию о том, что у этого подхода есть 2 довольно неприятных минуса:
      1. опечатки при вводе почты, которые приводят к тому, что сотрудник Сервиса тратит время напрасно, т.к. его ответ не дойдет получателю;
      2. отсутствие возможности фильтровать спамеров/неадекватов. Например, после того как в нашей ленте публикаций сделали приоритетной сортировку по «лучшим» (т.е. уже больше 9 месяцев) в наш саппорт, с периодичностью раз-два в неделю, какой-то ребенок-индиго шлет текст «на главной только Х страниц» с темой «ошибка в работе сайта», и нет никакой возможности обратить его внимание на то, что в шапке ленты есть переключатель фильтра публикаций, поскольку в качестве контактных он указывает адреса типа hj@hjdf.fh
        0
        Да, с минусами согласен. Нюанс с опечаткой есть, к сожалению, при обычной регистрации. Когда потом не можешь авторизоваться, хотя уверен на 100% в том, что «я же регался».
          0

          Юзайте что-то типа BriteVerify при регистрации/запросе и ситуация будет намного лучше

            0
            При регистрации (когда пользователь заинтересован в корректном указании адреса) это действительно целесообразно. А вот при запросах от всяких «одаренных», как мне кажется, не особо. По адресу hj@hjdf.fh хотя бы сразу видно, что на него можно не отвечать. А, если заставить его вводить валидный адрес, то свой адрес он все-равно не укажет и, в итоге, наш ответ получит сторонний человек.
          +1
          Не понадобятся вам 40-символьные хеши: habr.com/company/virgilsecurity/blog/311676

          А вообще идея простая и годная. Аж себе записал, т.к. проблема забытых паролей очень актуальна, а восстанавливать пароль догадается не каждый.
            0
            За статью спасибо, ознакомимся.
            0

            Мы такую систему уже довольно долго используем для логина, смены пароля, мыла и т.д. Единственный минум, это пререндеринг таких ссылок, так как ссылки у нас одноразовые

              0
              А я для таких вещей использую JWT токен. Да, длинный, но в базе хранить не надо, и деактивируется автоматически. Плюс в него можно записать доп. информацию. Вот одноразовую ссылку без базы не сделаешь. Но даже в этом случае можно хранить не все сгенерированные токены, а только уже использованные и не весь, а только ID токена.
                0
                Я прямо представил кривую реализацию такой системы, когда в цикле генерятся рандомные хеши, происходит попытка INSERT в базу, ловля исключение not uniq constrains и далее в цикле продолжаем пытаться сгенерить новый хеш (у читывая что у вас всего 6 символов)
                Надеюсь у вас не так
                Идея писать короткие ссылки руками плоха тем, что любой человек может начать эти ссылки перебирать
                  0
                  Мне такое встретилось в Slack. ОЧЕНЬ удобно — особенно для авторизации с мобильных устройств (там у них сразу приложение открывается(!)).
                    0
                    Сам такие авторизационные ссылки часто где встречаю, но, как правило все они громоздкие. Как-то оформлял покупку в интернет-магазине и мне пришла СМС с такой ссылкой, чтобы я мог проверить состояние заказа. Открывать ссылку на телефоне желания не было, но пришлось, т.к. вводить в браузере 40 символов ещё то удовольствие.
                    0
                    Удобно — да, из минусов, при таком подходе:
                    1. ссылка должна быть одноразовой
                    2. в контенте письма не должно быть даже части контента, который увидит пользователь после перехода по ссылке, только кнопка «перейти на сайт». В противном случае пользователь может переслать это письмо кому-то еще (например, если в первых строках содержится ответ на вопрос), с целью поделиться полезностью и таким образом сольет доступ к своему аккаунту
                      0
                      Спасибо, согласен. У нас ссылка не одноразовая, но имеет время жизни. И 2й пункт также придерживаемся: если есть контентная информация, ссылки нет и наоборот. Правда, о Вашем аргументе не задумывались, но теперь будем им оперировать.

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое