Laravel(API)で認証機能を作成してみた。

## はじめに
普段はRailsで実装をしている私が、Laravelで認証機能を作成しました。
「LaravelでAPIモードの実装ってどうやるんだ？？」と気になったこともあり、こちらの記事を作成しました。
(ログインのみの実装です)

Laravel初学者の方々への一助になれば幸いです。

**開発環境**
PHP 8.1.27 
Laravel 10.46.0

## 実装
### マイグレーション
説明の関係上、ログインに必要なカラムのみ以下に記載しています。
```database/migrations/2014_10_12_000000_create_users_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('email')->unique();
            $table->string('password');
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('users');
    }
};

```

### モデル
$fillableプロパティは、Eloquentモデルでマスアサインメントを行う際に許可される属性（カラム）を指定するためのプロパティです。
[マスアサインメントとは、一度に複数の属性をまとめてモデルに設定することです。](https://sunablog.tokyo/archives/2366)
```app/Models/User.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

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

    protected $hidden = [
        'password',
    ];

    protected $casts = [
        'password' => 'hashed',
    ];
}

```

### コントローラー
response()->jsonメソッドでJSONを返します。
Attemptは基本的に、ユーザーが提供した資格情報を使用してユーザーのログインを試行します。
資格情報は、下記のコードの$credentialsにあたリます。
認証が成功した場合、attemptメソッドはtrueを返します。それ以外の場合は、falseを返します。
```app/Http/Controllers/Auth/AuthenticatedSessionController.php
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Http\Requests\Auth\LoginRequest;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;

class AuthenticatedSessionController extends Controller
{

    public function store(LoginRequest $request): \Illuminate\Http\JsonResponse
    {
        try {
            # emailとpasswordのキーを持つ連想配列から値を取り出して、credentials変数に代入する
            $credentials = $request->only('email', 'password');
    
            # credentials変数の情報を渡し、Authファサードを介してログイン処理を行う。DB内のユーザーと一致したらattemptでtrue、存在しなかったらfalseを返す。
            if(Auth::attempt($credentials)) {
                # ユーザーを認証する
                $request->authenticate();
    
                # セッションIDの発行
                $request->session()->regenerate();
    
                return response()->json([
                    'status' => 'success',
                    'message' => 'ログインが成功しました'
                ], 200);
            } else {
                return response()->json([
                    'status' => 'error',
                    'message' => 'ログインに失敗しました'
                ], 401);
           }
        } catch (\Illuminate\Auth\AuthenticationException $e) {
            return response()->json([
                'status' => 'error',
                'message' => '予期せぬエラーが発生しました'
            ], 500);
        }
    }

}

```

### ルーティング
storeメソッドを呼び出して、認証を行います。
authミドルウェアを使用することでセッションの有効性を確認し、新しいセッションを作成・更新します。
[（Railsにもミドルウェアの概念があるが、Rackミドルウェアのスタック内で実行されているようです）](https://railsguides.jp/rails_on_rack.html#action-dispatcher%E3%81%AE%E3%83%9F%E3%83%89%E3%83%AB%E3%82%A6%E3%82%A7%E3%82%A2%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF)

```routes/auth.php
<?php

use App\Http\Controllers\Auth\AuthenticatedSessionController;
use Illuminate\Support\Facades\Route;


Route::post('/api/login', [AuthenticatedSessionController::class, 'store'])
                ->middleware('auth');

```

### Seedデータ
ログインするためのユーザーの初期データです。
 Hash::make~のパスワードハッシュ化の処理は手動で実装しています。
```
<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use DateTime;

class UsersTableSeeder extends Seeder
{
    public function run()
    {
        DB::table('users')->insert([
            'name' => 'sample1',
            'email' => 'sample1@example.com',
            'password' => Hash::make('password'),
            'created_at' => new DateTime(),
            'updated_at' => new DateTime()
        ]);
    }
}

```


## 結果
Postmanで動作確認を行いました。
**手順**
1. Bodyタグのform-dataでemailとpasswordを入力する
2. http://localhost/api/login でPOSTリクエストを送信する
3. 「ログインに成功しました」といったメッセージがJSONで表示されれば成功です
4. 違うメールアドレスかパスワードを入力してログインに失敗すればOKです

**ログイン成功**
![316219488-3a63b8ab-7898-485d-bffd-8bf86aad98f3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677000/eb9d79fd-0a14-2998-18c6-f69082705668.png)

**ログイン失敗**
![スクリーンショット 2024-04-05 6.42.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677000/208dcd4a-72e9-db82-298b-2307533dd1c4.png)

## 終わりに
今回は公式ドキュメントなどを見ながら、標準の認証機能を作成しました。
Laravel初学者なのでまだまだ理解が浅いですが、時間がある時にその他の実装方法も勉強していければと思います。

## 参考記事
[Laravel 10.x 認証](https://readouble.com/laravel/10.x/ja/authentication.html)
[Laravel 10.x ハッシュ](https://readouble.com/laravel/10.x/ja/hashing.html)(https://readouble.com/laravel/10.x/ja/authentication.html)
[Laravel 10.x HTTPレスポンス](https://readouble.com/laravel/10.x/ja/responses.html)
[Laravel で認証パラメータを追加する](https://qiita.com/moobay9/items/7a511cbee840ce20ee6d)
[Auth::login() vs Auth::attempt()](https://laracasts.com/discuss/channels/general-discussion/authlogin-vs-authattempt?page=1&replyId=577795)