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.

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

API AuthenticationAuthLaravelLaravel 5.8Laravel AuthenticationVue Js
Comments (19)
Add Comment
  • Ilnar

    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?

    • Code Briefly

      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.

      • Ilnar

        Thx a lot. I did it! Next lesson is communicatw jwt back-end with vuejs!

  • Mahdi

    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

  • Will

    Shouldn’t the Routes step be before Authentication middleware step? Because otherwise you will see 404 when testing in the browser as you demonstrated.

    • Pankaj Sood

      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.

  • Annu

    Also try the github uploaded repo, but same issue.

    • Pankaj Sood

      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…

  • Anuj Shrestha

    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.

  • ptucky

    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;

  • Gustavo Benites

    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

    • Gustavo Benites

      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’
      }
      }
      },

  • Gustavo Benites

    Perdon estos comentarios son para la parte 2,

  • Sergio Lorente

    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.

    • Sergio Lorente

      Solved!

  • Switch

    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

  • Brigitte

    This is great. I am wondering now, how to log out a user without using the controller…

  • kaziux

    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