LaravelではデフォルトでトークンベースのシンプルなAPI認証の機能が備わっていますが、公式マニュアルではLaravel Passportを使用することを強く推奨しています。

While Laravel ships with a simple, token based authentication guard, we strongly recommend you consider using Laravel Passport

本文書では、Laravel Passportを使ったAPIではなく、デフォルトのTokenベースのシンプルなAPIを利用してLaravelでのAPIの設定方法を確認します。また、Laravelの認証機能を理解する上で重要なGuardについても詳細に説明を行っています。

APIの設定についてはできるだけ公式マニュアルに沿って行っています。Laravelのバージョンは5.8を利用しています。

ログインの認証については下記を参考にしてください。

Authenticationの設定内容について

カスタマイズを行いたい時以外は設定変更を行うことはありませんが、認証に関する設定はconfig/auth.phpファイルで行います。

Guardとは

Laravelの認証ではGuardという単語が頻繁に出てきます。GuardによってLaravelのシステムにアクセスしてくるユーザをどうやって識別するのかということを定義します。

例えばconfig/auth.phpの中には、デフォルトでweb guardとapi guardという2つのGuardが設定されています。web guardはSessionを利用してユーザに関する情報を保持することで認証を行い、api guardはToken(トークン)を利用して認証を行います。

web guardはphp artisan make:authによって追加されるメールアドレスとパスワードを使用したログインの認証に利用するGuardです。

driver, providerについて

web, apiに対して下記のようにdriverやproviderを設定する必要があります。webやapiといった名前は決められた変更ができない名前ではなく、変更することも新たにguardを追加することも可能です。


'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'token',
        'provider' => 'users',
        'hash' => false,
    ],
],
multi authentication機能を実装したい場合は新たにGuardを追加して行います。

上記のdriverはどのように認証を行うかを設定する項目でsessionとtokenがデフォルトでサポートされており、sessionはセッションのcookieやstorageを利用して認証、tokenはランダムな英数字から構成された文字列を利用して認証を行います。

冒頭で出てきたLaravel Passportを利用する場合は、apiのdriverはtokenではなくpassportになります。このようにデフォルトでサポートされているもの以外のdriverも設定可能です。

providerはどこから認証に必要な情報を取得するかを設定する項目でどちらもusersが設定されています。

web, apiで指定されているproviderのusersは下記のようにprividersの中で定義されており、driverとmodelの2つの設定項目を持っています。


'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\User::class,
    ],

driverには、databaseとeloquentがサポートされており、databaseは直接データベースから情報を取得する場合に指定し、eloquentはモデルを経由して取得する場合に指定します。

web, apiはどちらもApp¥Userモデルを経由してusersテーブルから認証に必要な情報を取得しますが、apiの場合は新たにtokenの列を追加する必要があります。

Guardのdriverをtokenにした場合は、すべてのリクエストに対してtokenを確認してテーブルに保存されているtokenとその値が一致するのかチェックを行います。

APIを使用するための環境設定

APIを使用するためにはデータベースやusersテーブルが必要です。事前にphp artisan migrateコマンドを使用してusersテーブルが作成できる環境構築まで行ってください。

APIの設定

usersテーブルへの列の追加

php artisan migrate実行後に作成されるusersテーブルに新たにtoken_api列を追加するためにmigrationファイルの作成を行います。


$ php artisan make:migration add_api_token_to_users_table --table=users
Created Migration: 2019_07_27_115730_add_api_token_to_users_table

database¥migrationsフォルダに作成されたmigrationファイルにapi_token列を追加します。


public function up()
{
    Schema::table('users', function (Blueprint $table) {
        $table->string('api_token', 80)->after('password')
                ->unique()
                ->nullable()
                ->default(null);
    });
}

追加するapi_token列はpassword列の後に追加されるようにafterを指定しています。されにユニークでデフォルト値はnull、null値を入力することも可能なnullableも設定しています。

修正が必要な時のため、downメソッドも追加しておきます。


public function down()
{
    Schema::table('users', function (Blueprint $table) {
        $table->dropColumn('api_token');
    });
}
php artisan migrate:rollbackやrefreshを使ってClass ‘Doctrine\DBAL\Driver\PDOSqlite\Driver’ not foundエラーが発生した場合は、composerを使ってdoctrine/dbalパッケージをインストールする必要があります。composer require doctrine/dbal

migrationファイルの更新作業が完了したら、php artisan migrateコマンドを実行して列の追加を行ってください。


 $ php artisan migrate
Migrating: 2019_07_27_115730_add_api_token_to_users_table
Migrated:  2019_07_27_115730_add_api_token_to_users_table (0.01 seconds)
列名をapi_token以外に設定する場合は、auth.phpファイルでstorage_keyオプションを設定する必要があるようですが、変更については動作確認を行っていません。

