Создание беспарольной аутентификации в Laravel, используя только email

от автора

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

Итак, мы решили попробовать метод беспарольного входа. Если Вы никогда не имели возможности работать с этим, мы расскажем как это работает:

На странице входа в систему пользователь вводит свой email-адрес, на который получит ссылку на вход. Переход по ссылке подтверждает личность пользователя без необходимости ввода пароля, так как ссылка для каждого входа пользователя уникальная.

Начнем творить!

image

Новое приложение и make:auth

Вначале мы создадим наше приложение, подключив аутентификацию:

laravel new medium-login cd medium-login php artisan make:auth 

Теперь у нас есть все необходимые для авторизации файлы, в том числе вьюхи. Давайте начнем с них.

Изменение страницы входа и регистрации

Конечно, сочетание логина с паролем довольно хорошая идея, но нам нужно отказаться от поля ввода пароля на обеих формах.

Откройте файл `resources/views/auth/login.blade.php` и удалите группу, отвечающую за ввод пароля (label, input и обертка ). Сохраняем, закрываем.

Теперь открываем файл `resources/views/auth/register.blade.php` и удаляем группы, отвечающие за ввод пароля (`password`) и подтверждения пароля (`password-reset`). Сохраняем, закрываем.

Позже Вы можете добавить инструкцию о методе входа на странице аутентификации, а также разместите ссылки на сброс пароля, но это позже.

Изменение регистрационных роутов

Итак, нам нужно изменить роут, указывающий на точки входа и регистрации. Взглянем на контроллер ` AuthController`.

Во-первых, мы заметим метод `validator`, возвращающий валидацию поля пароля. Так как он отвечает за процесс регистрации учетной записи, нам нужно избавиться от его привязки к паролю.

В конечном итоге, функция должна выглядеть так:

// app/http/Controllers/Auth/AuthController.php protected function validator(array $data) {     return Validator::make($data, [         'name' => 'required|max:255',         'email' => 'required|email|max:255|unique:users',     ]); } 

То же самое мы сделаем для метода `Create`, приведя его к виду:

// app/http/Controllers/Auth/AuthController.php protected function create(array $data) {     return User::create([         'name' => $data['name'],         'email' => $data['email'],     ]); } 

Перекрытие роута `login`

Как Вы можете видеть, здесь нет методов для регистрации пользователей. Они скрыты в трейте `AuthenticatesAndRegistersUsers`, который использует трейты аутентификации `AuthenticatesUsers` и регистрации `RegistersUsers`. Вы можете перейти к трейту `AuthenticatesUsers` и в конце файла найти метод аутентификации пользователей под именем `login`.

Все, что там происходит, основывается на защищенных паролях, хотя этот метод можно и заменить…

Целью нашего нового метода является отправка на email пользователя ссылки для входа в систему. Давайте вернемся к контроллеру `AuthController` и добавим метод входа в систему, перекрывающий `login` в `AuthenticatesUsers`:

// app/http/Controllers/Auth/AuthController.php public function login(Request $request) {     // validate that this is a real email address     // send off a login email     // show the users a view saying "check your email" } 

Подтверждение реальности email-адреса

Подтвердить реальность email адреса для зарегистрированного пользователя очень просто:

$this->validate($request, ['email' => 'required|email|exists:users']); 

Отправка email-сообщения

Далее, нам необходимо отправить пользователю ссылку на вход. Это займет немного больше времени.

Создание структуры для формирования и проверки токенов email

Если Вы знакомы с формой структуры базы данных `password_reset`, то Вам будет проще, т.к. мы будем создавать нечто похожее. Каждый раз, когда кто-то пытается войти в систему, нам нужно добавлять запись в таблицу, которая будет фиксировать адрес электронной почты и уникальный токен, отправляемые в электронном письме в качестве URL, а также дату создания и срок жизни записи.

В конечном итоге мы будем использовать URL-адрес для создания (и проверки), например: `myapp.com/auth/email-authenticate/09ajfpoib23li4ub123p984h1234`. Так как срок жизни токена ограничен, мы должны связать этот URL с конкретным пользователем, отслеживая email, токен и дату создания для каждой записи таблицы.

Итак, создадим для него миграцию:

php artisan make:migration create_email_logins_table --create=email_logins 

И добавим в нее несколько полей:

Schema::create('email_logins', function (Blueprint $table) {     $table->string('email')->index();     $table->string('token')->index();     $table->timestamps(); }); 

Примечание: при желании можно использовать значение колонки `id` вместо токена, но есть несколько причин более лучших вариантов. Во всяком случае, решать Вам.

