Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(authentication-events): dispatch Attemping, Validated and Failed events #2137

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 85 additions & 3 deletions src/JWTGuard.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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.
*
Expand Down
3 changes: 2 additions & 1 deletion src/Providers/AbstractServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
57 changes: 56 additions & 1 deletion tests/JWTGuardTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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);
Expand All @@ -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
}
Expand All @@ -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));
}

Expand Down Expand Up @@ -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));
}

Expand All @@ -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));
}

Expand Down