Help us understand the problem. What is going on with this article?

Laravel5.7で標準?のAPI認証を試す

More than 1 year has passed since last update.

LaravelのAPIに認証を付ける方法としてはPassportやjwt-auth等の外部ライブラリを利用する方法がありますが、「そもそも標準の認証機能って何よ」ということで試してみます。

標準の認証機能

あまり記述が見当たりませんが、認証用のテーブルにapi_tokenってカラムを足して、その値をHTTPヘッダのAuthorization: Bearer の値として送ってやるのが標準?みたいですね(違ってたらご指摘を)。

Guardで下記のように定義されているやつ。

'api' => [
        'driver' => 'token',
        'provider' => 'users',
    ],

準備と実装

migrationファイルの編集

では、テーブルを作成します。標準で用意されているmigrationファイルにapi_tokenを追加します。
60文字にしていますが、それより短くても長くてもOKなようです。

<?php

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

class CreateUsersTable extends Migration
{

    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
+           $table->string('api_token',60)->unique()->nullable();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}

migrate

migrateします。

php artisan migrate

ユーザーの追加

認証用のユーザー(token)を追加します。
めんどいのでtinkerで追加します。api_tokenには60文字の乱数(乱文字)をセット。

$ php artisan tinker
Psy Shell v0.9.8 (PHP 7.2.7 — cli) by Justin Hileman
>>>
>>>
>>> $user = new App\User;
=> App\User {#2898}
>>> $user->name = 'user1';
=> "user1"
>>> $user->email = 'user1@test.com';
=> "user1@test.com"
>>> $user->password = Hash::make('testtest');
=> "$2y$10$o3Grx73i7PaNV1ptGyukDetQM0FJjxYbXmHdWyNSH0TYK9m2ssf2e"
>>> $user->api_token = str_random(60);
=> "p1zQhxuL2EaNUVUZiMYYPi1iXLWW9R8RLwbMFHjBJmxLgUkkOn1DZApuuobo"
>>> $user->save();
=> true

準備は以上です。サーバを立ち上げておきます。

php artisan serve

動作確認

リクエストしてみる

リクエストもめんどいのでcurlで。
api_tokenに登録した値をAuthorization: Bearer {token}という形で添付して送ります。
APIとしては標準で記述されている認証付きAPIルートである/api/userを利用してみます(リクエストしたユーザの情報を返す)。

API処理としてLaravelに認識させるためにAccept: application/jsonヘッダも追加しています。

curl -H 'Accept: application/json' -H 'Authorization: Bearer p1zQhxuL2EaNUVUZiMYYPi1iXLWW9R8RLwbMFHjBJmxLgUkkOn1DZApuuobo' http://localhost:8000/api/user

レスポンス

あっさりOK。
でも、tokenの内容が見えてるのでいちおう隠します。

{"id":1,"name":"user1","email":"user1@test.com","email_verified_at":null,"created_at":"2018-10-05 22:02:33","updated_at":"2018-10-05 22:02:33","api_token":"p1zQhxuL2EaNUVUZiMYYPi1iXLWW9R8RLwbMFHjBJmxLgUkkOn1DZApuuobo"}

hiddenにapi_tokenを追加

User.phpのhiddenにapi_tokenも追記してやります。

protected $hidden = [
    'password', 'remember_token', 'api_token',
];

見えなくなりました。

{"id":1,"name":"user1","email":"user1@test.com","email_verified_at":null,"created_at":"2018-10-05 22:02:33","updated_at":"2018-10-05 22:02:33"}

認証エラーを発生させてみる

tokenの値を少し変えてリクエストしてみます。
ハンドリングされていないエラーが出ます。標準では認証がNGをだとloginに飛ばされるようになっていますが、「loginなんて無いぞ」と言われています。make:authで必要な設定をしてもエラーは消えますが、APIリクエスト認証時のエラーを追加することにします。

{
    "message": "Route [login] not defined.",
    "exception": "InvalidArgumentException",
    .
    .
    .

とりあえずエラーをでなくする

loginって(名前の)ルートがないぞ!と怒られるので、とりあえずloginって名前のルートを作ります。
めんどいので既にある/にname()で名前つけます。

routes/web.php
Route::get('/', function () {
    return view('welcome');
})->name('login');

それかエラーの原因となっているリダイレクトを消してもいいです。

app/Http/Middleware/Authenticate.php
protected function redirectTo($request)
{
    // return route('login');
}

再度リクエスト

再度(エラーになるよう)リクエストしてみます。API認証エラー時の標準メッセージが出ています。

{"message":"Unauthenticated."}

エラーメッセージを編集する

標準メッセージを変更したい場合はHandle.phpをオーバーライドします。

app/Exceptons/Handle.php
<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;

+use Request;
+use Response;
+use Illuminate\Auth\AuthenticationException;

class Handler extends ExceptionHandler
{

    protected $dontReport = [
        //
    ];


    protected $dontFlash = [
        'password',
        'password_confirmation',
    ];

    public function report(Exception $exception)
    {
        parent::report($exception);
    }

    public function render($request, Exception $exception)
    {
        return parent::render($request, $exception);
    }

    //追加
+   public function unauthenticated($request, AuthenticationException $exception)
+   {
+       if($request->expectsJson()){
+           return response()->json(['error' => 'NG'], 401);
+       }

+       return route('login');
+   }
}

一応確認。

{"error":"NG"}

認証の実装より、エラー処理の方が手間。が、とりあえずは以上です。

Passport等の「おまかせ」系に比べ100%自分で制御できてる感があり、これはこれで安心な感じ。

zaburo
こんにちは。自分用のメモをだらだら公開しています。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした