Laravel(5.7)のパスワードリセットや確認メール機能をAPIからキックする


Laravelではmake:authすれば認証関連の基本的な機能が自動生成されます。
が、SPAとかだと、それらをAPI経由で叩く必要があるので、その方法を試してみます。

特に、

* パスワードリセットメールの送信
* 登録時の確認メール（5.7から）

機能について見てみます。

## 方法の検討

どちらの機能も独自のControllerを作成して実装すればよいのですが、ここでは、make:authで生成されるapp/Http/Controllers/Authをapp/Http/Controllers/Apiとしてコピーし利用してみます。

## パスワードリセット

パスワードリセットメールはWebではAuth/ForgotPasswordController.phpのsendResetLinkEmail()にて行われていますので、それをAPIで叩いてやります。ついでに戻り値をjsonにしてやります。

>Validationエラーの場合もHTMLベースの値が帰るのでapp/Exceptions/Handler.phpか[独自のRequestFormを作成](https://qiita.com/zaburo/items/1c6fabbb0dad5b9acafc)して処理すればいいでしょう。


### Api/ForgotPasswordController.php

```diff
<?php

+namespace App\Http\Controllers\Api;

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

+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Password;

class ForgotPasswordController extends Controller
{
    use SendsPasswordResetEmails;

    public function __construct()
    {
        $this->middleware('guest');
    }

+    public function sendResetLinkEmail(Request $request)
+    {
+        $this->validateEmail($request);

+        $response = $this->broker()->sendResetLink(
+            $request->only('email')
+        );

+        return $response == Password::RESET_LINK_SENT
+            ? response()->json(['message' => 'Reset link sent to your email.', 'status' => true], 201)
+            : response()->json(['message' => 'Unable to send reset link', 'status' => false], 401);
+    }
}
```

### routes/api.php

ルートを追加します。そもそもログインできていないのでguestで処理できるようにしておきます。

```diff
Route::post('password/email', 'Api\ForgotPasswordController@sendResetLinkEmail')->name('api.password.email');
```

### 動作確認

http://localhost:8000/api/password/email にemailをPOSTしてやれば（登録済みなら）指定のアドレスにメールが届くはずです。

## 確認メールの送信（というか登録）

登録時に確認メールを送信する場合です。Userに対して確認メール設定を行っているものとします（いちおう方法は後述）。

### Api/RegisterController.php

WebではAuth/Api/RegisterController.phpのregister()でユーザー登録を受けています。
なので、Api/RegisterController.phpのregister()をオーバーライドします。

passwordのvalidationからcomfirmを外しています。

```diff
<?php

namespace App\Http\Controllers\Api;

use App\User;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;

+use Illuminate\Http\Request;
+use Illuminate\Auth\Events\Registered;

class RegisterController extends Controller
{
    use RegistersUsers;

    public function __construct()
    {
        $this->middleware('guest');
    }

    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
+            'password' => ['required', 'string', 'min:6'],
        ]);
    }

    protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
        ]);
    }

+    public function register(Request $request)
+    {
+        $this->validator($request->all())->validate();

+        event(new Registered($user = $this->create($request->all())));

+        return $user ? response()->json(['user' => $user, 'status' => true], 201)
+                     : response()->json(['user' => null, 'status' => false], 401);
+    }
}
```

>各種エラー処理についてはパスワードリセットと同様、独自のFormRequestを作成するなどして対応したほうがいいです。

### Route

ルートを追加します。新規登録なのでguestで処理できるようにしておきます。

```php:api.php
Route::post('register', 'Api\RegisterController@register')->name('api.register');
```

### 動作確認

http://localhost:8000/api/register にname, email, passwordをPOSTして登録できることを確認します。



## 補足

email verify機能の利用方法です。

### User.php

モデルを下記のように変更します。

```diff:User.php
<?php

namespace App;

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

+class User extends Authenticatable implements MustVerifyEmail
{
    use Notifiable;
    use HasApiTokens;

    protected $fillable = [
        'name', 'email', 'password',
    ];

    protected $hidden = [
        'password', 'remember_token',
    ];
}
```

### Route

必要なルートを追加します。SPA等で利用する場合は特に画面遷移などは必要ありません。

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

Route::get('/home', 'HomeController@index')->name('home')->middleware('verified');
```