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

johncarmackfan95's avatar

Telling the user their account is locked.

I have been trying to add a requirement to my LoginController that a user must be 'active' to log in. I know there are plenty of projects and scenarios where this is needed, but I can't find some definite answers on how Laravel wants this implemented.

In the documentation it says this:

  • If you wish, you may also add extra conditions to the authentication query in addition to the user's e-mail and password. For example, we may verify that user is marked as "active": *
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
    // The user is active, not suspended, and exists.
}

This is great, and certainly helpful. But the message that comes back is:

'failed' => 'These credentials do not match our records.'

This isn't accurate, because the credentials are correct. I could change it to say "These credentials do not match our records, or your account is disabled" but then the user wouldn't know which is which.

What I really want is the user to know that the authentication failed because they are disabled.

I have tried overriding sendFailedLoginResponse() to include the active field in the ValidationException like this:

protected function sendFailedLoginResponse(Request $request)
    {
        throw ValidationException::withMessages([
        	$this->username() => [trans('auth.failed')],
		'active' => [trans('auth.inactive_account')]
        ]);
    }

lang/en/auth.php:

'failed' => 'These credentials do not match our records.',
    'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
	'inactive_account' => 'This account is no longer active.'

attemptLogin():

protected function attemptLogin(Request $request)
    {
        return $this->guard()->attempt(
            array_merge(
			$this->credentials($request),
			['active' => 1]
		),
		$request->filled('remember')
        );
    }

But now what happens is, even when the credentials are wrong, it always says the 'inactive_account' message.

Hoping to get some help here. I have seen solutions where the account is queried first, and then redirected back if the 'active' column is false. I feel this breaks the proper workflow of the AuthenticatesUsers trait.

0 likes
6 replies
Sti3bas's avatar
Sti3bas
Best Answer
Level 53
  • Create event listener php artisan make:listener ValidateUserActive.

  • Add logic:

namespace App\Listeners;

use Illuminate\Auth\Events\Validated;
use Illuminate\Validation\ValidationException;

class ValidateUserActive
{
    public function handle(Validated $event)
    {
        if ($event->user->active) {
            return;
        }

        throw ValidationException::withMessages([
            'email' => ['This account is no longer active.'],
        ]);
    }
}
  • Register it in EventServiceProvider:
use Illuminate\Auth\Events\Validated;
use App\Listeners\ValidateUserActive;

/...

protected $listen = [
    //...
    Validated::class => [
        ValidateUserActive::class
    ]
];

https://github.com/laravel/framework/blob/7.x/src/Illuminate/Auth/SessionGuard.php#L362

https://github.com/laravel/framework/blob/7.x/src/Illuminate/Auth/SessionGuard.php#L388

https://github.com/laravel/framework/blob/7.x/src/Illuminate/Auth/SessionGuard.php#L640

Snapey's avatar

My approach would be.

Write a new method authenticatedin your LoginController. This gets called after a user's credentials have been verified and the user logged in. Then check the active tag and if not active, log the user out again.

public function authenticated(Request $request, User $user)
{
	if($user->active == false) {
		Auth::logout();
		
		return redirect('login')->with('status', 'Your account is not enabled');
	}

	return redirect($this->redirectTo);
}

remove the check from your login

johncarmackfan95's avatar

I forgot to mention I am running Laravel 6, not 7. So the answer from @sti3bas does not work, though I like it a lot. Is there a benefit to using an Event Listener over the controller's authenticated() function? @snapey's answer works.

click's avatar

There is another solution: Middleware.

The downside of above solutions could be that it is only checked once during login. When a user is logged in already and you deactivate their account nothing will change for them. They can continue to use your application until they logout.

You can add your own middleware to check the status of the user or you can extend the existing Authenticate method.

https://github.com/laravel/laravel/blob/master/app/Http/Middleware/Authenticate.php

https://github.com/illuminate/auth/blob/master/Middleware/Authenticate.php

Add this to your Authenticate.php middleware file in your project. Note I wrote this without testing (I don't run L7 yet) but something like this should do what you want.

public function handle($request, Closure $next, ...$guards)
{
        // check if user is still authenticated
        $this->authenticate($request, $guards);

        // user is authenticated at this moment, check if it is still active
        // @todo, not sure if it is `->user()->` or `->user->`
        if (!$this->auth->user()->active) {
              $this->auth->logout();

              // this is the same exception as when you are not authenticated.
              // you could ofcourse throw your own exception and add a flash message or something. 
              throw new AuthenticationException(
                 'Not active!.', $guards, $this->redirectTo($request)
              );
        }
        

        return $next($request);
}

Sti3bas's avatar

@johncarmackfan95 I don't see why it should not work in Laravel 6 as the code of SessionGuard class seems the same.

Is there a benefit to using an Event Listener over the controller's authenticated() function?

The benefit is that validation happens before logging in the user, so you don't need to log him in and then log him out which looks more like a workaround for me.

johncarmackfan95's avatar

Thanks @sti3bas, I realized I hadn't updated laravel in a while, so Illuminate\Auth\Events\Validated did not exist when I first tried your solution. But that class is part of a more recent version of Laravel 6, and it's working without issues.

I do want to kick users off as soon as they are locked as well, so I added a middleware to the web group like @click suggested.

<?php

namespace App\Http\Middleware;

use Auth;
use Closure;
use App\Listeners\ValidateUserActive;
use Illuminate\Auth\AuthenticationException;

class AuthLockout
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next, ...$guards)
    {
	if( Auth::check() )
	{
		/**
		 * Assume any guard type missing an 'active' 
		 *	attribute is always considered active.
		*/
		$active = Auth::user()->active ?? true;

		/**
		 * If inactive, log out and throw AuthenticationException.
		 */
		if( !$active )
		{
			Auth::logout();
			
			throw new AuthenticationException(
                   		'Not active!.', $guards
                	);
		}
	}

        return $next($request);
    }

}

Now my only issue is getting the redirect to function properly in a multi-auth system, because it currently always kicks the user to the admin login instead of the login page for the guard they were logged into.

However this solved my main question so I'll mark it as complete.

Please or to participate in this conversation.