Search code examples
csrflaravel-6csrf-token

419 CSRF Token Mismatch after logging out and logging back in


After I log out and want to log back in I get the following error:

message: "CSRF token mismatch.", exception: "Symfony\Component\HttpKernel\Exception\HttpException"

I customized some parts of the LoginController so it might have something to do with that. I tried searching in the unmodified LoginController but I can't find anything. My login function from my vuex store:

login({ commit }, user) {
    return new Promise((resolve, reject) => {
        axios({ url: '/login', data: user, method: 'POST' })
            .then(response => {
                const user = response.data.user
                commit('auth_success', user)
                resolve(response)
            })
            .catch(error => {
                commit('auth_error')
                reject(error)
            })
    })
},

My logout function, also from my vuex store:

logout({ commit }) {
    return new Promise((resolve, reject) => {
        axios({ url: '/logout', method: 'POST' })
            .then(() => {
                commit('logout')
                localStorage.removeItem('user');
                resolve()
            })
            .catch(error => {
                reject(error)
            })
    })
},

And this is my LoginController:

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class LoginController extends Controller
{

    use AuthenticatesUsers;

    protected $redirectTo = RouteServiceProvider::HOME;    

    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }

    public function login(Request $request)
    {

        if ($this->attemptLogin($request)) {

            $request->session()->regenerate();

            return $this->authenticated($request, $this->guard()->user());
        }
    }

    protected function attemptLogin(Request $request)
    {
        return $this->guard()->attempt(['email' => $request->json('email'), 'password' => $request->json('password')]);
    }

    protected function authenticated(Request $request, $user)
    {
        if ($user) {
            return response()->json([
                'user' => $user
            ], 200);
        } else {
            return response('', 400);
        }
    }

    public function logout(Request $request)
    {
        $this->guard()->logout();

        if ($request->session()->invalidate()) {
            return response('', 200);
        }
    }
}

Am I missing something?

Update: If I do a page refresh, I don't get the error anymore. This has probably something to do with that I have a Single Page Application using vue-router. So I guess I need a page refresh to avoid the 419 error. But this is not an ideal situation. Does someone has experience with this?


Solution

  • In a SAP, your browser still holds all of your JS variables that you do not specifically clear. It sounds like when you log out of your SAP you are generating a new CSRF token, but not clearing your JS variables. The CSRF protection then checks to see if you already have a CSRF token, when it finds it, and sees a mismatch, it assumes you are attempting a CSRF attack and blocks you. In general, it is always best to force a page reload on a logout to clear your JS variables since not doing this risks the next user being able to see all sorts of other should be private data you may have forgotten to clear.

    Responding with with the following JS at the end your log out procedure will trigger a refresh on the user's browser automatically.

    // Reloads the current URL.
    location.reload();
    

    If you absolutely don't want to do a page reload, you will need to clear all of your local data, not just the 'user' object. The best way to do this that I know of is as follows:

    // Clear all JS variables for the current domain.
    localStorage.clear();
    sessionStorage.clear();
    

    On a logout, I personally prefer doing a reload because it also prompts the cache to revalidate non-JS local data as well which can prevent certain other kinds of leeks like showing the new user images from the last user's session.