nuxt.jsとlaravel8とmysqlで認証機能付きSPAを作る（laravel編）

# 前提
フロントをnuxt.js、API側をlaravel8、DBにmysqlを使って認証機能付きSPAの環境構築をしていきます。

私の環境は下記の通りです。

OS: windows10
node.js: 14.17.6
php: 8.0.13
laravel: 8.83.8
nuxt: ^2.15.7
mysql: 8.0.13

# 下準備
作業ディレクトリの作成
```
$ mkdir sample-app
$ cd sample-app
```
以降は任意の場所に作成した新たなsample-appディレクトリ内で作業をしていきます。

# laravel8の構築
先ずはlaravel8のインストールから
```
$ composer create-project "laravel/laravel=8.*" sample-api
$ cd sample-api
$ php artisan serve
```
ブラウザでlocalhost:8000と入力して下記画面が返ってこればOK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878891/d62c4962-16c0-2685-2303-034583c4223a.png)

次にlaravelをインストールしてから最初に行う初期設定を済ましておく
```config/app.php
'timezone' => 'Asia/Tokyo', // UTCから変更

    /*
    |--------------------------------------------------------------------------
    | Application Locale Configuration
    |--------------------------------------------------------------------------
    |
    | The application locale determines the default locale that will be used
    | by the translation service provider. You are free to set this value
    | to any of the locales which will be supported by the application.
    |
    */

    'locale' => 'ja', // enから変更

```

```.env
APP_NAME=Laravel
APP_ENV=local
APP_KEY=hogehoge 
APP_DEBUG=true
APP_URL=http://localhost

LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

DB_CONNECTION=mysql 
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=APIで使用するDB名を記入する
DB_USERNAME=APIで使用するmysqlのユーザー名を記入する
DB_PASSWORD=APIで使用するmysqlのパスワードを記入する

BROADCAST_DRIVER=log
CACHE_DRIVER=file
FILESYSTEM_DRIVER=local
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120

MEMCACHED_HOST=127.0.0.1

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"

AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false

PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1

MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"

```

これで初期設定が完了しました。

続いて認証機能の作成をしていきます。
laravelで認証機能を作成する方法はいくつか存在しますが、今回はsanctumを使っていきます。

```
$ composer require laravel/sanctum
$ php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider" 
$ php artisan migrate
```
これで認証機能に必要な機能の追加と必要なテーブルの生成が完了しました。

次にシーダーを利用してデモアカウントを作成しましょう。

```
$ php artisan make:seeder  UserTableSeeder
```

```database/seeders/UsertableSeeder.php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB; //追加する

class UserTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        // デモアカウントを作成
        DB::table('users')->insert([
            'name' => 'test',
            'email' => 'test@test',
            'password' => bcrypt('test'),
        ]);
    }
}
```

```
$ php artisan db:seed --class=UserTableSeeder 
```

これでログイン用のデモアカウントができました。

次はログイン機能の作成です。

```routes/api.php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Auth\RegisterController; // 追加する
use App\Http\Controllers\Auth\LoginController; // 追加する

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

Route::post('/register', [RegisterController::class, 'register']);// ユーザー登録
Route::post('/login', [LoginController::class, 'login']);// ログイン
Route::post('/logout', [LoginController::class, 'logout']);// ログアウト

```

続いてcontrollerを作成していきます。

```
$ php artisan make:controller Auth/RegisterController
$ php artisan make:controller Auth/LoginController
```

```app/Http/Controllers/Auth/RegisterController.php
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Providers\RouteServiceProvider; // 追加する
use App\Models\User; // 追加する
use Illuminate\Http\RedirectResponse; // 追加する
use Illuminate\Support\Facades\Hash; // 追加する
use Illuminate\Support\Facades\Validator; // 追加する
use \Symfony\Component\HttpFoundation\Response; // 追加する

class RegisterController extends Controller
{
    public function register(Request $request)
    {
        //入力バリデーション
        $validator = Validator::make($request->all(),[
            'name' => 'required',
            'email' => 'required|email|unique:users',
            'password' => 'required'
        ]);

        //バリデーションで問題があった際にはエラーを返す
        if ($validator->fails()) {
            return response()->json($validator->messages(), Response::HTTP_UNPROCESSABLE_ENTITY);
        }

        //バリエーションで問題がなかった場合にはユーザを作成する
        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => Hash::make($request->password),
        ]);

        //ユーザの作成が完了するとjsonを返す
        $json = [
            'data' => $user,
            'message' => 'User registration success!',
            'error' => ''
        ];
        return response()->json( $json, Response::HTTP_OK);
    }
}
```

