laravel ui 認証

# リファレンスサイト
https://github.com/mahmoudyounesg/laravel-multi-auth-tutorial.git

https://reffect.co.jp/laravel/laravel-multi-authentication-understand

https://www.webopixel.net/php/1665.html

# Breaze認証
[laravel Breeze 認証](https://qiita.com/kennsukea/private/a576c1dfdb088b568b55)

# 作成するlaravelのui認証機能一覧

 1. ログイン機能
 1. パスワードのリセット機能 
 1. Email Verification機能(登録されたメールの確認機能)
     1. メールの実在性の確認
     1. ログインかつメール認証済みのユーザだけを許可する認証ができる。
 1. password confirm機能(再度passwordの再確認を求める)

認証機能はuserとadminで別々に作成します。

## larabelのインストール

```c:$
laravel new laravel-multi-auth
```

## ui認証機能のインストール

```c:version3.4からjQueryがデフォルトで削除されました。
composer require laravel/ui:^3.3
```
```c:bootstrapの他に・react・vueがあります。
php artisan ui bootstrap --auth
```
```c:$
npm install && npm run dev
```
```c:$
npm run dev
```
## user認証を作成する
usersテーブを作成するだけ

データベースはsqlite

```c:env
#データベースをmySqlからsqliteに変更
DB_CONNECTION=sqlite
# DB_CONNECTION=mysql
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=laravel
# DB_USERNAME=root
# DB_PASSWORD=
```
- database.sqliteファイル の作成
```c:windows
type nul > database/database.sqlite
```
- 環境変数にgitを通していたら `windows`でも`toouch`コマンドが使える。
```c:mac
touch database/database.sqlite
```
```c:usersテーブル
php artisan migrate
```
認証リンク
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/0107cd06-958a-8f41-6c36-64c9436a0e1a.png)
user登録画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/cb4f2eed-e9f3-5900-4038-4efd29755d71.png)
user登録するとusersテーブルにデータが登録されて
[sql viewer](https://marketplace.visualstudio.com/items?itemName=qwtel.sqlite-viewer)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/fc3bc7f4-019d-20a1-6715-7a7a34156134.png)
登録されたデータでログインできる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/79a65763-c10b-c8c4-e069-1c3cdd35465e.png)
リメンバーミーにチェックをいれる。セッションが切れても次回アクセス時自動でログインしてくれる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/ef4f9d6e-950a-5cbc-9728-8ab75040184a.png)
セッション時間はデフォルトで2時間
リメンバーミーを設定すると5年

[通常のセッションのためのクッキー ララジャパン](https://www.larajapan.com/2016/11/13/laravel-5-3-remember-me/)

```php5:session時間
//config\app.php
   //session時間
    'lifetime' => env('SESSION_LIFETIME', 120),
         //env
               SESSION_LIFETIME=120
//config\session.php
   //ブラウザをクローズした時,sessionを(true:切る)(false:切らない)
    'expire_on_close' => false,

   //クッキーの名前
    'cookie' => 'laravel_session',
```
//remember meで延長される時間 5年 2628000分<-変更する時はこの数字を変更する

```php5:vendor\laravel\framework\src\Illuminate\Cookie\CookieJar.php
    public function forever($name, $value, $path = null, $domain = null, $secure = null, $httpOnly = true, $raw = false, $sameSite = null)
    {
        return $this->make($name, $value, 2628000, $path, $domain, $secure, $httpOnly, $raw, $sameSite);
    }
```
パスワードリセットを確認をする。(forget passwordリンク).

<details><summary>メール送信のための環境設定</summary><div>
<details><summary>googleメールを使用する。</summary><div>
３番めのカードの　Google へのログイン　
1,[googleの2段回認証プロセスをオンにする](https://myaccount.google.com/security)
2.同じサイトで、上をオンにするとアプリ パスワードの設定ができる。
①アプリを選択->その他->②名前を入力->適当な名前
お使いのデバイスのアプリ パスワード 123456789 <-メールパスワードになる。
3. envファイルの設定

```:env
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=グーグルのメール
MAIL_PASSWORD=123456789
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=グーグルのメール(適当なメールでもOK,but必要null値はerrorになる)
MAIL_FROM_NAME="٩(♡ε♡ )۶of٩(♡ε♡ )۶"
```
</div></details>
<details><summary>**MailTrap**のダミーのSMTPサーバを使用する</summary><div>

[MailTrap](https://mailtrap.io/)

[https://reffect.co.jp/laravel/mailtrap-dummy-smtp-server](https://reffect.co.jp/laravel/mailtrap-dummy-smtp-server)

![mailltrop2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/bbdb81c7-1bda-8f4c-f70d-37b778f60746.png)

</div></details>
</div></details>

リンクをクリックするとメールアドレスの入力フォームが表示される
`resources\views\auth\passwords\email.blade.php`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/d0c5e342-1c8d-2f97-6a6b-5eeea9df7566.png)
データベースにアドレスがなければエラーになる
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/ee6e2788-d7be-9e84-0f4b-cbff052580ea.png)
あれば、メールを送信しましたとのアラートが表示される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/f4762a7c-0c43-42ab-24b3-19f7d076ee22.png)
送られてきたメールのリンクをクリックすると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/dba722b9-ca29-817b-de98-b1be2b43f8e3.png)
パスワードの再入力画面が表示される
`resources\views\auth\passwords\reset.blade.php`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/5770dd6b-a72b-a2cf-b54e-62df2e73ba4c.png)
パスワードが再設定されログインされる。
`resources\views\home.blade.php`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/b5283d2b-f2ed-0d43-a929-7deb2fe494d9.png)


