こんにちは、CTOの奥田です。
最近Apple Watchを購入し、アクティビティアプリのリングを完成させたくてわざわざ外出しては色んな所を歩き回っています。
運動不足解消には最適なツールですね。
さて、今回はLaravel8とNuxt.jsを使ってJWT(JSON Web Token)での認証方法をご説明したいと思います。
Table of contents
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ベースでの認証は少し複雑なのでこのようにライブラリを使って簡単にできるのはありがたいですね。
少しでも皆様の参考になりましたら幸いです。