Let's take care of some loose ends before we can start developing the actual functionality of our app.

Route Protection

At the moment even unauthenticated users can access the supposedly protected /dashboard route. We can guard our Vue routes in a similar way routes are guarded in Laravel using middlewares.

First thing you need to know is that we can attach arbitrary data to our routes inside the meta object. We're gonna use it to mark the /dashboard route as protected by the authentication system. Open the routes.js file and edit the /dashboard route like this:

{
    path: '/dashboard',
    component: require('./views/Dashboard.vue'),
    meta: { middlewareAuth: true }
}

Then, we can apply a global beforeEach() method to our router, which lets us to perform any checks and actions before each route is loaded - just what middlewares in Laravel do.

Remove all the code after the routes array and replace it with this:

const router = new VueRouter({
    routes
});

router.beforeEach((to, from, next) => {
    if (to.matched.some(record => record.meta.middlewareAuth)) {                
        if (!auth.check()) {
            next({
                path: '/login',
                query: { redirect: to.fullPath }
            });

            return;
        }
    }

    next();
})

export default router;

This is a slightly modified example from the vue-router documentation. Read https://router.vuejs.org/en/advanced/meta.html to understand this code better. In short, we're just retrieving the matched route and checking if it has meta.requiresAuth set to true or set at all. If it's set to true it means we require the user to be authenticated to access the route and if they're not we're redirecting them to the login page. Reload the page and try clicking on the Dashboard link now - you will be redirected to /login. Other links should work as before.

We should also forbid authenticated user from accessing the /login route. We're hiding the link but nothing stops our users from entering a direct URL manually. I'm sure you can figure it out yourself now. Take some time away from reading and implement that functionality.

Restoring the Authenticated State on Page Load

We're persisting data in the local storage on a successful login, but we're not reading that data from the storage on page load. As a result the authenticated state gets lost on page re-load. Let's fix that.

Open auth.js. We're going to read data from the local storage instead of setting properties to null in the constructor.

constructor () {
    this.token = window.localStorage.getItem('token');

    let userData = window.localStorage.getItem('user');
    this.user = userData ? JSON.parse(userData) : null;

    if (this.token) {
        axios.defaults.headers.common['Authorization'] = 'Bearer ' + this.token;
    }
}

Also, in order to access axios inside the constructor, we need to change the module export at the end of the file to this:

export default Auth;

Then let's make the corresponding changes in the app.js file.

import Auth from './auth.js';

window.auth = new Auth();

Alright! Now log in, refresh the page and you'll still be logged in. Although, there could be some fake data stored in the local storage instead. Would be nice to verify this data before we move on.

What I think we should do is create a protected API endpoint which returns a user object of the currently authenticated user. If the token in the request header is invalid, we'll log the user out in our front-end. If it is valid, we'll simply update the user information, just in case it has been changed on the server or corrupted in the storage.

Let's start with the front-end. Alter the constructor of the Auth class and call a new method getUser() if there was a token in the local storage:

if (this.token) {
    axios.defaults.headers.common['Authorization'] = 'Bearer ' + this.token;

    this.getUser();
}

The method will simply make an AJAX call and update the user property after a successful request or log the user out if the response code was 401 "Unauthorized".

getUser() {
    axios.get('/api/get-user')
        .then(({data}) => {
            this.user = data;
        })
        .catch(({response}) => {
            if (response.status === 401) {
                this.logout();
            }
        });
}

Now the back-end. Open routes/api.php and add a new middleware-protected route /api/get-user:

...
Route::middleware('auth:api')->group(function () {
    Route::post('/logout', 'API\AuthController@logout');
    Route::get('/get-user', 'API\AuthController@getUser');
});

As for the controller method, it will be very simple. We're simply going to return the authenticated user.

public function getUser()
{
    return auth()->user();
}

Now log in, manually set the token by running auth.login('token', {}); in the browser console, and then refresh the page. You should be logged out shortly after the page loads.

Making API calls

Congratulations, the authentication part is done. Now you can start developing your app. But before that, let's take care of a problem I mentioned in the pervious article, that is we need to log the user out after each API call that returns 401 (a user could execute auth.login('token', {}); after the initial check on page load). To achieve this we would have to attach this bit to every axios call:

.catch(({response}) => {
    if (response.status === 401) {
        this.logout();
    }
});

Doesn't sound like a great option, right? Instead, let's make an axios wrapper so that it attaches the part above to every axios call for us. We're going to use a JS Promise (look it up if you don't know what that is). Ok, create a new file resources/assets/js/api.js:

class Api {
    constructor () {}
}

export default Api;

Now create a new global Api instance in the app.js file. Nothing new here. Note, that it is important to put window.api = new Api(); before the window.auth = new Auth(); line or you'll be getting an error.

import Api from './api.js';

window.api = new Api();

Add a new call method to the Api class:

call (requestType, url, data = null) {
    return new Promise((resolve, reject) => {
        axios[requestType](url, data)
            .then(response => {
                resolve(response);
            })
            .catch(({response}) => {
                if (response.status === 401) {
                    auth.logout();
                }

                reject(response);
            });
    });
}

It just makes a regular AJAX call using axios, but adds some default logic to the catch method. Now you can use this method for all your API calls instead of using axios directly. Each AJAX call resulting in 401 will log the user out. You still can use the catch method after call as you would with axios, the 401 condition won't be overridden. We can edit the getUser method from the auth.js file and everything should work as before:

getUser() {
    api.call('get', '/api/get-user')
        .then(({data}) => {
            this.user = data;
        });
}

Final Words

We have everything ready to start developing the actual app at this point. You would probably organize your JavaScript & Vue files better in a real project, same could be told about the API or the back-end. I recommend you reading the vue-router documentation to learn more about what you can do with it, we've only covered the essentials in this article series. You might also find Vuex useful, but I'd say that's a more advanced topic.