## mail Verification機能
- 非検証のメールから検証済みのメールへ変更する。

ユーザーモデルにimplements MustVerifyEmail　を追記

```php5:app\Models\User.php
// class User extends Authenticatable
class User extends Authenticatable implements MustVerifyEmail
```
ユーザー登録時メールの確認も行う場合
`[verify=>true]`を`Auth::routes()`に追加

```php5:routes\web.php
//Auth::routes()
Auth::routes(['verify' => true]);
```

登録が完了すると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/2f330252-4570-427d-c988-83b925bf6365.png)
普通にログインできる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/4774d141-4803-f01a-f77c-c97d41fafebd.png)
ただ、メールが送られてくるのでリンク先にアクセスすると。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/40b2f361-32c8-eb92-3a9d-0a9589204838.png)
メールの検証が完了する。テーブルに検証時間が入る。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/da4439ae-f37a-ad50-8164-fc23e2fb2a20.png)
メール検証済みのユーザーのみが許可されるルートを作成する。

```php5:web.php
Route::get('/test', function () {
    return 'メールは検証済みです。';
})->middleware(['verified']);
```
検証済みのユーザーがアクセスすると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/68328749-b9b6-2b3d-c091-1f23d67ca350.png)
未検証のユーザーがアクセスすると
リダイレクトされる。リンクをクリックすると。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/4bcd2b9d-cd39-f2c1-eaa1-8e486a1d6025.png)
未検証のメール宛にメールが送信される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/93ffbc49-e27e-af98-d639-9af6d636ad00.png)
送られてきたメールのリンクにアクセスすると検証済みのメールになり、サイトに許可される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/68328749-b9b6-2b3d-c091-1f23d67ca350.png)

## password.confirm
- ユーザのパスワードの再確認を求めるルート

```php5:web.php
//middleware(['auth', 'password.confirm'])をはってやる。必ずauthとセット
Route::get('/confirmed', function () {
    return 'パスワードの確認';
})->middleware(['auth', 'password.confirm']);
```
アクセスするとパスワードの再入力画面にリダイレクトされる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/2b797b21-0012-137a-3f3c-dae38070c038.png)
確認されるとアクセスサイトにリダイレクトされる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/bb35c27d-0b61-bcf7-4d63-2b9992e0af90.png)
その他パスワードのリセットをすると、password_resetsテーブルにメールアドレスとトークンと時間が記録される。時間内にメールのリンクにアクセスすると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/75b32916-49a2-7abb-68b0-351b07457a8a.png)
パスワードのリセットが完了すると消去される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/d70570b0-a104-00ab-4083-d455ebb5ce88.png)

# マルチ認証を実装する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/edb88ef3-7115-091c-f2e9-8e64feebf852.png)
- 修正１⃣
  - メールのviewsと宛先の修正。
- 修正２⃣
  - ルートの修正
    - これは作成と同時に修正されるので修正は不要
- 修正３⃣
  - viewsのリンクの宛先の修正
- 修正4⃣
  - コントローラーでリターンするviewsを修正
- 修正5⃣
  - ガードとモデルを修正する
- 修正6⃣
  - ミドルウェアのリダイレクト先の修正
# ➀,➁,➂ guardとmodelを作成(コピペ)する
[[図解] Laravel の認証周りのややこしいあれこれ。](https://zenn.dev/ad5/articles/48671b32c89897)

 1. まず、2種類のログイン機構が存在するため、ガードを2つに分けます。
 1. それぞれのテーブルを管理するモデルが必要なためモデルも2つに分ける
  必然的に、ログイン対象のモデルを管理している「プロバイダ」も2つに分ける
 1. プロバイダーが分かれたため、パスワードのリセットも分ける。

## 認証を新たに追加する。

```php5:config\auth.php
    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
//+provider＋driver(session)がguardになる。
        'admin-web'=>[
            'driver' => 'session',
            'provider' => 'admins',
        ]
    ],
```

◎providerはmodelを組み込んでいるため、modelが異なると区分する必要がある。

```php5:config\auth.php
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],
//+これがguardではなく、上のadmin-webがguardになる。
        'admins' => [
            'driver' => 'eloquent',
            'model' => App\Models\Admin::class,
        ],
    ],
```

◎パスワードリセットの設定もプロバイダーとセットになっているため、
　　　　　　　　　　　　　　　　　　　　　　　　区分する必要がある

```php5:config\auth.php
    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],
//+ return Password::broker('admins');の時の名前
        'admins' => [
            //消去するプロバイダー
            'provider' => 'admins',
            'table' => 'password_resets',//テーブルを分ける場合はここを修正 'admin_password_resets'とかにする。
            //リセットパスワードメールの有効時間(分)
            'expire' => 60,//一時間
            'throttle' => 60,
        ],
    ],
```

感覚的にはプロバイダーのadminがguardだが、
実際はその上のガードドライバ(session)を含むadmin-webがguardになる。

## Adminモデルとadminsテーブルを作成する
```:$
php artisan make:model Admin -mf
```

```php5:database\migrations\2022_02_04_100720_create_admins_table.php
//今回は機能をそのままコピペするため、usersテーブルをそのままコピペ。
    public function up()
    {
        Schema::create('admins', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }
```
```c:adminsテーブルを作成
php artisan migrate
```
認証系のAdminモデルはAuthenticatableを必ず継承して作成する。

```php5:app\Models\Admin.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;

//+
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

// class Admin extends Model
class Admin extends Authenticatable implements MustVerifyEmail
{
//+
    use HasApiTokens, HasFactory, Notifiable;
    protected $fillable = [
        'name',
        'email',
        'password',
    ];
    protected $hidden = [
        'password',
        'remember_token',
    ];
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

}
```
## 修正５⃣ メールのviewsと宛先の変更
Authenticatableを継承して作成するため、メールの返信先のリンクを変更する必要がある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/7c860e2d-d506-e4fa-ee4f-61162fa39704.png)

