Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

opheliadesign's avatar

L5 - Properly handle TokenMismatchException when logging in

Clients are complaining about TokenMismatchExceptions when trying to log in. Many use the sites I develop on workstations that always keep the site's login form in view, so this happens very frequently.

The error is thrown here (in /vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php) -

public function handle($request, Closure $next)
    {
        if ($this->isReading($request) || $this->tokensMatch($request))
        {
            return $this->addCookieToResponse($request, $next($request));
        }
 
        throw new TokenMismatchException;
    }

I searched around and could not find a 100% definitive solution to this. I was able to find this bit of code but it does not seem to make any difference: App\Exceptions\Handler.php

public function render($request, Exception $e)
    {
        if ($e instanceof TokenMismatchException){
            //Redirect to login form if session expires
            return redirect($request->fullUrl())->with('errors',"The login form has expired, please try again. In the future, reload the login page if it has been open for several hours.");
        }
        return parent::render($request, $e);
    }

What is the proper way to handle this without displaying an error screen to the user? Is there any way to dynamically refresh the token from the login form to prevent this altogether? Clients do not see this as a security feature, they see it as a bug in my code. I understand the purpose of CSRF protection but it does not translate for them.

Thanks!

0 likes
21 replies
pmall's avatar

Increasing the duration of the session in config/sessions.php ? (default : 120 min)

1 like
opheliadesign's avatar

@pmall thanks for the suggestion, but unfortunately their use case can mean that a workstation sits with the login window open for a long period of time. I'm really hoping to avoid them seeing "a screen that looks like Greek," as they often phrase it.

1 like
pmall's avatar

Put a js to reload the page every hour :)

1 like
robgeorgeuk's avatar

@opheliadesign Thanks for the heads up on this issue, I experienced it today but didn't manage to work out why it happened. As @pmall says I'm sure there would be a JS workaround but, like you, I feel there really should be a more graceful way to handle this issue.

1 like
opheliadesign's avatar

Guess the Javascript reload is the best option.. just feels wrong to me.

I'm already ignoring several routes for CSRF in L5 because otherwise a lot of features will not function, such as external webhooks.

subbe's avatar

@opheliadesign @robgeorgeuk found this on stackoverflow.

Create a custom exception render in the App\Exceptions\Handler class (in the /app/Exceptions/Handler.php file).

 /**
 * Render an exception into an HTTP response.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Exception  $e
 * @return \Illuminate\Http\Response
 */
public function render($request, Exception $e)
{
    if ($e instanceof ModelNotFoundException) {
        $e = new NotFoundHttpException($e->getMessage(), $e);
    }

    if ($e instanceof TokenMismatchException) {

        return redirect(route('login'))->with('message', 'You page session expired. Please try again');
    }

    return parent::render($request, $e);
}
13 likes
shangsunset's avatar

does laravel-caffeine work well with SPA app or ajax? react for example.

ashestree's avatar

I am also using laravel-caffeine, which is great if the computer sits open and connected to the internet. The problem is when you are using on a laptop or something and the laptop gets shut or disconnected from the internet and then you open the laptop expecting to be able to login and cannot without getting a mismatch error. Would really like a better solution that just removing csrf protection completely or forcing them to login twice with an error saying your session expired. To an end user this isn't neat and for security its not ideal to remove csrf completely.

kilrizzy's avatar

I was able to solve this pretty simply essentially using what OP had posted at the beginning:

In: app\Exceptions\Handler.php

Be sure to add TokenMismatchException to the top:

use Illuminate\Session\TokenMismatchException;

Then in your render method:

if ($e instanceof TokenMismatchException){
    return redirect()->back()->withError("The login form has expired, please try again. In the future, reload the login page if it has been open for several hours.");
}

Obviously you would need to change "withError" to however you are currently passing error messages to the user, but this worked fine for my case

4 likes
mrjonleek's avatar

@subbe's suggestion was close but didn't quite nail it for me. The following tweak did:

if ($e instanceof \Illuminate\Session\TokenMismatchException) {
    return redirect('/auth/login')->with('message', 'You session expired. Please try again.');
} 
Snapey's avatar

@mrjonleek just exclude login from csrf protection - it is of no use on that form (it is not protecting anything)

3 likes
Snapey's avatar

@novakkornel

the point about csrf is to stop another page in your browser from posting to your account.

So imagine if you had a page on your site that took input and changed the contents of the database, amended an account rights, placed an order or whatever, then that malicious form could cause some damage.

But, with your login form, what damage can be done if another application tries to write to your login page? As the login page is typically public access then it's no different to just entering gibberish directly to the login form

so, the short answer is; as the login form is public, csrf provides no additional protection.

1 like
novakkornel's avatar

@Snapey Thanks, I was thinking if one tried to break into my page with brute force it would be easier without csrf as he could post credentials to my login route from a different server, check for the response and see if passed.

Snapey's avatar

@novakkornel the standard authentication has rate limiting to protect against brute force attacks

tishma's avatar

@Snapey Great point! CSRF token (as it's name says) is meant to protect authenticated users from making unauthorized requests towards my application. So there really is nothing to protect if user is not logged in yet.

Putting token validation on each and every form is plain wrong.

In case of public forms (not just login), this exception is just getting in the way of the innocent customers.

Public stuff needs to feel nice and smooth to new users. Do I as a developer really want to make my potential customers go through 'Your session has expired' scenario before they haven't even joined? No way! If I've created a session for a reason (e.g. to anonymously track their activity) - it is my problem, not theirs.

I have recently had to deal with tokens on an anonymous search form which does nothing but create doubt in our competence (as a dev team).

Regarding brute force attacks - anyone seriously trying to such attack will simply write the code to load the correct token right before attempting an attack.

Session expiration in the first place is measure taken to free the server resources when users are idle. It might be somewhat helpful to protect users who forget to lock their device while they are away.

1 like
developers's avatar

CSRF on a login form is actually a vital part in protecting your visitors from malicious activity and possibly user information extraction. When you remove the CSRF protection on the login form, an attacker can create a cross site login request for it's victim with valid credentials - credentials the attacker knows since it's their account. When the victim is - within session time limit - navigating to your site, he is immediately logged in. due to the previously executed cross site login request. That is to say: in the attackers account. When the victim then wants to sign up on your website, he'll be ending up on the account page (since the create account/login page is most likely nowhere to be found for already logged in users).

It's harsh to say, but the average website visitor is either ignorant or plain dumb (have seen this to often), thus he probably won't notice he's logged in into an account which is not his. After going to the account page to update it's profile information (or thinking he's going to sign up), the attacker can then log in into his own account, which the victim just updated and by that, has access to account information of the victim.

This might be low prio or even a non-issue in other/your cases. In our case a high level of account information protection is required so the CSRF protection on the login form is not something to get rid of.

*Just wanted to put this attack vector here to give everyone a complete view on the importance of CSRF protection on the login form.

2 likes

Please or to participate in this conversation.