登録処理はこれで完了です。
POSTMAN等を利用してアカウント登録のテストをして成功するか確認してみましょう。

下記のようなjsonが返ってきたら成功です。
```json
{
    "data": {
        "name": "hoge",
        "email": "hoge@hoge",
        "updated_at": "2022-04-19T02:12:36.000000Z",
        "created_at": "2022-04-19T02:12:36.000000Z",
        "id": 2
    },
    "message": "User registration success!",
    "error": ""
}
```

※もしくは自身のDBを確認して新たなアカウントが追加されているかを確認してみましょう。


アカウント登録の処理が完成したら次はログイン処理を作成していきましょう。

```app/Http/Controllers/Auth/LoginController.php
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; // 追加する
use Illuminate\Validation\ValidationException; // 追加する
use App\Models\User; // 追加する
use \Symfony\Component\HttpFoundation\Response; // 追加する

class LoginController extends Controller
{
    public function login(Request $request){
        // バリデーション
        $credentials = $request->validate([
            'email' => 'required|email',
            'password' => 'required'
        ]);

        // ログイン処理
        if(Auth::attempt($credentials)){
            $user = User::whereEmail($request->email)->first(); //トークン作成と取得

            $user->tokens()->delete();
            $token = $user->createToken("login:user{$user->id}")->plainTextToken;
            
            //ログインが成功するとtokenを返す。
            return response()->json(['token' => $token], Response::HTTP_OK);
        }
        // ログイン失敗時の処理
        return response()->json('Can Not Login.', Response::HTTP_INTERNAL_SERVER_ERROR);
    }

    public function logout(Request $request){
        auth('sanctum')->user()->tokens()->delete();
        return response(['message' => 'You have been successfully logged out.'], 200);
    }

    public function user(Request $request){
        $user = $request->user();
        return response()->json(compact('user'), 200);
    }
}
```

これでログインの処理は完成です。
ちゃんとログインできるか、POSTMAN等を利用してAPIの動作確認をしていきましょう。
emailとpasswordをbodyに書いて渡します。

今回は先程作成したメールアドレス`test@test`とパスワード`test`を利用してログインしていきます。

尚、ログインのURLは`http://localhost:8000/api/login`です。

下記のようなjsonが返ってきたら成功です。

```json
{
    "token": "1|VVmQ0GlY9pjwQyIev9ou7Octpx9ZMQa9xhhFuhOe"
}
```

これで完成です。

最後にtokenが発行されていないと処理できないrouteを作成しておきます。

```routes/api.php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Auth\RegisterController; // 追加する
use App\Http\Controllers\Auth\LoginController; // 追加する

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

Route::post('/register', [RegisterController::class, 'register']); // ユーザー登録
Route::post('/login', [LoginController::class, 'login']); // ログイン
Route::post('/logout', [LoginController::class, 'logout']); // ログアウト

//ログインしたユーザーのみが/hogeにアクセスできる
Route::middleware('auth:sanctum')->group(function(){
    Route::get('/hoge', function(){
        return 'auth is working';
    });
});
```


最後にAPIとして利用する為に細かな設定をいくつかしないといけません。
あとちょっと。

```config/cors.php
<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Cross-Origin Resource Sharing (CORS) Configuration
    |--------------------------------------------------------------------------
    |
    | Here you may configure your settings for cross-origin resource sharing
    | or "CORS". This determines what cross-origin operations may execute
    | in web browsers. You are free to adjust these settings as needed.
    |
    | To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
    |
    */

    'paths' => ['api/*', 'sanctum/csrf-cookie'],

    'allowed_methods' => ['*'],

    'allowed_origins' => ['*'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true, // 変更する

];

```


```app/Http/Kernel.php

        'api' => [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, // コメントアウトを外す
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

```
↑大体43行目あたり


```app/Http/Middleware/VerifyCsrfToken.php
<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array<int, string>
     */
    protected $except = [
        'api/register', // 追記する
        'api/login' // 追記する
    ];
}
```


以上になります。
本APIで発行したtokenを受け取るnuxt.js側の環境構築は別記事に書いていきます。

お疲れ様でした！

### 次のステップ

https://qiita.com/ryuki1126/items/eb91b9a9d515ca27b4b8