modelから送るメールを修正する。
返信先のアドレスをadminに変更する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/04bb2e34-81aa-e1a0-e03a-54ebea3e884b.png)

1. パスワードのリセットメールの修正
1. 検証メールの修正

### 1.パスワードのリセットメールの宛先を修正する
- 送り先を adminsテーブルのユーザーに修正するため
```c:
php artisan make:notification ResetPasswordAdminNotification
```

```php5:
app\Models\Admin.php
use App\Notifications\ResetPasswordAdminNotification;
---------------------
//リセットパスワードを送るメソッド
    public function sendPasswordResetNotification($token)
    {
   //作成したクラスに変更する 引数はtokenと返信先のルート(url)とresetPasswordConfig
        $this->notify(new ResetPasswordAdminNotification($token, 'admin.password.reset', 'admins'));
    }
```
ResetPasswordAdminNotificationを編集する。

```php5:
<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Auth\Notifications\ResetPassword;


// class ResetPasswordAdminNotification extends Notification
class ResetPasswordAdminNotification  extends ResetPassword //修正
{
    use Queueable;

    public $resetPasswordRoute;
    public $resetPasswordConfig;

    public function __construct($token, $resetPasswordRoute = null, $resetPasswordConfig = null)
    {
        parent::__construct($token);
        $this->resetPasswordRoute = $resetPasswordRoute;
        $this->resetPasswordConfig = $resetPasswordConfig;
    }

    //$notifiableはモデルが入っている。
    public function toMail($notifiable)
    {

        if (static::$toMailCallback) {
            return call_user_func(static::$toMailCallback, $notifiable, $this->token);
        }

        if (static::$createUrlCallback) {
            $url = call_user_func(static::$createUrlCallback, $notifiable, $this->token);
        } else {

            //これが返信用のアドレス。引数でルート名'admin.password.reset'を渡している。
            $url = route($this->resetPasswordRoute,[
                'token' => $this->token,
                'email' => $notifiable->getEmailForPasswordReset(),
            ]);
        }

        //メールの内容
        return (new MailMessage)
            ->subject('主題:パスワードリセットについてや')
            ->line('リクエスト要求があったらからメールをおくった')
            //返信用URLのactionボタン
            ->action('パスワード変更サイトへGO', $url)
            ->line("このパスワードの有効期限は".config('auth.passwords.admins.expire')."期間です")
            ->line('見覚えがなかったら無視してくれ。');
    }
}
```
### 2.検証メールの宛先の修正

```
php artisan make:notification VerifyEmailNotification
```
```php5:app\Models\Admin.php
//+
use App\Notifications\VerifyEmailNotification;

----

//+
    public function sendEmailVerificationNotification()
    {
// 引数は 返信用のリンクの Uri
        $this->notify(new VerifyEmailNotification('admin.verification.verify'));
    }
```
```php5:
<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\URL;
use Illuminate\Auth\Notifications\VerifyEmail;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Config;
use Illuminate\Notifications\Messages\MailMessage;

// class VerifyEmailNotification extends Notification
class VerifyEmailNotification extends VerifyEmail //修正
{
    use Queueable;

    protected $verifyEmailRoute; //追記

    //引数でルートを渡している。$this->notify(new XXX($token, 'admin.password.reset', 'admins'))
    public function __construct(/*追記*/$verifyEmailRoute/*='admin.verification.verify'*/)
    {
        $this->verifyEmailRoute = $verifyEmailRoute; //追記
    }

    protected function verificationUrl($notifiable)
    {
// 時間制限付きのリンク の作成
        return URL::temporarySignedRoute(
            $this->verifyEmailRoute,
            Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
            [
                'id' => $notifiable->getKey(),
                'hash' => sha1($notifiable->getEmailForVerification()),
            ]
        );
    }
// 引数の$url には $this->verifyEmailRoute が 渡る。
    protected function buildMailMessage($url)
    {
        return (new MailMessage)
            ->subject('メールアドレスの確認')
            ->line('以下のボタンをクリックして、メールアドレスの確認をしてください。')
            ->action('メールアドレスの確認', $url)
            ->line('アカウントを作成されていない場合は、これ以上の操作は必要ありません。');
    }
}
```
# Admin authのルートを作成する。
- 修正は不要 - 修正２⃣ に該当する
```php5:routes\web.php
//uri,コントローラー,nameの重複をgroupで解消
Route::group(['prefix' => '/admin', 'namespace' => 'Admin', 'as' => 'admin.'], function () {

    Auth::routes(['verify' => true]);

});
```
# Admin authのコントローラーとviewsを作成する

