Refreshing authentication tokens for a Vue.js SPA using Laravel for the backend Refreshing authentication tokens for a Vue.js SPA using Laravel for the backend vue.js vue.js

Refreshing authentication tokens for a Vue.js SPA using Laravel for the backend


Considering that you are using an api for authentication, I would suggest using Passport or JWT Authentication to handle authentication tokens.


Finally fixed it!

By returning the UserResource directly in the LoginControllers authenticated method, it is not a valid Laravel Response (but I guess raw JSON data?) so probably things like cookies are not attached. I had to attach a call to response() on the resource and now everything seems to work fine (though I need to do more extensive testing).

So:

protected function authenticated(\Illuminate\Http\Request $request, $user){    ...    return (new UserResource($user))->additional(        ['permissions' => $user->getUIPermissions()]    );}

becomes

protected function authenticated(\Illuminate\Http\Request $request, $user){    ...    return (new UserResource($user))->additional(        ['permissions' => $user->getUIPermissions()]    )->response();  // Add response to Resource}

Hurray for the Laravel docs on attributing a section to this:https://laravel.com/docs/5.5/eloquent-resources#resource-responses

Additionally, the laravel_token is not set by the POST request to login, and the call to refreshCsrfToken() also didn't do the trick, probably because it was protected by the guest middleware.

What worked for me in the end is to perform a dummy call to '/' right after the login function returned (or the promise was fulfilled).

In the end, my login function in the component was as follows:

login(){    // Copy the user object    const data = {...this.user};    // If remember is false, don't send the parameter to the server    if(data.remember === false){        delete data.remember;    }    this.authenticating = true;    this.authenticate(data)        .then( csrf_token => {            window.Laravel.csrfToken = csrf_token;            window.axios.defaults.headers.common['X-CSRF-TOKEN'] = csrf_token;            // Perform a dummy GET request to the site root to obtain the larevel_token cookie            // which is used for authentication. Strangely enough this cookie is not set with the            // POST request to the login function.            axios.get('/')                .then( () => {                    this.authenticating = false;                    this.$router.replace(this.$route.query.redirect || '/');                })                .catch(e => this.showErrorMessage(e.message));        })        .catch( error => {            this.authenticating = false;            if(error.response && [422, 423].includes(error.response.status) ){                this.validationErrors = error.response.data.errors;                this.showErrorMessage(error.response.data.message);            }else{                this.showErrorMessage(error.message);              }        });

and the authenticate() action in my vuex store is as follows:

authenticate({ dispatch }, data){    return new Promise( (resolve, reject) => {        axios.post(LOGIN, data)            .then( response => {                const {csrf_token, ...user} = response.data;                // Set Vuex state                dispatch('setUser', user );                // Store the user data in local storage                Vue.ls.set('user', user );                return resolve(csrf_token);            })            .catch( error => reject(error) );    });},

Because I didn't want to make an extra call to refreshTokens in addition to the dummy call to /, I attached the csrf_token to the response of the /login route of the backend:

protected function authenticated(\Illuminate\Http\Request $request, $user){    $user->last_login = \Carbon\Carbon::now();    $user->timestamps = false;    $user->save();    $user->timestamps = true;    return (new UserResource($user))->additional([        'permissions' => $user->getUIPermissions(),        'csrf_token' => csrf_token()    ])->response();}


You should use Passports CreateFreshApiToken middleware in your web middleware passport consuming-your-api

web => [...,    \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,],

this attaches attach the right csrftoken() to all your Request headers as request_cookies