Laravel 8 Sanctum認証で安全なRest APIを作成

初めての記事ですが、軽くSanctumを使用してAPI作成を紹介していきたいと思います。よろしくお願いします。

●ログイン者のみアクセスできるAPIを作成
●環境：Laravel8


新しいプロジェクトを作成します。

```
 create project laravel/laravel sanctum_test
```

.envファイルの設定を自分の環境と合わせて修正します。

```
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=db_name
DB_USERNAME=root
DB_PASSWORD=
```

Sanctumパッケージをインストール

```
 composer require laravel/sanctum
```
app/Http/Kernel.phpにapiミドルウェア設定を行います。追加されている場合、コメントアウトを外します。

```
 修正前：
　protected $middlewareGroups = [
        'api' => [
            // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            'throttle:api',
        ],
    ];

修正後：
　protected $middlewareGroups = [
        'api' => [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            'throttle:api',
        ],
    ];

```

マイグレートを実行しデフォルトのユーザモデルにSanctumの設定を追加

```
 php artisan migrate
```
```
 <?php

namespace App\Models;

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

//Sanctum
use Laravel\Sanctum\HasApiTokens;

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

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

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

```

app\Http\Controllersにログイン処理を作成

```
 <?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Carbon\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;
use \Symfony\Component\HttpFoundation\Response;
use Illuminate\Foundation\Auth\AuthenticatesUsers;

class LoginController extends Controller{

    public function index(Request $request)
    {
        $email = $request->email;
        $password = $request->password;

        //バリデーション
        $credentials = $this->validator($request);

        if(Auth::attempt($credentials)){
            $user = User::where('email', $email)->first();

            if($user && Hash::check($password, $user->password)){
                //既存トークン削除
                $user->tokens()->delete();

                $user->email_verified_at = new Carbon ('now');
                $user->save();

                $result =  [
                    'token' => $user->createToken("login:user{$user->id}")->plainTextToken
                ];

                return response()->json($result, Response::HTTP_OK);
            }

        }
    }

    /**
      * @param Request $request
      * @return array
      */
    protected function validator(Request $request)
    {
        return $this->validate($request, [
            'email'   => 'required',
            'password' => 'required'
        ]);
    }
}

```

routes/api.phpにルートを設定

```
 Route::post('/login', 'App\Http\Controllers\LoginController@index')->name('login');
```
作成したログインAPIをPostmanでアクセスしてみると!![Screen Shot 2021-12-16 at 18.03.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2384626/c5a5b80c-ba11-7ad7-d294-a53e00692f32.png)
![Screen Shot 2021-12-16 at 18.00.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2384626/d838354c-e5b9-6ec6-c5a5-36a47368cda8.png)


次は上記のトークンでログイン者のみアクセルできるAPIを作成いたします。まず新しいModelを作成

```
 php artisan make:model Article
```
Articleテーブル用のダミーデータを作成してArticleFactory.phpに以下を追加

```
 php artisan make:factory ArticleFactory
```
```
 <?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

class ArticleFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array
     */
    public function definition()
    {
        return [
            'title' => $this->faker->sentence,
            'body' => $this->faker->paragraph,
            'category_id' => rand (1,5)

        ];
    }
}

```

DatabaseSeeder.phpにも追加

```
 <?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use App\Models\Article;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        Article::factory()->count(20)->create();
    }
}

```
ダミーデータSeedを実行します。これでデータの準備が終わりました。作成したArticleテーブルのデータを取得するAPIの作成を進みます。

```
 php artisan db:seed
```
Article取得APIを作成.「--api」オプションをつけたのでCRUDの関数が揃っている状態ですが、
今回はindex関数のみに使います。

```
 php artisan make:Controller ArticleApiController --api
```

ArticleApiControllerに以下の処理を追加

```
 <?php

namespace App\Http\Controllers;

use App\Models\Article;
use Illuminate\Http\Request;

class ArticleApiController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return Article::all();
    }
}
```
またルートを設定

```
 //Sanctum認証
Route::middleware('auth:sanctum')->group(function(){
	　　Route::get('/articles','App\Http\Controllers\ArticleApiController@index'); 
});
```
Postmanの認証設定で発行されたトークンを入れてアクセスするとちゃんと返しています。![Screen Shot 2021-12-17 at 11.49.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2384626/c451f9f5-5466-56cf-4050-374dfc59d9d7.png)


今回違うトークンを使ってみると認証失敗となっています。これで終わりですが、よかったら参考してください。最後まで読んでくれてありがとう！
![Screen Shot 2021-12-17 at 11.49.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2384626/69c515ee-9193-b620-99c6-0e71327e8cfe.png)