Admin authのControllerを作成する

 - AuthディレクトリをAdminディレクトリの下にコピー

```:windows
xcopy app\Http\Controllers\Auth app\Http\Controllers\Admin\Auth/e/i
```
 - homeコントローラーをAdminディレクトリの下にコピー

```:windows
copy app\Http\Controllers\HomeController.php app\Http\Controllers\Admin\HomeController.php
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/59087d0d-e2dd-95e7-08c9-e958f369c7c2.png)


Admin authのviewsを作成する

 - authディレクトリをAdminディレクトリの下にコピー

```:windows
xcopy resources\views\auth resources\views\admin\auth/e/i
```
 - home.blade.phpをAdminディレクトリの下にコピー

```:windows
copy resources\views\home.blade.php resources\views\admin
```
 - welcome.blade.phpをAdminディレクトリの下にコピー

```:windows
copy resources\views\welcome.blade.php resources\views\admin
```
 - app.blade.phpのファイルをコピーしてファイル名をadmin_app.blade.phpに変更する

```:windows
copy resources\views\layouts\app.blade.php resources\views\layouts\admin_app.blade.php
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/9ac863ac-4c0e-5398-c6e7-606a2de9a1a0.png)

# Admin authのコントローラーとviewsのリンクの宛先を修正する。

Admin Authコントローラーを修正する。

 1. コントローラーの階層を変更したため、ネームスペースを変更する。
 1. リダイレクト先をroute('admin.home')に変更する
 1. veiwsをauthからAdmin/authに変更
 1. guradをwebからweb-adminに変更
 1. providerをusersからadminsに変更
 1. passwordsをusersからadminsに変更       
 1. middlewareのリダイレクト先をAdmin/authへ変更。

1.階層を変更したため、ネームスペースを変更する。

```php5:Admin/Auth配下の全コントローラーファイル
// namespace App\Http\Controllers\Auth;
namespace App\Http\Controllers\Admin\Auth;
```
```php5:app\Http\Controllers\Admin\HomeController.php
// namespace App\Http\Controllers;
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller; //追記 
```
2.リダイレクト先をAdmin/authに変更する

```php5:Admin/Auth配下の全コントローラー
 protected $redirectTo = RouteServiceProvider::ADMIN_HOME;
```
RouteServiceProviderにADMIN_HOMEを追加

```php5:app\Providers\RouteServiceProvider.php
    //ついでに追加 laravel8対策　コントローラーをフル表記しなくてすむ。
    protected $namespace = 'App\Http\Controllers'; //追記

    public const HOME = '/home';
    public const ADMIN_HOME = '/admin/home';//追記
```
3.veiwsをauthからAdmin/authに変更
Admin/Auth/Controllerの`return view`を`return view('auth.*')`から 
　　　　　　　　　　　　　　　　　　　　`return veiw('admin.auth.*')`に変更する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/5c1e674b-5932-760c-4f92-1d75f7dea02f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/1b72e9e2-a7ec-7912-b7af-42f30806f55f.png)
passwordの再確認を行うviewをAdmin/auth/＊に変更する
password/confirm.blade.php

```php5:app\Http\Controllers\Admin\Auth\ConfirmPasswordController.php

    public function showConfirmForm()
    {
        return view('admin.auth.passwords.confirm');
    }
```
リセットパスワードのemailの再入力画面をAdmin/auth/＊に変更する。
password/email.blade.php

```php5:app\Http\Controllers\Admin\Auth\ForgotPasswordController.php


    public function showLinkRequestForm()
    {
        return view('admin.auth.passwords.email');
    }
```
ログインフォームとログアウト後の遷移先ををAdmin/auth/＊に変更する。
login.blade.php

```php5:app\Http\Controllers\Admin\Auth\LoginController.php
use Illuminate\Http\Request;//ファザードではない。
use Illuminate\Http\Response;


    //adminのログインフォームを表示
    public function showLoginForm()
    {
        return view('admin.auth.login');
    }

    //adminサイトを表示させる。ログアウトしたらurl('/admin')へリダイレクト
    protected function loggedOut(Request $request)
    {
        // $request->wantsJson()は、ヘッダーX-Requested-Withを必要とせずにaxiosクエリをチェックします。
        //($requext->ajax())
        return $request->wantsJson()
            ? new Response('', 204)
            : redirect('/admin');
    }
```
レジスター登録画面をAdmin/auth/＊に変更する。
register.blade.php

```php5:app\Http\Controllers\Admin\Auth\RegisterController.php
    // return veiwを修正
    public function showRegistrationForm()
    {
        return view('admin.auth.register');
    }
```
リセットパスワードの返信先の画面をAdmin/auth/＊に変更する。
password/reset.blade.php

