Laravel JWT Authentication – Vue Js SPA (Part 1)
In this article, we will discuss the Laravel JWT Authentication – Vue Js SPA. As you know, we already discuss the same in our previous article. But, we are creating this tutorial with some new amendments. So I’m creating this in the separate setup with latest versions of Laravel and JWT.
I’m considering this you already know Laravel, if you are still not familiar with this, then go and take a look at the Laravel Documentation or my other article on Laravel for more understanding.
Table of Contents
Laravel Application Setup
Use the following composer command to create your application.
composer create-project laravel/laravel laravel_jwt_vuejs --prefer-dist
Add JWT Auth Package
You can use the composer command to add a package in your application. It is one of the easy ways to add any package into the Laravel application.
composer require tymon/jwt-auth:dev-develop // "dev-develop" means still under development
Publish JWT Configuration
Here, we can use an artisan command to publish the JWT package configuration.
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider" // You can also use this command without provider flag, and select provider from the given list.
After executing the above mention command, this will create the configuration at “config/jwt.php”.
Create JWT Secret
You can create the JWT secret using the following artisan command.
php artisan jwt:secret
This command creates a secret code for JWT internal use. You can find this in your “.env” file.
Update Auth Config
Open your “config/auth.php” then update the default guard and API driver.
// Default Guard 'defaults' => [ 'guard' => 'api', 'passwords' => 'users', ], // Driver of the API Guard 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'jwt', 'provider' => 'users', ], ],
Because I’m creating a new directory “Models” for storing all my models. So need to update the provider model for the users.
'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => App\Models\User::class, ], ],
Update User Model
Open user model, it’s provided with the Laravel application. As I already mention about the changes of the custom Model directory, You need to update the following code into your model file.
<?php namespace App\Models; use Illuminate\Notifications\Notifiable; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Foundation\Auth\User as Authenticatable; use Tymon\JWTAuth\Contracts\JWTSubject; class User extends Authenticatable implements JWTSubject { use Notifiable; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'email', 'password', 'role' ]; /** * The attributes that should be hidden for arrays. * * @var array */ protected $hidden = [ 'password', 'remember_token', ]; public function getJWTIdentifier() { return $this->getKey(); } public function getJWTCustomClaims() { return []; } }
Don’t forget to add getJWTIdentifier() and getJWTCustomClaims() methods that are required by the JWT interface.
Update Authentication Middleware
Here, I’m updating my “app/Http/Middleware/Authenticate.php” middleware. I’m overriding the handle and authenticate method and I’m setting out to return the JSON formatted responses. Because this setup will be used in Vue Js application where I need JSON formatted responses and login page will be handled by the Vue Js.
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Auth\Middleware\Authenticate as Middleware; class Authenticate extends Middleware { // Override handle method public function handle($request, Closure $next, ...$guards) { if ($this->authenticate($request, $guards) === 'authentication_failed') { return response()->json(['error'=>'Unauthorized'],400); } return $next($request); } // Override authentication method protected function authenticate($request, array $guards) { if (empty($guards)) { $guards = [null]; } foreach ($guards as $guard) { if ($this->auth->guard($guard)->check()) { return $this->auth->shouldUse($guard); } } return 'authentication_failed'; } }
You have seen the “Route [login] not defined” error when you try to access “http://127.0.0.1:9090/api/v1/user/1” URL. So I’m applying changes in our middleware as mentioned above. After applying those changes when we access the same URL then we receive the error in JSON format.
Laravel Routes
Now, we will create required authentication routes in the “routes/api.php”. You just need to add the following routes:
Route::prefix('v1')->group(function () { Route::prefix('auth')->group(function () { // Below mention routes are public, user can access those without any restriction. // Create New User Route::post('register', 'AuthController@register'); // Login User Route::post('login', 'AuthController@login'); // Refresh the JWT Token Route::get('refresh', 'AuthController@refresh'); // Below mention routes are available only for the authenticated users. Route::middleware('auth:api')->group(function () { // Get user info Route::get('user', 'AuthController@user'); // Logout user from application Route::post('logout', 'AuthController@logout'); }); }); });
Create Auth Controller
php artisan make:controller AuthController
This will create an “AuthController.php” controller at “app/Http/Controllers”.
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Auth; use Validator; use App\Models\User; class AuthController extends Controller { /** * Register a new user */ public function register(Request $request) { $v = Validator::make($request->all(), [ 'name' => 'required|min:3', 'email' => 'required|email|unique:users', 'password' => 'required|min:3|confirmed', ]); if ($v->fails()) { return response()->json([ 'status' => 'error', 'errors' => $v->errors() ], 422); } $user = new User(); $user->name = $request->name; $user->email = $request->email; $user->password = bcrypt($request->password); $user->save(); return response()->json(['status' => 'success'], 200); } /** * Login user and return a token */ public function login(Request $request) { $credentials = $request->only('email', 'password'); if ($token = $this->guard()->attempt($credentials)) { return response()->json(['status' => 'success'], 200)->header('Authorization', $token); } return response()->json(['error' => 'login_error'], 401); } /** * Logout User */ public function logout() { $this->guard()->logout(); return response()->json([ 'status' => 'success', 'msg' => 'Logged out Successfully.' ], 200); } /** * Get authenticated user */ public function user(Request $request) { $user = User::find(Auth::user()->id); return response()->json([ 'status' => 'success', 'data' => $user ]); } /** * Refresh JWT token */ public function refresh() { if ($token = $this->guard()->refresh()) { return response() ->json(['status' => 'successs'], 200) ->header('Authorization', $token); } return response()->json(['error' => 'refresh_token_error'], 401); } /** * Return auth guard */ private function guard() { return Auth::guard(); } }
In this controller, I’m applying the simple logic to make the registration and login process.
The register() method create a new user after validation.
The login() method use the Auth::guard() method that use the JWT. The attempt() method checks the given credentials then after the success, it will generate a token which will be returned in the headers of the response.
The refresh() method regenerate a token if the current token is expired. You can manage the duration in the “config/jwt.php”. And reset the duration as per your application requirement. You just need to add JWT_TTL in your “.env” file. By default, the JWT token is valid for 60 minutes (1 Hour).
JWT_TTL=10
I’m changing this limit to 10 minutes, from now our JWT token is valid only for the 10 minutes.
The “logout()” method simply unset the token.
The “user()” method returns the authenticated user details.
You can test those API in postman.
Conclusion
In this article, We are discussing Laravel JWT Authentication for our Vue Js application. This is the first part of this series. In the next tutorial, we will cover the Vue Js setup and connect this API with Vue Js application. You can get the Github repository link in the 2 part of this series Please feel free to add your comments if any query. You can also send me your feedback 😉
If you like our content, please consider buying us a coffee.
Thank you for your support!
Buy Me a Coffee
Hey, there! That is not work on 2019.04.15. After “Update Authentication Middleware” I do not see “Route [login] not defined”, i am seen “404 Not Found” that is matter? Where is error? How can i decide this?
It’s hard to reproduce the same. Please check at your end each of the step you follow. You can also go with the Github repository.
Thx a lot. I did it! Next lesson is communicatw jwt back-end with vuejs!
Hi thank you for your nice tutorial jwt-auth laravel + vuejs. I follow your tutorial and successfully run my project and can login and direct to admin dashboard but I don’t know when I want open l my sidebar link I can’t collapse sidebar navigation link? this is simple code side bar -> sub-menu
Packages
User
Shouldn’t the Routes step be before Authentication middleware step? Because otherwise you will see 404 when testing in the browser as you demonstrated.
sir i get the route error when submiit the form or login form.
app.js:1014 POST http://127.0.0.1:8000/undefined/api/v1/auth/register 405 (Method Not Allowed)
please help to resolve. Thanks
Hi Annu,
Send me some details, such as Laravel version, and other dependencies version so I can try to reproduce the same.
I’m trying to make a new repository with the latest version. I will update the post when complete.
Also try the github uploaded repo, but same issue.
Hi Annu,
As I test, everything working fine. Just one issue but related to the Reset password functionality.
Use the following code snippet in your AuthController.
use SendsPasswordResetEmails, ResetsPasswords {
SendsPasswordResetEmails::broker insteadof ResetsPasswords;
ResetsPasswords::credentials insteadof SendsPasswordResetEmails;
}
Instead of this.
use ResetsPasswords, SendsPasswordResetEmails {
ResetsPasswords::broker insteadof SendsPasswordResetEmails;
}
Please review your code. Thanks…
Quick question:
How is the token being handled here?
Is the new token being generated on the fly when the user Logs In or we save the generated token in server end?
I am very new to this JWT token authorizations and cannot figure out how does we securely login the users without exposing the token directly to the users browser and preventing another 3rd party script to get access to it.
Great! Thanks I do this article with Laravel.6 it working well
Only AuthController.php
I do not create folder Models to keep all models, so I change only
use App\Models\User; to use App\User;
Hola, excelente tutorial pero encontre algunos errores cuando quise activar el admin user…en el caso mio el role es un caracter y aqui espera un integer , en Login.vue
Donde dice
success: function() {
// handle redirection
app.success = true
const redirectTo = redirect ? redirect.from.name : this.$auth.user().role === -1 ? ‘admin.dashboard’ : ‘dashboard’
deberia decir
success: function() {
// handle redirection
app.success = true
const redirectTo = redirect ? redirect.from.name : this.$auth.user().role === ‘-1’ ? ‘admin.dashboard’ : ‘dashboard’
y en route.js
// ADMIN ROUTES
{
path: ‘/admin’,
name: ‘admin.dashboard’,
component: AdminDashboard,
meta: {
auth: {
roles: ‘2’,
redirect: {
name: ‘login’
},
forbiddenRedirect: ‘/403’
}
}
},
deberia decir
// ADMIN ROUTES
{
path: ‘/admin’,
name: ‘admin.dashboard’,
component: AdminDashboard,
meta: {
auth: {
roles: ‘-1’,
redirect: {
name: ‘login’
},
forbiddenRedirect: ‘/403’
}
}
},
y por supuesto en la base de datos el usuario debe estar registrado con role=-1
Gracias y saludos
Perdon, quise decir
y en route.js
Dice
// ADMIN ROUTES
{
path: ‘/admin’,
name: ‘admin.dashboard’,
component: AdminDashboard,
meta: {
auth: {
roles: -1,
redirect: {
name: ‘login’
},
forbiddenRedirect: ‘/403’
}
}
},
deberia decir
// ADMIN ROUTES
{
path: ‘/admin’,
name: ‘admin.dashboard’,
component: AdminDashboard,
meta: {
auth: {
roles: ‘-1’,
redirect: {
name: ‘login’
},
forbiddenRedirect: ‘/403’
}
}
},
Perdon estos comentarios son para la parte 2,
The register POST method always return “error”: “Unauthorized” … However, the login POST method works fine and return
“error”: “login_error” … I’m using Laravel 7.
Solved!
Hello Author,
I have been having issues trying to implement your solution to my project, but it keeps giving me
Errors:
app.js:21064 [Vue warn]: Error in v-on handler: “TypeError: Cannot read property ‘register’ of undefined”
found in
—> at resources/js/components/Register.vue
at resources/js/views/App.vue
warn @ app.js:21064
logError @ app.js:22323
globalHandleError @ app.js:22318
handleError @ app.js:22278
invokeWithErrorHandling @ app.js:22301
invoker @ app.js:22618
original._wrapper @ app.js:27977
app.js:22327 TypeError: Cannot read property ‘register’ of undefined
at VueComponent.register (app.js:3280)
at submit (app.js:16790)
at invokeWithErrorHandling (app.js:22293)
at HTMLFormElement.invoker (app.js:22618)
at HTMLFormElement.original._wrapper (app.js:27977)
Question 1: Can this solution be implemented on Laravel 6/7, cause I am currently running my application with laravel 7
This is great. I am wondering now, how to log out a user without using the controller…
I have issue 401 error, when after successfull login trying to get https;//localhost:8000/api/auth/user, because in response headers, there isn’t authorization token, although websanova should handle that under the hood