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

niallgd's avatar

Login Event

I would like to capture a successful user login and update their last logged in date. I have added

'Illuminate\Auth\Events\Login' => [ 'App\Listeners\LogSuccessfulLogin', ],

to the EventServiceProvider class and generated a LogSuccessfulLogin class using artisan

However the event does not get fired unless I manually fire it i.e. adding event(new Login(config('auth.defaults.guard'), $user, '')); to an overridden authenticated function in the LoginController will cause the even to fire but leaving this out and nothing happens. What am I missing?

0 likes
17 replies
martinbean's avatar

@nialldorgan I don’t follow. If you overriding the controller method that fired that method, then you’ll need to remember to manually fire it yourself.

D9705996's avatar

I take it you have added this to app/Providers/EventServiceProvider.php


protected $listen = [
    'Illuminate\Auth\Events\Login' => [
        'App\Listeners\LogSuccessfulLogin',
    ],
];

as per

https://laravel.com/docs/5.7/authentication#events

To be honest your code looks fine so surprised it isnt working. Do you see anything in you rstorage/logs/laravel.log that might explain things?

What version of Laravel are you using?

2 likes
niallgd's avatar

@MARTINBEAN - Hi I am not overriding the controller but as a debug exercise I did override it to manually fire the event after login

niallgd's avatar
class EventServiceProvider extends ServiceProvider
{
    /**
     * The event listener mappings for the application.
     *
     * @var array
     */
    protected $listen = [
        'App\Events\Event' => [
            'App\Listeners\EventListener',
        ],

            'Illuminate\Mail\Events\MessageSending' => [
            'App\Listeners\LogSendingMessage',
        ],
        'Illuminate\Mail\Events\MessageSent' => [
            'App\Listeners\LogSentMessage',
        ],
        'Illuminate\Auth\Events\Login' => [
        'App\Listeners\LogSuccessfulLogin',
        ],
    ];

    /**
     * Register any events for your application.
     *
     * @return void
     */
    public function boot()
    {
        parent::boot();

        //
    }
}

here is EventServiceProvider FYI

niallgd's avatar

I am overriding SessionGuard with a custom guard and it seems that the eventsdispatcher is not being set in the custom class.

 protected function fireLoginEvent($user, $remember = false)
    {
        
        if (isset($this->events)) {
            $this->events->dispatch(new Events\Login(
                $this->name, $user, $remember
            ));
        }
    }

in the above $this-events is null

D9705996's avatar

Are you using the standard laravel authentication from something like php artisan make:auth?

niallgd's avatar

my custom guard

class TwoFAGuard extends SessionGuard
{
    
    //override this to add 2fa verification
    public function hasValidCredentials($user, $credentials)
    {
        $google2fa = new Google2FA();

       
        return ! is_null($user) && $this->provider->validateCredentials($user, $credentials) && $google2fa->verifyKey($user->googlesecret, $credentials['secretKey']) && $user->active == 1; //make sure the user is active
    }

    //override this to stop secretKey being used to try to get the user from the DB
    public function attempt(array $credentials = [], $remember = false)
    {
        $barecredentials = $credentials;
        unset($barecredentials['secretKey']);
             

        $this->fireAttemptEvent($credentials, $remember);

        $this->lastAttempted = $user = $this->provider->retrieveByCredentials($barecredentials);
        
        // If an implementation of UserInterface was returned, we'll ask the provider
        // to validate the user against the given credentials, and if they are in
        // fact valid we'll log the users into the application and return true.
        if ($this->hasValidCredentials($user, $credentials)) {
            $this->login($user, $remember);

            return true;
        }

        // If the authentication attempt fails we will fire an event so that the user
        // may be notified of any suspicious attempts to access their account from
        // an unrecognized user. A developer may listen to this event as needed.
        $this->fireFailedEvent($user, $credentials);

        return false;
    }
}

niallgd's avatar

@D9705996 - Yes its out of the box auth apart from the custom guard which implements 2FA see code block

D9705996's avatar

have you tried doing

parent::fireLoginEvent($user, $remember); 

in your session guard method at the top

D9705996's avatar

And just to be sure - if you take away your customised guard do the evens work?

niallgd's avatar

@D9705996 - just tried that and yes it does work if I use SessionGuard So it seems I need something else in my custom class

D9705996's avatar

you should just be able to fire the event in

if ($this->hasValidCredentials($user, $credentials)) {
  $this->login($user, $remember);
  $this->events->fire(new Events\Authenticated($user)); 
  return true;
}
niallgd's avatar

OK so the solution is as follows:

$config = $this->getConfig($name);

        if (is_null($config)) {
            throw new InvalidArgumentException("Auth guard [{$name}] is not defined.");
        }

        if (isset($this->customCreators[$config['driver']])) {
            return $this->callCustomCreator($name, $config);
        }

        $driverMethod = 'create'.ucfirst($config['driver']).'Driver';
        
        if (method_exists($this, $driverMethod)) {
            return $this->{$driverMethod}($name, $config);
        }

when using a custom driver code diverts to callCustomCreater and bypasses $this->{$driverMethod}($name, $config); This is important as the call to the above sets several properties such as the events dispatcher

So you need to put thes calls back into where you first create your custom guard i.e. AuthServiceProvider boot function as below

public function boot()
    {
        $this->registerPolicies();

        //remove this to go back to standard auth
        Auth::extend('twofaguard', function ($app, $name, array $config) {
            // Return an instance of Illuminate\Contracts\Auth\Guard...
            //dd($app);
            $guard = new TwoFAGuard($name, Auth::createUserProvider($config['provider']), $app['session.store']);

            if (method_exists($guard, 'setCookieJar')) {
                $guard->setCookieJar($app['cookie']);
            }

            if (method_exists($guard, 'setDispatcher')) {
                $guard->setDispatcher($app['events']);
            }

            if (method_exists($guard, 'setRequest')) {
                $guard->setRequest($app->refresh('request', $guard, 'setRequest'));
            }

            return $guard;
        });
    }

then all is well

2 likes
winkbrace's avatar

Thanks for sharing! little weird that we have to manually add the EventDispatcher.

Please or to participate in this conversation.