```php5:app\Http\Controllers\Admin\Auth\ResetPasswordController.php
use Illuminate\Http\Request;

------
    //追記 return veiwの変更
    public function showResetForm(Request $request, $token = null)
    {
        return view('admin.auth.passwords.reset')->with(
            ['token' => $token, 'email' => $request->email]
        );
    }
```
検証メールの確認を求める画面をAdmin/auth/＊に変更する。
verify.blade.php

```php5:app\Http\Controllers\Admin\Auth\VerificationController.php
use Illuminate\Http\Request;

------------

    //追記 return viewの変更
    public function show(Request $request)
    {
        return $request->user('admin-web')->hasVerifiedEmail()
                        ? redirect($this->redirectPath())
                        : view('admin.auth.verify');
    }
```
ホーム画面をAdmin/auth/＊に変更する。
home.blade.php

```php5:app\Http\Controllers\Admin\HomeController.php
    public function index()
    {
        return view('admin.home');//修正
    }
```
# guradをwebからweb-adminに変更
1. providerをusersからadminsに変更
1. passwordsをusersからadminsに変更
Admin/Auth/Controller
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/a0b6914d-18cc-e28f-9d97-1497fd098cd4.png)
web認証からadmin-web認証へ変更する

認証動作を伴うコントローラー

- LoginController.php
- RegisterController.php
- ResetPasswordController.php

providerをusersからadminsに変更する

テーブルに登録動作を行うコントローラー

- RegisterController.php

passwordsをusersからadminsに変更する
パスワードリセットを伴うコントローラー

- ForgotPasswordController.php
- ResetPasswordController.php

```php5:app\Http\Controllers\Admin\Auth\LoginController.php
use Illuminate\Support\Facades\Auth;

---------
    //認証(ガード)をwebからadmin-webに変更してやる。
    protected function guard()
    {
        return Auth::guard('admin-web');
    }
```
```php5:app\Http\Controllers\Admin\Auth\RegisterController.php
//+
use App\Models\Admin;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;

--------------

    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => ['required', 'string', 'max:255'],
            // 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
//ユニークの参照先テーブルを変更
            'email' => ['required', 'string', 'email', 'max:255', 'unique:admins'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
        ]);
    }
 //登録先をAdminへ変更
    protected function create(array $data)
    {
        return Admin::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
            'api_token' => Str::random(80),
        ]);
    }

//認証はadmin-webに変更
    protected function guard()
    {
        return Auth::guard('admin-web');
    }
```
```php5:app\Http\Controllers\Admin\Auth\ResetPasswordController.php
//+
use Illuminate\Support\Facades\Auth;
------------

//認証はadmin-webに変更
    protected function guard()
    {
        return Auth::guard('admin-web');
    }

//パスワードリセットのpasswordsをusersからadminsに変更
//Adminモデルに変更される。
    public function broker()
    {
        return Password::broker('admins');
    }
```
```php5:app\Http\Controllers\Admin\Auth\ForgotPasswordController.php
use Illuminate\Support\Facades\Password;

--------
//パスワードリセットのpasswordsをusersからadminsに変更
    public function broker()
    {
        return Password::broker('admins');
    }
```
# middlwareをwebからadmin-webへ変更する。
defaultのwebは省略されている。

- middleware('auth')
   - 未ログイン中ににアクセスするとlogin画面にリダイレクトさせる。
- middleware('guest')
   - ログイン中にアクセスするとhome画面にリダイレクトさせる。

```php5:middleware('auth')がはられたコントローラー
- ConfirmPasswordController.php
- VerificationController.php
- HomeController.php//忘れずに

         //$this->middleware('auth');
          $this->middleware('auth:admin-web');
```
```php5:middleware('guest')がはられたコントローラー
- LoginController.php
        // $this->middleware('guest')->except('logout');
        $this->middleware('guest:admin-web')->except('logout');

- RegisterController.php
        // $this->middleware('guest');
        $this->middleware('guest:admin-web');
```
## middlewareのリダイレクト先を修正する
- middlewareの設定はApp\Http\Kernel.phpに記述されている.
```php5:app\Http\Kernel.php
    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    ];
```
よって 
- `middleware('auth')` は
    `app\Http\Middleware\Authenticate.php` を修正する
- `middleware('guest')` は
    `app\Http\Middleware\RedirectIfAuthenticated.php` を修正する

`middleware('auth')` を修正する

```php5:app\Http\Middleware\Authenticate.php
<?php

namespace App\Http\Middleware;

use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Auth\AuthenticationException;


class Authenticate extends Middleware
{

    //下で追加した引数を追加。
    //引数はmiddleware('auth:追記1,追記2,追記3,・・・')といくらでも追加できる。
    //protected function redirectTo($request)
    protected function redirectTo($request, $guards=null)
    {
        if (!$request->expectsJson()) {
            $guards = empty($guards) ? [null] : $guards;

            foreach ($guards as $guard) {
                if ($guard == 'admin-web') {
// リダイレクトするルートを返してやる。
                    return route('admin.login');
                }
            }
            return route('login');
        }
    }

//+
    function unauthenticated($request, array $guards)
    {
// 未認証なら スローされる。
        throw new AuthenticationException(
// 修正
// 'Unauthenticated.', $guards, $this->redirectTo($request)
            'Unauthenticated.',
            $guards,
//$this->redirectTo()関数の引数に array $gurdsを追記してやる。
            $this->redirectTo($request, $guards)
        );
    }
}
```

