【laravel5.7】MultiAuthにパスワードリセットを追加する

MultiAuthにパスワードリセットを追加する実装メモ
[【laravel5.7】 MultiAuthログイン](https://qiita.com/kana-t/items/196fa542746db3a6a565)の続きとして書きます。



#やりたいこと
User/Adminそれぞれにパスワードリセットを実装したい


#環境
MacOS 10.14.3
VisualStudio
laravel 5.7

#Admin側の設定
##auth.php
リセット用のtokenを保存するテーブルが必要なので、auth.phpで設定します。

```diff:config/auth.php

'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
        ],

+        'admins' => [
+            'provider' => 'admins',
+            'table' => 'password_resets',
+            'expire' => 60,
+        ],
 ],

```

##Controller
###ForgotPasswordController.php
```diff:app/Http/Controllers/Admin/Auth/ForgetPasswordController.php
<?php

+namespace App\Http\Controllers\Admin\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;

class ForgotPasswordController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Password Reset Controller
    |--------------------------------------------------------------------------
    |
    | This controller is responsible for handling password reset emails and
    | includes a trait which assists in sending these notifications from
    | your application to your users. Feel free to explore this trait.
    |
    */

    use SendsPasswordResetEmails;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
+        $this->middleware('guest:admin');
    }

+    public function showLinkRequestForm()
+    {
+        return view('admin.auth.passwords.email');
+    }

+    protected function guard()
+    {
+        return \Auth::guard('admin');
+    }

+    public function broker()
+    {
+        return \Password::broker('admins');
+    }
}

```

###ResetPasswordController.php
```diff:app/Http/Controllers/Admin/Auth/ResetPasswordController.php
<?php

+namespace App\Http\Controllers\Admin\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
+use Illuminate\Http\Request;


class ResetPasswordController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Password Reset Controller
    |--------------------------------------------------------------------------
    |
    | This controller is responsible for handling password reset requests
    | and uses a simple trait to include this behavior. You're free to
    | explore this trait and override any methods you wish to tweak.
    |
    */

    use ResetsPasswords;

    /**
     * Where to redirect users after resetting their password.
     *
     * @var string
     */
+    protected $redirectTo = '/admin/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
+        $this->middleware('guest:admin');
    }

+    public function showResetForm(Request $request, $token = null)
+    {
+        return view('admin.auth.passwords.reset')->with(['token' => $token,'email' => $request->email]);
+    }

+    protected function guard()
+    {
+        return \Auth::guard('admin');
+    }

+    public function broker()
+    {
+        return \Password::broker('admins');
+    }
}

```

##Views
###email.blade.php
```diff:resources/views/admin/auth/passwords/email.blade.php
+@extends('layouts.admin.auth')

@section('auth')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-6">
            <div class="card mx-4">
                <div class="card-body p-4">
                    <h2>{{ __('Reset Password') }}</h2>
                    <p class="text-muted">リセットURLを送信します。</p>
                    @if (session('status'))
                        <div class="alert alert-success" role="alert">
                            {{ session('status') }}
                        </div>
                    @endif

+                    <form method="POST" action="{{ route('admin.password.email') }}">
                    @csrf
                    <div class="col-auto">
                        
                        <div class="input-group mb-3">
                            <div class="input-group-prepend">
                                <div class="input-group-text">@</div>
                            </div>
                            <input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ old('email') }}" required>
                        </div>
                        @if ($errors->has('email'))
                            <span class="invalid-feedback" role="alert">
                                <strong>{{ $errors->first('email') }}</strong>
                            </span>
                        @endif
                            
                        <button type="submit" class="btn btn-primary">
                            {{ __('Send Password Reset Link') }}
                        </button>
                    </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

```

###reset.blade.php
```diff:resources/views/admin/auth/passwords/reset.blade.php
+@extends('layouts.admin.auth')

@section('auth')
<div class="container">
        <div class="row justify-content-center">
            <div class="col-md-6">
                <div class="card mx-4">
                    <div class="card-body p-4">
                        <h2>{{ __('Reset Password') }}</h2>

                        {{-- <div class="card-body"> --}}
+                            <form method="POST" action="{{ route('admin.password.update') }}">
                                @csrf

                                <input type="hidden" name="token" value="{{ $token }}">
                                <div class="col-auto">
                        
                                    <div class="input-group mb-3">
                                        <div class="input-group-prepend">
                                            <div class="input-group-text">Email</div>
                                        </div>
                                        <input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ $email ?? old('email') }}" required autofocus>
                                    </div>
                                    @if ($errors->has('email'))
                                        <span class="invalid-feedback" role="alert">
                                            <strong>{{ $errors->first('email') }}</strong>
                                        </span>
                                    @endif
                                    
                                </div>

                                <div class="col-auto">
                        
                                    <div class="input-group mb-3">
                                        <div class="input-group-prepend">
                                            <div class="input-group-text"><i class="fas fa-key"></i></div>
                                        </div>
                                        <input id="password" type="password" class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" required>

                                        @if ($errors->has('password'))
                                            <span class="invalid-feedback" role="alert">
                                                <strong>{{ $errors->first('password') }}</strong>
                                            </span>
                                        @endif
                                    </div>
                                    
                                </div>

                                <div class="col-auto">
                        
                                    <div class="input-group mb-3">
                                        <div class="input-group-prepend">
                                            <div class="input-group-text"><i class="fas fa-key"></i></div>
                                        </div>
                                        <input id="password-confirm" type="password" class="form-control" name="password_confirmation" required>
                                    </div>
                                </div>

                                <div class="form-group row mb-0">
                                    <div class="col-md-6 offset-md-4">
                                        <button type="submit" class="btn btn-primary">
                                            {{ __('Reset Password') }}
                                        </button>
                                    </div>
                                </div>
                             </form>
                        {{-- </div> --}}
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

```

##ルーティング
###web.php
```diff:routes/web.php
// Admin
Route::group(['namespace' => 'Admin','prefix'=>'admin'],function(){
    // home
    Route::get('home','HomeController@index')->name('admin.home');

    // login logout
    Route::get('login','Auth\LoginController@showLoginForm')->name('admin.login');
    Route::post('login','Auth\LoginController@login')->name('admin.login');
    Route::post('logout','Auth\LoginController@logout')->name('admin.logout');

    // register
    Route::get('register','Auth\RegisterController@showRegisterForm')->name('admin.register');
    Route::post('register','Auth\RegisterController@register')->name('admin.register');

+    // password resets
+    Route::post('password/email','Auth\ForgotPasswordController@sendResetLinkEmail')->name('admin.password.email');
+    Route::post('password/reset','Auth\ResetPasswordController@reset')->name('admin.password.request');
+    Route::get('password/reset','Auth\ForgotPasswordController@showLinkRequestForm')->name('admin.password.update');
+    Route::get('password/reset/{token}','Auth\ResetPasswordController@showResetForm')->name('admin.password.reset');
});
```

##送信機能のオーバーライド
AdminPasswordResetNotification.phpを作ります。

```terminal:terminal
php artisan make:notification AdminPasswordResetNotification
```

```php:app/Notifications/AdminPasswordResetNotification.php
<?php

namespace App\Notifications;

use Illuminate\Auth\Notifications\ResetPassword as ResetPasswordNotification;
use Illuminate\Notifications\Messages\MailMessage;

class AdminPasswordResetNotification extends ResetPasswordNotification
{
    public function toMail($notifiable)
    {
        return (new MailMessage)
            ->line('ADMINパスワードリセット用メール.')
            ->action('Reset Password', url(config('url').route('admin.password.reset', $this->token, false)))
            ->line('If you did not request a password reset, no further action is required.');
    }
}
```

##Admin．phpの変更
AdminPasswordResetNotificationがメールで送信されるようにModelに設定します。

```diff:app/Admin.php
<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

+use App\Notifications\AdminPasswordResetNotification;

class Admin extends Authenticatable 
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password','api_token'
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

+    public function sendPasswordResetNotification($token)
+    {
+        $this->notify(new AdminPasswordResetNotification($token));
+    }
}

```

##動作確認
http://127.0.0.1:8000/admin/login

web
<img width="658" alt="スクリーンショット 2019-02-28 12.56.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/252667/24324be2-bebf-f6b4-779a-44a9b9825a2e.png">

入力したメールアドレス 
<img width="636" alt="スクリーンショット 2019-02-28 12.56.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/252667/2460d1f4-4b11-5770-86d3-c9b626c994e6.png">

Reset Passwordボタンをクリック
<img width="773" alt="スクリーンショット 2019-02-28 13.07.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/252667/2b15fb75-a76d-95bc-ae73-72ba4370e556.png">

こんな感じで動けばOK


#User側の設定
Adminとやることは変わりません
##Controller
###ForgotPasswordController.php
```diff:app/Http/Controllers/User/Auth/ForgetPasswordController.php
<?php

+namespace App\Http\Controllers\User\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;

class ForgotPasswordController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Password Reset Controller
    |--------------------------------------------------------------------------
    |
    | This controller is responsible for handling password reset emails and
    | includes a trait which assists in sending these notifications from
    | your application to your users. Feel free to explore this trait.
    |
    */

    use SendsPasswordResetEmails;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
+        $this->middleware('guest:user');
    }

+    public function showLinkRequestForm()
+    {
+        return view('user.auth.passwords.email');
+    }

+    protected function guard()
+    {
+        return \Auth::guard('user');
+    }

+    public function broker()
+    {
+        return \Password::broker('users');
+    }
}
```

###ResetPasswordController.php
```diff:app/Http/Controllers/User/Auth/ResetPasswordController.php
<?php

+namespace App\Http\Controllers\User\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
+use Illuminate\Http\Request;


class ResetPasswordController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Password Reset Controller
    |--------------------------------------------------------------------------
    |
    | This controller is responsible for handling password reset requests
    | and uses a simple trait to include this behavior. You're free to
    | explore this trait and override any methods you wish to tweak.
    |
    */

    use ResetsPasswords;

    /**
     * Where to redirect users after resetting their password.
     *
     * @var string
     */
+    protected $redirectTo = '/user/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
+        $this->middleware('guest:user');
    }

+    public function showResetForm(Request $request, $token = null)
+    {
+        return view('user.auth.passwords.reset')->with(['token' => $token,'email' => $request->email]);
+    }

+    protected function guard()
+    {
+        return \Auth::guard('user');
+    }

+    public function broker()
+    {
+        return \Password::broker('users');
+    }
}
```
##Views
###email.blade.php
```diff:resources/views/user/auth/passwords/email.blade.php
+@extends('layouts.user.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Reset Password') }}</div>

                <div class="card-body">
                    @if (session('status'))
                        <div class="alert alert-success" role="alert">
                            {{ session('status') }}
                        </div>
                    @endif

+                    <form method="POST" action="{{ route('user.password.email') }}">
                        @csrf

                        <div class="form-group row">
                            <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>

                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ old('email') }}" required>

                                @if ($errors->has('email'))
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $errors->first('email') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>

                        <div class="form-group row mb-0">
                            <div class="col-md-6 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Send Password Reset Link') }}
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

```

###reset.blade.php
```diff:resources/views/user/auth/passwords/reset.blade.php
+@extends('layouts.user.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Reset Password') }}</div>

                <div class="card-body">
+                    <form method="POST" action="{{ route('user.password.update') }}">
                        @csrf

                        <input type="hidden" name="token" value="{{ $token }}">

                        <div class="form-group row">
                            <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>

                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ $email ?? old('email') }}" required autofocus>

                                @if ($errors->has('email'))
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $errors->first('email') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>

                            <div class="col-md-6">
                                <input id="password" type="password" class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" required>

                                @if ($errors->has('password'))
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $errors->first('password') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>

                            <div class="col-md-6">
                                <input id="password-confirm" type="password" class="form-control" name="password_confirmation" required>
                            </div>
                        </div>

                        <div class="form-group row mb-0">
                            <div class="col-md-6 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Reset Password') }}
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
```

##ルーティング
###web.php
```diff:routes/web.php
// User
Route::group(['namespace' => 'User','prefix'=>'user'],function(){
    Route::get('/',function(){
        return redirect()->to('user/home');
    })->name('user');
    // home
    Route::get('home','HomeController@index')->name('user.home');

    // login lgoout
    Route::get('login','Auth\LoginController@showLoginForm')->name('user.login');
    Route::post('login','Auth\LoginController@login')->name('user.login');
    Route::post('logout','Auth\LoginController@logout')->name('user.logout');

    // register
    Route::get('register','Auth\RegisterController@showRegisterForm')->name('user.register');
    Route::post('register','Auth\RegisterController@register')->name('user.register');

    // emailverify
    Route::middleware('throttle:6,1')->get('email/resend','Auth\VerificationController@resend')->name('user.verification.resend');
    Route::middleware('throttle:6,1')->get('email/verify','Auth\VerificationController@show')->name('user.verification.notice');
    Route::middleware('signed')->get('email/verify/{id}','Auth\VerificationController@verify')->name('user.verification.verify');


+    // password reset
+    Route::post('password/email','Auth\ForgotPasswordController@sendResetLinkEmail')->name('user.password.email');
+    Route::post('password/reset','Auth\ResetPasswordController@reset')->name('user.password.request');
+    Route::get('password/reset','Auth\ForgotPasswordController@showLinkRequestForm')->name('user.password.update');
+    Route::get('password/reset/{token}','Auth\ResetPasswordController@showResetForm')->name('user.password.reset');
});
```

##送信機能のオーバーライド
UserPasswordResetNotification.phpを作ります。

```terminal:terminal
php artisan make:notification UserPasswordResetNotification
```

```php:app/Notifications/UserPasswordResetNotification.php
<?php

namespace App\Notifications;

use Illuminate\Auth\Notifications\ResetPassword as ResetPasswordNotification;
use Illuminate\Notifications\Messages\MailMessage;

class UserPasswordResetNotification extends ResetPasswordNotification
{
    public function toMail($notifiable)
    {
        return (new MailMessage)
            ->line('USERパスワードリセット用メール.')
            ->action('Reset Password', url(config('url').route('user.password.reset', $this->token, false)))
            ->line('If you did not request a password reset, no further action is required.');
    }
}
```

##User．phpの変更
UserPasswordResetNotificationがメールで送信されるようにModelに設定します。

```diff:app/User.php
<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

+use App\Notifications\UserPasswordResetNotification;

class User extends Authenticatable implements MustVerifyEmail
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

+    public function sendPasswordResetNotification($token)
+    {
+        $this->notify(new UserPasswordResetNotification($token));
+    }
}
```

##動作確認
http://127.0.0.1:8000/user/login

Adminと同じように動けばOKです。

メール内容
<img width="635" alt="スクリーンショット 2019-02-28 12.57.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/252667/a7c845d3-10a5-d5e7-e425-01814d45dc2d.png">


以上です。