@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.
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?
I take it you have added this to app/Providers/EventServiceProvider.php
protected $listen = [
'Illuminate\Auth\Events\Login' => [
'App\Listeners\LogSuccessfulLogin',
],
];
as per
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?
@MARTINBEAN - Hi I am not overriding the controller but as a debug exercise I did override it to manually fire the event after login
@D9705996 - yes added that.
nothing in logs
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 - what version of Laravel are you using?
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 - 5.7
Are you using the standard laravel authentication from something like php artisan make:auth?
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;
}
}
@D9705996 - Yes its out of the box auth apart from the custom guard which implements 2FA see code block
have you tried doing
parent::fireLoginEvent($user, $remember);
in your session guard method at the top
And just to be sure - if you take away your customised guard do the evens work?
@D9705996 - just tried that and yes it does work if I use SessionGuard So it seems I need something else in my custom class
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;
}
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
Thanks for sharing! little weird that we have to manually add the EventDispatcher.
Please or to participate in this conversation.