2.`middleware('guest')` を修正する

```php5:app\Http\Middleware\RedirectIfAuthenticated.php
<?php

namespace App\Http\Middleware;

use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class RedirectIfAuthenticated
{
    // $guardはmiddlewareの引数
    //->middleware(['guest:追記1,追記2,追記3']);今回は追記1だけなので、...$guardsから$guardに修正
    public function handle(Request $request, Closure $next, $guard = null/*...$guards*/)
    {
        //1個だけなので繰り返す必要もない。
        // $guards = empty($guards) ? [null] : $guards;
        // foreach ($guards as $guard) {
        //     if (Auth::guard($guard)->check()) {
        //         return redirect(RouteServiceProvider::HOME);
        //     }
        // }

//もうすでに認証ずみなら、拒否して各々にリダイレクトさせる。
        if (Auth::guard($guard)->check()) {
            switch ($guard) {
                case 'admin-web':
                    return redirect(RouteServiceProvider::ADMIN_HOME);
                default:
                    return redirect(RouteServiceProvider::HOME);
            }
        }
        return $next($request);
    }
}
```
その他、ミドルウェアを修正する。

3.middleware('verified')--検証済みのユーザーだけ許可

```:
php artisan make:middleware EnsureEmailIsVerified
```
```php5:app\Http\Middleware\EnsureEmailIsVerified.php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Support\Facades\Redirect;

class EnsureEmailIsVerified
{
    //middleware(['verified:admin.verification.notice,admin-web'])
    //$redirectToRoute = 'admin.verification.notice', 
    //$guard = 'admin-web'
// $guard の追加
//public function handle($request, Closure $next, $redirectToRoute = null)
    public function handle($request, Closure $next, $redirectToRoute = null, $guard = null)
    {
        // if (! $request->user() ||
        // ($request->user() instanceof MustVerifyEmail &&
        // ! $request->user()->hasVerifiedEmail())) {
        if (
// $guard の追加
            !$request->user($guard) ||
            ($request->user($guard) instanceof MustVerifyEmail &&
                !$request->user($guard)->hasVerifiedEmail())
        ) {

            return $request->expectsJson()
                ? abort(403, 'Your email address is not verified.')
                : Redirect::route($redirectToRoute ?: 'verification.notice');
        }

        return $next($request);
    }
}
```
```php5:app\Http\Kernel.php
protected $routeMiddleware = [
-------------------------------------
    // 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    'verified' => \App\Http\Middleware\EnsureEmailIsVerified::class,
];
```

4.middleware('password.confirm)
引数にリダイレクトルートを設定するだけで修正する必要がない。

```php5:
})->middleware(['auth:admin-web', 'password.confirm:admin.password.confirm']);
```

# Admin authのviewsのリンクの宛先を修正する。
- 修正３⃣にあたる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/f04bb219-3ba7-c6b4-b9b6-99341e0dce57.png)

新たにルートを追加する

```php5:web.php
Route::group(['prefix' => '/admin', 'namespace' => 'Admin', 'as' => 'admin.'], function () {
//+
    Route::get('/', function () {
        return view('admin.welcome');
    })->name('welcome');

    Auth::routes(['verify' => true]);
//+
    Route::get('/home', [App\Http\Controllers\Admin\HomeController::class, 'index'])->name('home');
});
```

```:Admin配下の全bladeで実行
//検索
@extends('layouts.app
//置換
@extends('layouts.admin_app
```

```:Admin配下の全bladeで実行
//検索
route('
//置換
route('admin.
```

```php5:resources\views\Admin\welcome.blade.php
        @if (Route::has('login'))
        
{{-- @if (Route::has('login'))の中を置き換え--}}
            <div class="hidden fixed top-0 right-0 px-6 py-4 sm:block">
                @auth('admin-web')
                    <a href="{{ url('/admin/home') }}" class="text-sm text-gray-700 underline">Home</a>
                @else
                    <a href="{{ route('admin.login') }}" class="text-sm text-gray-700 underline">Log in</a>

                    @if (Route::has('register'))
                        <a href="{{ route('admin.register') }}" class="ml-4 text-sm text-gray-700 underline">Register</a>
                    @endif
                @endauth
                <a href="{{ url('/') }}" class="text-sm text-gray-700 underline" style="margin-left: 15px">User</a>
            </div>

        @endif
```

```:layouts.app_admin.blade.php
//検索
@guest
//置換
@guest('admin-web')
//検索
route('
//置換
route('admin.
```
ついでに、adminのリンクを追記

```php5:resources\views\welcome.blade.php
----- 
 @auth
  省略
 @endauth
//追記
 <a href="{{ url('/admin') }}" class="text-sm text-gray-700 underline" style="margin-left: 15px">Admin</a>
```

# Adminユーザーの機能を確認する。

 1. ログイン機能
 1. パスワードのリセット機能 
 1. Email Verification機能(登録されたメールの確認機能)
     1. メールの実在性の確認
     1. ログインかつメール認証済みのユーザだけを許可する認証ができる。
 1. password confirm機能(再度passwordの再確認を求める)

上記機能を確認するためのルートを作成する

```php5:routes\web.php
//uri,コントローラー,nameの重複を解消
Route::group(['prefix' => '/admin', 'namespace' => 'Admin', 'as' => 'admin.'], function () {
    Route::get('/', function () {
        return view('admin.welcome');
    })->name('welcome');
    Auth::routes(['verify' => true]);
 Route::get('/home', [App\Http\Controllers\Admin\HomeController::class, 'index'])->name('home');

//+
    //検証確認済みしかアクセスできないルート
    Route::get('/verified', function () {
        return 'あなたのメールは検証済みです。';
    })->middleware(['auth:admin-web','verified:admin.verification.notice,admin-web']);

    //パスワードの再確認がいるルート
    Route::get('/confirmed', function () {
        return 'パスワードが確認できました';
    })->middleware(['auth:admin-web', 'password.confirm:admin.password.confirm']);

});
```

route('admin.welcome')にアクセスすると、リンクが作成されている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/a946a390-113a-2163-d323-ad547eda53bc.png)
adminユーザー登録するとデータがadminsテーブルに登録される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/ad48ce87-2530-e079-0ca2-f294fba643c2.png)
ログインしてミドルウェアを確認する。
route('admin.login')にアクセスする。
route('admin.home')に遷移される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/8fab2c19-26af-a656-6e32-b0d35b6af927.png)
ログアウトするとroute('admin.welcome')に遷移される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/94111c60-cad3-ac60-0423-0dca062355aa.png)
ログアウト中にroute('admin.home')へアクセスすると
route('admin.login')に遷移される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/fd0179ea-9eca-6598-2186-cfe6a3f4087c.png)
route('admin.verified')にアクセスする
ログインアウト中ならroute('admin.login')に遷移
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/fd0179ea-9eca-6598-2186-cfe6a3f4087c.png)
ログイン中＋未検証なら。route('admin.email')に遷移される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/eab89154-6046-22be-3495-ea6405f95456.png)
メールにアクセスするとroute('admin.verified')にアクセスできた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/68328749-b9b6-2b3d-c091-1f23d67ca350.png)


middleware('verified:admin.verification.notice,admin-web')の
リダイレクト先を遷移前のルートに変更する。
デフォルトではログイン後route('admin.home');に遷移される。

```php5:app\Http\Middleware\EnsureEmailIsVerified.php
    public function handle($request, Closure $next, $redirectToRoute = null, $guard = null)
    {
        if (
            !$request->user($guard) ||
            ($request->user($guard) instanceof MustVerifyEmail &&
                !$request->user($guard)->hasVerifiedEmail())
        ) {

//+
    //追記 Session::pull('url.intended');コレが使えない(T_T)ためこんなことしてる。
            $path = $request->url();//追記
            session(['verified' => $path]);//追記
            
            return $request->expectsJson()
                ? abort(403, 'Your email address is not verified.')
                : Redirect::route($redirectToRoute ?: 'verification.notice');
        }

        return $next($request);
    }
```
```php5:app\Http\Controllers\Admin\Auth\VerificationController.php
    //ログイン後の遷移先
//+
    public function redirectPath()
    {
        session()->forget('url.intended');
        $path = session()->pull('verified');
        return  $path;
    }
```

webとadmin-webの情報を取得する場合

```php5:
{{ dd(auth('web')->user(),auth('admin-web')->user()) }}

@Auth
```

Laravel メールをキューを使って送信する
---
①Userモデルのpasswordリセット、verificationメール
②Adminモデルのpasswordリセット、verificationメール
それぞれで実装する。
Userモデルのpasswordリセット、verificationメールをキュ～送信する。

envファイルでの設定。

```env
QUEUE_CONNECTION=database
//queue送信時routeの生成で利用するため。
APP_URL=https://localhost/user_admin_auth/public
```

jobsテーブルのマイグレーションファイルを作成

```
php artisan queue:table
```
テーブルを作成する。

```
php artisan migrate
```
1.Userのメール送信をqueu送信する。
verificationメールをqueu送信.
メール機能を拡張する必要があるため、メールを作る

```
php artisan make:notification UserVerifyEmailNotification
```

```php5:app\Notifications\UserVerifyEmailNotification.php
<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Auth\Notifications\VerifyEmail;//追記

class UserVerifyEmailNotification /*<修正>*/extends VerifyEmail implements ShouldQueue /*</修正>*/
{
    use Queueable;

    //以下削除

}
```

```php5:app\Models\User.php
use App\Notifications\UserVerifyEmailNotification;//追記

------------
    //追記
    public function sendEmailVerificationNotification()
    {
        $this->notify(new UserVerifyEmailNotification());
    }
```

passwordリセットをqueu送信する
手順は全く同じ。

```
php artisan make:notification UserResetPasswordEmailNotification
```

