LoginSignup
86
81

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-10-07

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%自分で制御できてる感があり、これはこれで安心な感じ。

86
81
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
86
81