LoginSignup
31

More than 3 years have passed since last update.

LaravelでのJWT認証

Last updated at Posted at 2020-05-18

Laravel + tymon/jwt-auth にて認証つきAPIサーバーを構築する

いろいろな情報を参照させて頂き試したが、AccessTokenが切れた状態 & RefreshTokenの期限が切れていない状態でのトークンの更新ができなかったので、少し工夫したところも記載しています。

※Laravel 8.6.0にて、routes/api.phpの書き方が変わっていたので追記しました。

なお、ここで構築した環境は以下にてアクセスできますのでご参照ください。
GitHub: https://github.com/sankosc/apitest-server
サーバー: https://develop.sankosc.co.jp/apitest/

環境

  • PHP 7.4.5
  • Laravel Framework 7.10.3
  • 10.4.11-MariaDB
  • composerとnpmはインストール済み

準備

Laravelのインストール

$ composer create-project laravel/laravel [プロジェクト名] --prefer-dist

データベース作成

  • PhpMyAdminで作成 ( utf8mb4_bin )
  • .env編集 DB_DATABASE=[データベース名]

Laravelの認証機能を有効にする

$ composer require laravel/ui
$ php artisan ui vue --auth
$ npm install
$ npm run dev

マイグレーション

$ php artisan migrate

認証API構築

JWT認証ライブラリインポート

$ composer require tymon/jwt-auth
$ php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
$ php artisan jwt:secret

ユーザーモデル編集

app/User.php
<?php

namespace App;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Authenticatable implements JWTSubject
{
//
// ~~ 省略 ~~ 
//

    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    /**
     * Return a key value array, containing any custom claims to be added to the JWT.
     *
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return [];
    }
}

config/auth.phpを編集

config/auth.php
'defaults' => [
    'guard' => 'api',
    'passwords' => 'users',
],

'guards' => [
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],

route追加

routes/api.php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::post('login', 'Api\AuthController@login');
Route::get('refresh', 'Api\AuthController@refresh');
Route::group(['middleware' => ['jwt.auth']], function () {
    Route::post('logout', 'Api\AuthController@logout');
    Route::get('me', 'Api\AuthController@me');
});

※Larave 8.6.0

routes/api.php
Route::post('login', [App\Http\Controllers\Api\AuthController::class, 'login']);
Route::get('refresh', [App\Http\Controllers\Api\AuthController::class, 'refresh']);
Route::group(['middleware' => ['jwt.auth']], function () {
    Route::post('logout', [App\Http\Controllers\Api\AuthController::class, 'logout']);
    Route::get('me', [App\Http\Controllers\Api\AuthController::class, 'me']);
});

AuthController作成

app/controllers/api/AuthController.php
<?php

namespace App\Http\Controllers\Api;

use Illuminate\Http\Request;
use Illuminate\Auth\Events\Registered;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;

class AuthController extends Controller
{
    /**
     * Create a new AuthController instance.
     *
     * @return void
     */
    public function __construct()
    {
//        $this->middleware('auth:api', ['except' => ['login']]);
    }

    /**
     * Get a JWT via given credentials.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function login()
    {
        $credentials = request(['email', 'password']);

        if (! $token = auth()->attempt($credentials)) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        return $this->respondWithToken($token);
    }

    /**
     * Get the authenticated User.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function me()
    {
        return response()->json(auth()->user());
    }

    /**
     * Log the user out (Invalidate the token).
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function logout()
    {
        auth()->logout();

        return response()->json(['message' => 'Successfully logged out']);
    }

    /**
     * Refresh a token.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function refresh()
    {
        try {
            return $this->respondWithToken(auth()->refresh());
        } catch (\Tymon\JWTAuth\Exceptions\JWTException $e) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }
    }

    /**
     * Get the token array structure.
     *
     * @param  string $token
     *
     * @return \Illuminate\Http\JsonResponse
     */
    protected function respondWithToken($token)
    {
        return response()->json([
            'access_token' => $token,
            'token_type' => 'bearer',
            'expires_in' => auth()->factory()->getTTL() * 60
        ]);
    }
}

確認

Invoke-RestMethod -Uri "http://localhost/api/login" -Method POST -Body "email=sample@sankosc.co.jp&password=sample123"

テスト用のAPI作成

認証なしのget API helloとpost API echoを作成します。

コントローラ作成

app/controllers/api/TestController.php
<?php

namespace App\Http\Controllers\APi;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class TestController extends Controller
{
    public function hello()
    {
        return response()->json([
            'message' => 'hello'
        ]);
    }

    public function echo(Request $request)
    {
        return response()->json([
            'message' => $request->input('message')
        ]);
    }
}

route追加

routes/api.php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::post('login', 'Api\AuthController@login');
Route::get('hello', 'Api\TestController@hello');
Route::post('echo', 'Api\TestController@echo');
Route::get('refresh', 'Api\AuthController@refresh');

Route::group(['middleware' => ['jwt.auth']], function () {
    Route::post('logout', 'Api\AuthController@logout');
    Route::get('me', 'Api\AuthController@me');
});

AccessTokenのRefreshについて

AccessTokenが切れた状態かつ、RefreshTokenの期限が切れていない状態でのトークンの更新する方法がどうしてもわかりませんでした。
jwt.refresh をroutes/api.phpにて設定するように思えるのですがうまく動作しません。
なので、公式のQuick startで紹介されている方法から以下の2点を変更しています。

  1. route/api.phpにてrefreshを認証の外におく
  2. AuthControllerにてrefreshの処理を少し修正
routes/api.php(変更前:refreshが認証の中にいる)
Route::post('login', 'Api\AuthController@login');
Route::get('hello', 'Api\TestController@hello');
Route::post('echo', 'Api\TestController@echo');

Route::group(['middleware' => ['jwt.auth']], function () {
    Route::post('logout', 'Api\AuthController@logout');
    Route::get('refresh', 'Api\AuthController@refresh');
    Route::get('me', 'Api\AuthController@me');
});
routes/api.php(変更後:refreshが認証の外にいる)
Route::post('login', 'Api\AuthController@login');
Route::get('hello', 'Api\TestController@hello');
Route::post('echo', 'Api\TestController@echo');
Route::get('refresh', 'Api\AuthController@refresh');

Route::group(['middleware' => ['jwt.auth']], function () {
    Route::post('logout', 'Api\AuthController@logout');
    Route::get('me', 'Api\AuthController@me');
});

AuthController.refresh修正

app/controllers/api/AuthController.php(変更前)
    public function refresh()
    {
        return $this->respondWithToken(auth()->refresh());
    }

AuthController.refresh修正

app/controllers/api/AuthController.php(変更後)
    public function refresh()
    {
        try {
            return $this->respondWithToken(auth()->refresh());
        } catch (\Tymon\JWTAuth\Exceptions\JWTException $e) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }
    }

こうすることで、完全に認証がない状態だと401を返し、RefreshTokenの期限が切れる前であれば、更新したトークン返すようになります。

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
31