Blog スタッフブログ

Laravel8とNuxt.jsを使ってJWT-Authを実装する

Category | Blog
Tag | /
/ 1,144views


こんにちは、CTOの奥田です。

最近Apple Watchを購入し、アクティビティアプリのリングを完成させたくてわざわざ外出しては色んな所を歩き回っています。
運動不足解消には最適なツールですね。

さて、今回はLaravel8とNuxt.jsを使ってJWT(JSON Web Token)での認証方法をご説明したいと思います。

Table of contents

  1. Laravel側の準備をする
  2. Controllerの設定をする
  3. nuxtjs/authを追加する
  4. ログイン機能を実装する
  5. さいごに

Laravel側の準備をする

ここではLaravel側でのmake:authやmigration等は完了しているものとしてご説明します。
まず、composerでjwt-authをインストールします。

composer require tymon/jwt-auth

次にjwt-authの初期設定を行います。下記コマンドでconfig/jwt.phpが生成されます。

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

下記コマンドで秘密鍵が生成されます。

php artisan jwt:secret

config/auth.phpのguardsに下記を追記します。

'guards' => [
    'users' => [
        'driver' => 'jwt',
        'provider' => 'users',
        'hash' => false,
    ],
],

下記コマンドでUserモデルを作成してJwt-Authの設定を追加します。

php artisan make:model User
<?php
    namespace App\Models;
    
    use Illuminate\Foundation\Auth\User as Authenticatable;
    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 [];
        }    
    
    }

Controllerの設定をする

次にControllerの設定をしていきます。
まずRouterを追加します。routes/api.phpに下記を追記します。

use App\Http\Controllers\AuthController;
    
Route::group(['middleware' => 'api','prefix' => 'Users'], function ($router) {

    Route::post('/login', [AuthController::class, 'login']);
    Route::post('/logout', [AuthController::class, 'logout']);
    Route::post('/refresh', [AuthController::class, 'refresh']);
    Route::get('/me', [AuthController::class, 'me']);
});

AuthControllerを下記のようにします。

<?php
namespace App\Http\Controllers;

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

use Illuminate\Support\Facades\Auth;
use App\Models\User;
use Validator;
use Illuminate\Http\Exceptions\HttpResponseException;
class AuthController extends Controller
{
    /**
        * Create a new AuthController instance.
        *
        * @return void
        */
    public function __construct() {
        $this->middleware('auth:users', ['except' => ['login','refresh']]);
    }
    /**
    * Get a JWT via given credentials.
    *
    * @return \Illuminate\Http\JsonResponse
    */
    public function login(Request $request){
        $validator = Validator::make($request->all(), [
            'email' => 'required|email',
            'password' => 'required|string|min:6',
        ]);

        if ($validator->fails()) {
            return response()->json($validator->errors(), 422);
        }

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

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

        return response()->json(['message' => 'User successfully signed out']);
    }

    /**
     * Refresh a token.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function refresh() {
        return $this->createNewToken(auth('users')->refresh());
    }

    /**
     * Get the authenticated User.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function me() {
        // return response()->json(auth()->user());
        $user = User::find(Auth::id());
        $user->links = json_decode( $user->links, 1 );
        return response()->json( $user );
    }

    protected function createNewToken($token){
        return response()->json([
            'access_token' => $token,
            'token_type' => 'bearer',
            'expires_in' => auth('users')->factory()->getTTL() * 60
        ]);
    }
}

nuxtjs/authを追加する

それではNuxt.js側の設定を行っていきます。
@nuxtjs/auth-nextを追加します。

yarn add @nuxtjs/auth-next

次にnuxt.config.jsを編集していきます。
modulesに’@nuxtjs/auth-next’を追加します。

modules: [
    ...
    '@nuxtjs/auth-next',
],

axiosのbaseUrlとauthプロパティを追加します。

axios: {
    baseURL:  "path/to/api_base/", // apiのベースURLを追加 
},
auth: {
    redirect: {
        login: '/login', 
        logout: '/login', 
        callback: false,
        home: '/home'
    },
    strategies: {
        User: {
            provider: 'laravel/jwt',
            url: '/Users',
            token: {
                property: 'access_token',
                maxAge: 60 * 60,
            },
            refreshToken: {
                property: 'access_token',
                maxAge: 20160 * 60,
            },
            
            endpoints: {
                login: { url: '/login', method: 'post', propertyName: 'access_token' },
                logout: { url: '/logout', method: 'post' },
                refresh: { url: '/refresh', method: 'post' , propertyName: 'access_token'}, 
                user: { url: '/me', method: 'get', propertyName: false},
            }
        }
    },
},

ログイン機能を実装する

pages/login.vueを作成します。

<template>
    <div id="p-login">
        <div class="p-login__wrapper">

            <div class="p-login__content">
                <div class="p-login__card">
                    <form @submit.prevent="login">
                        <div class="p-login__card--body">
                            <div class="p-login__error" v-if="auth.error">メールアドレスまたはパスワードが違います。</div>
                            <div class="c-form__row p-login__form--group"> 
                                <input type="text" placeholder="メールアドレス" name="email" class="c-form__control p-login__form--control" v-model="auth.email">
                            </div>
                            <div class="c-form__row p-login__form--group"> 
                                <input type="password" placeholder="パスワード" name="password" class="c-form__control p-login__form--control" v-model="auth.password">
                            </div>
                            <div class="c-form__row p-login__form--group p-login__form--submit"> 
                                <button class="c-button__reset c-button__primary c-button__block c-button__submit" v-bind:disabled="processing"><span>ログイン</span></button>
                            </div>
                            <div class="p-login__form--supply">パスワードを忘れた方は<n-link to="/password/forgot" v-bind:disabled="processing">こちら</n-link></div>
                            <div class="c-form__row p-login__form--group p-login__form--register"> 
                                <n-link to="/register" class="btn btn-secondary btn-submit btn-block p-reset__form--btn c-auth__button" v-bind:disabled="processing">新規会員登録</n-link>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</template>

script内を下記のようにします。

<script>
export default {
    middleware: ['auth'],
    layout: 'login',
    head() {
        return {
            title: "ログイン"
        }
    },
    data() {
        return {
            processing :false,
            auth: {
                email : '',
                password : '',
                error :false
            }
        }
    },
    methods: {
        async login() {
            this.auth.error = false
            this.processing = true
            try {
                await this.$auth.loginWith('User', { data: this.auth })
                .then(()=>{
                    this.processing = false
                })
            } catch (err) {
                console.log(err)
                this.auth.error = true
                this.processing = false
            }
        }
    }
}
</script>

認証させたいページにはmiddleware:[‘auth’]を追加することで未認証の場合、loginページへリダイレクトされます。

export default {
    ...
    middleware: ['auth'],
    ...
}

これでJWTでの認証が完了します。
仕組みとしては this.$auth.loginWith() で認証が通るとBearer tokenを取得し、それを元に/Users/meにユーザー情報を取得しに行きます。
また、tokenは1時間で期限が切れるのでrefreshTokenの設定から/Users/refreshにてTokenを再取得しに行きます。

さいごに

いかがだったでしょうか?今回はLaravel8とNuxt.jsをつかったJWT-Authでの認証方法をご説明致しました。
Apiベースでの認証は少し複雑なのでこのようにライブラリを使って簡単にできるのはありがたいですね。
少しでも皆様の参考になりましたら幸いです。

Category | Blog
Tag | /
Author | Mineo Okuda / 1,144views

Company information

〒650-0024
神戸市中央区海岸通5 商船三井ビルディング4F

Contact us

WEBに関するお問い合わせは
078-977-8760 (10:00 - 18:00)