```php5:
<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

use Illuminate\Auth\Notifications\ResetPassword;

class UserResetPasswordEmailNotification /*<修正>*/extends ResetPassword implements ShouldQueue/*</修正>*/
{
    use Queueable;
}
```

```php5:app\Models\User.php
    public function sendPasswordResetNotification($token)
    {
        //引数は要,トークンとルート名とpasswords名
        $this->notify(new UserResetPasswordEmailNotification($token, 'password.reset', 'users'));
    }
```

```
php artisan queu:work
```

:::note info
設定を確認する。
:::

[未検証のメールで登録](https://localhost/user_admin_auth/public/register)して、動作が改善されているのを確認して送られてきたメールのリンクをクリックして検証を完了させる。

[パスワードリセット](https://localhost/user_admin_auth/public/password/reset)にメールを送って、動作が改善されているのを確認して送られてきたメールのリンクをクリックしてパスワードをリセットする。

Adminのメールをqueu送信する。

これは既にメールを作成済みなので作成済みメールを修正する。

Adminのverificationメールをqueu送信.

```php5:app\Notifications\VerifyEmailNotification.php
use Illuminate\Contracts\Queue\ShouldQueue;//無ければ追加

class VerifyEmailNotification extends VerifyEmail /*<追記>*/implements ShouldQueue/*</追記>*/ 
{
    use Queueable;//コメントアウトしてたら解除
```

AdminのpasswordResetメールをqueu送信.

```php5:app\Notifications\ResetPasswordNotification.php
use Illuminate\Contracts\Queue\ShouldQueue;//無ければ追加

class ResetPasswordNotification  extends ResetPassword /*<追記>*/implements ShouldQueue/*</追記>*/
{
    use Queueable;//コメントアウトしてたら解除
```
一般ログインと管理ログインを同時にしている状態を防ぐ
---
								
`user` の ログインコントローラに、
```php5:app/Http/Controllers/Auth/LoginController.php													
    public function __construct()								
    {
// web 認証していると logout メソッド以外のメソッドにはアクセスできなくなる。								
        $this->middleware('guest')->except('logout');		

// admin-web認証していると 全てのメソッドへのアクセスを拒否される。				
        $this->middleware('guest:admin-web');								
    }								
```

管理画面のルートとuser画面のルートでファイルを区分
---

 - `RouteServiceProvider.php`でadminルートとuserルートを別々のファイルで管理させる。
     - 一般ユーザー用の`front.php`、
     - 管理画面用の`back.php`


```php5:app/Providers/RouteServiceProvider.php
    public const HOME = '/home';

 //追記
    protected $namespace = 'App\Http\Controllers';

//追記
    public const ADMIN_HOME = '/admin/home';

    public function boot()
    {
        $this->configureRateLimiting();

        $this->routes(function () {
            Route::prefix('api')
                ->middleware('api')
                ->namespace($this->namespace)
                ->group(base_path('routes/api.php'));

//コメントアウト
            // Route::middleware('web')
            //     ->namespace($this->namespace)
            //     ->group(base_path('routes/web.php'));

// フロント画面 routes/front.php
            Route::middleware('web')
            ->namespace($this->namespace . '\Front')
            ->as('front.')
    //filepath+file名
            ->group(base_path('routes/front.php'));

// 管理画面 routes/back.php
           //admin/～
            Route::prefix('admin')
                ->middleware('web')
                //コントローラーのエイリアスを指定
                ->namespace($this->namespace . '\Back')
                ->as('back.')
    //filepath+file名
                ->group(base_path('routes/back.php'));
        });
    }
```
ルートでのコントローラーのエイリアスを省略したい場合は下記のコードを追加するだけでいい。

```
 //追記
    protected $namespace = 'App\Http\Controllers';
```




ログインに必要な項目を変更する
---
- email から name に変更
```php5:app/Http/ Controllers/Auth/LoginController.php
// +
    public function username()
    {
        // return 'email';
        return 'name';
    }
```


ログインに必要な項目を追加する
---
https://tech.arms-soft.co.jp/entry/2020/01/22/090000

## 簡単な方から。
active項目が1である条件を付加している。らしぃ。
リクエストで`active`を送信して`users`テーブルからuserを取得できたら`true`になる。

```php5:laravel/app/Http/Controllers/Auth/LoginController
//credentials()をオーバーライド
protected function credentials(Request $request)
{
    $request->merge(['active' => 1]);
    
    return $request->only($this->username(), 'password', 'active');
}
```
- 解説
  - SessionGuardインスタンスのattemptメソッド
```php5:PHP
// credentialsは['email'=>'xxx','password'=>'xxx','active'=>1]
  public function attempt(array $credentials = [], $remember = false)
  {
      $this->fireAttemptEvent($credentials, $remember);

// ここで DB から (emailとactive の値 が一致する) user を 取得 する。
      $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);

// 取得したuser の パスワードをチェックしてOKだったら
      if ($this->hasValidCredentials($user, $credentials)) {

// ログインして true を返す
          $this->login($user, $remember);

          return true;
      }
// パスワードチェックがfalseだったら false を 返す
      return false;
  }
```

## 複数テーブルを参照する等、少し複雑な認証をしたい場合

その他参照サイト

https://zenn.dev/ad5/articles/48671b32c89897

```php5:

```