Теперь, давайте создадим модель.

php artisan make:model EmailLogin 

Отредактировать файл (`app/EmailLogin.php`) и сделать его простым для нас, создав экземпляр с нужными свойствами:

class EmailLogin extends Model {     public $fillable = ['email', 'token']; } 

И когда хотим найти пользователя, мы должны использовать электронную почту, а не идентификатор, вручную связав столбец email:

class EmailLogin extends Model {     public $fillable = ['email', 'token'];      public function user()     {         return $this->hasOne(\App\User::class, 'email', 'email');     } } 

Создание токена

Теперь мы готовы к созданию email-сообщения. Мы будем использовать URL-адрес, содержащий уникальный токен, сгенерированный заранее.

Нужно понять, как мы будем создавать и хранить токен. Для этого, нам нужно создать экземпляр `EmailLogin`, так что приступим:

public function login() {     $this->validate($request, ['email' => 'required|email|exists:users']);      $emailLogin = EmailLogin::createForEmail($request->input('email')); } 

Давайте добавим этот метод в `EmailLogin`:

class EmailLogin extends Model {     ...     public static function createForEmail($email)     {         return self::create([             'email' => $email,             'token' => str_random(20)         ]);     } } 

Мы генерируем рандомный токен и создаем экземпляр класса `EmailToken`, получая его обратно.

Формирование URL для отправки по email

Итак, нам нужно использовать `EmailToken` для формирования URL перед отправкой сообщения пользователю.

public function login() {     $this->validate($request, ['email' => 'required|email|exists:users']);      $emailLogin = EmailLogin::createForEmail($request->input('email'));      $url = route('auth.email-authenticate', [         'token' => $emailLogin->token     ]); } 

Давайте создадим для него роут:

// app/Http/routes.php Route::get('auth/email-authenticate/{token}', [     'as' => 'auth.email-authenticate',     'uses' => 'Auth\AuthController@authenticateEmail' ]); 

… и добавим метод в контроллер для работы этого маршрута:

class AuthController {     ...     public function authenticateEmail($token)     {         $emailLogin = EmailLogin::validFromToken($token);          Auth::login($emailLogin->user);          return redirect('home');     } } 

… и еще добавим метод `validFromToken` для проверки токена:

class EmailLogin {     ...     public static function validFromToken($token)     {         return self::where('token', $token)             ->where('created_at', '>', Carbon::parse('-15 minutes'))             ->firstOrFail();     } 

Теперь у нас есть входящий роут, учитывающий актуальность каждого токена. Если токен актуален — пользователь будет перенаправлен по адресу `mysite.ru/home`.

Что ж, давайте отправим письмо.

Отправка письма

Добавим вызов `call email` в наш контроллер:

public function login() {     ...     Mail::send('auth.emails.email-login', ['url' => $url], function ($m) use ($request) {         $m->from('noreply@myapp.com', 'MyApp');         $m->to($request->input('email'))->subject('MyApp Login');     }); 

… и создадим шаблон:

<!-- resources/views/auth/emails/email-login.blade.php --> Log in to MyApp here: <a href="{{ $url }}">{{ $url }}</a> 

Возвращение шаблона

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

return 'Login email sent. Go check your email.'; 

Совместный вход

Взглянем на нашу систему. У нас есть новый метод `login` в контроллере `AuthController`:

public function login(Request $request) {     $this->validate($request, ['email' => 'required|exists:users']);      $emailLogin = EmailLogin::createForEmail($request->input('email'));      $url = route('auth.email-authenticate', [         'token' => $emailLogin->token     ]);      Mail::send('auth.emails.email-login', ['url' => $url], function ($m) use ($request) {         $m->from('noreply@myapp.com', 'MyApp');         $m->to($request->input('email'))->subject('MyApp login');     });      return 'Login email sent. Go check your email.'; } 

Мы создали несколько вьюх, обновив существующие (убрали в них записи о пароле). Также создали новый роут в `/auth/email-authenticate`. И также создали миграцию `EmailLogin` с классом всех его потребностей.

Это все!

И… профит! Поместите все примеры в Ваш код и получите полностью функциональную систему беспарольного входа.

Для регистрации пользователя нужно будет узнать всего-лишь их email-адрес. И при авторизации кроме их email-адреса больше ничего не нужно будет запоминать и вводить. Больше нет забытых паролей. Бум!

От переводчика

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

ссылка на оригинал статьи https://habrahabr.ru/post/279173/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *