diff --git a/src/JWTGuard.php b/src/JWTGuard.php index 28f477fa6..0a1c6672b 100644 --- a/src/JWTGuard.php +++ b/src/JWTGuard.php @@ -12,9 +12,13 @@ namespace Tymon\JWTAuth; use BadMethodCallException; +use Illuminate\Auth\Events\Attempting; +use Illuminate\Auth\Events\Failed; +use Illuminate\Auth\Events\Validated; use Illuminate\Auth\GuardHelpers; use Illuminate\Contracts\Auth\Guard; use Illuminate\Contracts\Auth\UserProvider; +use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Http\Request; use Illuminate\Support\Traits\Macroable; use Tymon\JWTAuth\Contracts\JWTSubject; @@ -48,20 +52,40 @@ class JWTGuard implements Guard */ protected $request; + /** + * The event dispatcher instance. + * + * @var \Illuminate\Contracts\Events\Dispatcher + */ + protected $events; + + /** + * The name of the Guard. + * + * @var string + */ + protected $name = 'tymon.jwt'; + /** * Instantiate the class. * * @param \Tymon\JWTAuth\JWT $jwt * @param \Illuminate\Contracts\Auth\UserProvider $provider * @param \Illuminate\Http\Request $request + * @param \Illuminate\Contracts\Events\Dispatcher $eventDispatcher * * @return void */ - public function __construct(JWT $jwt, UserProvider $provider, Request $request) - { + public function __construct( + JWT $jwt, + UserProvider $provider, + Request $request, + Dispatcher $eventDispatcher + ) { $this->jwt = $jwt; $this->provider = $provider; $this->request = $request; + $this->events = $eventDispatcher; } /** @@ -123,10 +147,14 @@ public function attempt(array $credentials = [], $login = true) { $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials); + $this->fireAttemptEvent($credentials); + if ($this->hasValidCredentials($user, $credentials)) { return $login ? $this->login($user) : true; } + $this->fireFailedEvent($user, $credentials); + return false; } @@ -387,7 +415,13 @@ public function getLastAttempted() */ protected function hasValidCredentials($user, $credentials) { - return $user !== null && $this->provider->validateCredentials($user, $credentials); + $validated = $user !== null && $this->provider->validateCredentials($user, $credentials); + + if ($validated) { + $this->fireValidatedEvent($user); + } + + return $validated; } /** @@ -422,6 +456,54 @@ protected function requireToken() return $this->jwt; } + /** + * Fire the attempt event. + * + * @param array $credentials + * + * @return void + */ + protected function fireAttemptEvent(array $credentials) + { + $this->events->dispatch(new Attempting( + $this->name, + $credentials, + false + )); + } + + /** + * Fires the validated event. + * + * @param \Illuminate\Contracts\Auth\Authenticatable $user + * + * @return void + */ + protected function fireValidatedEvent($user) + { + $this->events->dispatch(new Validated( + $this->name, + $user + )); + } + + /** + * Fire the failed authentication attempt event. + * + * @param \Illuminate\Contracts\Auth\Authenticatable|null $user + * @param array $credentials + * + * @return void + */ + protected function fireFailedEvent($user, array $credentials) + { + $this->events->dispatch(new Failed( + $this->name, + $user, + $credentials + )); + } + /** * Magically call the JWT instance. * diff --git a/src/Providers/AbstractServiceProvider.php b/src/Providers/AbstractServiceProvider.php index ca312a40e..723555aca 100644 --- a/src/Providers/AbstractServiceProvider.php +++ b/src/Providers/AbstractServiceProvider.php @@ -97,7 +97,8 @@ protected function extendAuthGuard() $guard = new JWTGuard( $app['tymon.jwt'], $app['auth']->createUserProvider($config['provider']), - $app['request'] + $app['request'], + $app['events'] ); $app->refresh('request', $guard, 'setRequest'); diff --git a/tests/JWTGuardTest.php b/tests/JWTGuardTest.php index d15ca5a50..01cd13254 100644 --- a/tests/JWTGuardTest.php +++ b/tests/JWTGuardTest.php @@ -12,6 +12,10 @@ namespace Tymon\JWTAuth\Test; use Illuminate\Auth\EloquentUserProvider; +use Illuminate\Auth\Events\Attempting; +use Illuminate\Auth\Events\Failed; +use Illuminate\Auth\Events\Validated; +use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Http\Request; use Mockery; use Tymon\JWTAuth\Exceptions\JWTException; @@ -39,13 +43,24 @@ class JWTGuardTest extends AbstractTestCase */ protected $guard; + /** + * @var \lluminate\Contracts\Events\Dispatcher|\Mockery\MockInterface + */ + protected $eventDispatcher; + public function setUp(): void { parent::setUp(); $this->jwt = Mockery::mock(JWT::class); $this->provider = Mockery::mock(EloquentUserProvider::class); - $this->guard = new JWTGuard($this->jwt, $this->provider, Request::create('/foo', 'GET')); + $this->eventDispatcher = Mockery::mock(Dispatcher::class); + $this->guard = new JWTGuard( + $this->jwt, + $this->provider, + Request::create('/foo', 'GET'), + $this->eventDispatcher + ); } /** @test */ @@ -204,6 +219,14 @@ public function it_should_return_a_token_if_credentials_are_ok_and_user_is_found ->with(['foo' => 'bar']) ->andReturnSelf(); + $this->eventDispatcher->shouldReceive('dispatch') + ->once() + ->with(Mockery::type(Attempting::class)); + + $this->eventDispatcher->shouldReceive('dispatch') + ->once() + ->with(Mockery::type(Validated::class)); + $token = $this->guard->claims(['foo' => 'bar'])->attempt($credentials); $this->assertSame($this->guard->getLastAttempted(), $user); @@ -226,6 +249,14 @@ public function it_should_return_true_if_credentials_are_ok_and_user_is_found_wh ->with($user, $credentials) ->andReturn(true); + $this->eventDispatcher->shouldReceive('dispatch') + ->twice() + ->with(Mockery::type(Attempting::class)); + + $this->eventDispatcher->shouldReceive('dispatch') + ->twice() + ->with(Mockery::type(Validated::class)); + $this->assertTrue($this->guard->attempt($credentials, false)); // once $this->assertTrue($this->guard->validate($credentials)); // twice } @@ -246,6 +277,14 @@ public function it_should_return_false_if_credentials_are_invalid() ->with($user, $credentials) ->andReturn(false); + $this->eventDispatcher->shouldReceive('dispatch') + ->once() + ->with(Mockery::type(Attempting::class)); + + $this->eventDispatcher->shouldReceive('dispatch') + ->once() + ->with(Mockery::type(Failed::class)); + $this->assertFalse($this->guard->attempt($credentials)); } @@ -346,6 +385,14 @@ public function it_should_authenticate_the_user_by_credentials_and_return_true_i ->with($user, $credentials) ->andReturn(true); + $this->eventDispatcher->shouldReceive('dispatch') + ->once() + ->with(Mockery::type(Attempting::class)); + + $this->eventDispatcher->shouldReceive('dispatch') + ->once() + ->with(Mockery::type(Validated::class)); + $this->assertTrue($this->guard->once($credentials)); } @@ -365,6 +412,14 @@ public function it_should_attempt_to_authenticate_the_user_by_credentials_and_re ->with($user, $credentials) ->andReturn(false); + $this->eventDispatcher->shouldReceive('dispatch') + ->once() + ->with(Mockery::type(Attempting::class)); + + $this->eventDispatcher->shouldReceive('dispatch') + ->once() + ->with(Mockery::type(Failed::class)); + $this->assertFalse($this->guard->once($credentials)); }