api_tokenの列に値を挿入できるようにApp¥User.phpの$fillableにapi_tokenを追加します。


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

api_token列への値の挿入

api_tokenのデフォルト値はnullに設定されるため、ユーザ作成後にユーザ毎に個別に設定することが可能です。今回は、ユーザ登録時に実行されるcreateメソッドを変更することでapi_tokenを追加します。

App¥Http¥Controller¥Auth¥RegisterController.phpファイルを開いてcreateファイルを下記のように変更します。


protected function create(array $data)
{
    return User::create([
        'name' => $data['name'],
        'email' => $data['email'],
        'password' => Hash::make($data['password']),
        'api_token' => str_random(60),
    ]);
}

設定が完了したら、ユーザ登録画面を使ってユーザの登録を行ってください。

Register画面はphp artisan make:authを実行していない場合は表示されません。
ユーザ登録画面
ユーザ登録画面

ユーザの登録が完了したら、tinkerを使ってapi_token列に値が入っているか確認を行います。下記のようにapi_tokenにはランダムは英数字の文字列が入っています。


$ php artisan tinker
Psy Shell v0.9.9 (PHP 7.1.23 — cli) by Justin Hileman
>>> $user = App\User::find(1)
=> App\User {#2973
     id: "1",
     name: "John Doe",
     email: "johndoe@example.com",
     email_verified_at: null,
     created_at: "2019-07-27 12:26:30",
     updated_at: "2019-07-27 12:26:30",
     api_token: "eQ5sIys2fhPDFCVFcJSH7El24gxk6SRn4wUAQO5or4KDS7UELipLraG4bRD4",
   }
>>>

api認証では、api_tokenに入っている値を利用します。

Tokenを使ったアクセス

ルーティングファイルのweb.phpとは別にAPI用のルーティングファイルapi.phpがデフォルトで準備されています。api.phpの中身を下記のようになっています。


Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
});
api認証を使用したい場合はmiddlewareでは必ずauth:apiと設定しなければなりません。authのみだとデフォルトのwebが認証に使用されるためです。Guardのデフォルト値はconfig/auth.phpファイルに記述されています。

middlewareを使ってAPIの認証によるアクセス制限を行っています。api.phpに記述された/userにアクセスは、/userではなくその前に/apiをつけて/api/userとします。

開発サーバを使っている場合は、URLがhttp://127.0.0.1:8000になるのでhttp://127.0.0.1:8000/api/userでアクセスを行います。

curlコマンドを使用してアクセスを行ってみます。


$ curl http://127.0.0.1:8000/api/user

api_tokenの情報がつけられていないのでアクセスに失敗し、loginページへのリダイレクトのHTMLが表示されます。


$ curl http://127.0.0.1:8000/api/user
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="refresh" content="0;url=http://127.0.0.1:8000/login" />

        <title>Redirecting to http://127.0.0.1:8000/login</title>
    </head>
    <body>
        Redirecting to <a href="http://127.0.0.1:8000/login">http://127.0.0.1:8000/login</a>.
    </body>

今度は、api_tokenをつけてアクセスを行います。今回は認証に成功して、JSONでユーザ情報が戻されていることを確認することができます。


$ curl http://127.0.0.1:8000/api/user?api_token=eQ5sIys2fhPDFCVFcJSH7El24gxk6SRn4wUAQO5or4KDS7UELipLraG4bRD4
{"id":1,"name":"John Doe","email":"johndoe@example.com","email_verified_at":null,"created_at":"2019-07-27 12:26:30","updated_at":"2019-07-27 12:26:30","api_token":"eQ5sIys2fhPDFCVFcJSH7El24gxk6SRn4wUAQO5or4KDS7UELipLraG4bRD4"}

次にmiddlewareをなくした状態でアクセスを行います。api.phpを開いて下記のように書き換えます。


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

middlewareをなくしたので、api_tokenをつけなくてもユーザ情報が取得できると考えた人も多いかと思いますが、実際は何も表示されません。


$ curl http://127.0.0.1:8000/api/user

api.phpのreturnで$request->user()となっていますが、api_tokenがないためユーザ情報を取得することができないために$request->user()には何も表示されません。これをreturn ‘test’;のように文字列にすると下記のようにtestが表示されます。


 $ curl http://127.0.0.1:8000/api/user
test

curl以外のアクセス方法

今回は簡単なcurlコマンドを使ってアクセスを行いましたが、JavaScriptを利用して/api/userにアクセスして情報を取得したい場合はあります。axiosを利用している場合は下記のように行うことができます。


axios.get('/api/user?api_token=eQ5sIys2fhPDFCVFcJSH7El24gxk6SRn4wUAQO5or4KDS7UELipLraG4bRD4')
  .then(function (response) {
    // handle success
    console.log(response);
  })