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

Migrated PR2137: Authentication events #18

Merged
merged 1 commit into from
Oct 6, 2021
Merged
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
90 changes: 84 additions & 6 deletions src/JWTGuard.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@
namespace PHPOpenSourceSaver\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 PHPOpenSourceSaver\JWTAuth\Contracts\JWTSubject;
Expand Down Expand Up @@ -48,6 +52,20 @@ 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';
Messhias marked this conversation as resolved.
Show resolved Hide resolved

/**
* Instantiate the class.
*
Expand All @@ -57,11 +75,12 @@ class JWTGuard implements Guard
*
* @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 All @@ -75,7 +94,8 @@ public function user()
return $this->user;
}

if ($this->jwt->setRequest($this->request)->getToken() &&
if (
$this->jwt->setRequest($this->request)->getToken() &&
($payload = $this->jwt->check(true)) &&
$this->validateSubject()
) {
Expand All @@ -92,7 +112,7 @@ public function user()
*/
public function userOrFail()
{
if (! $user = $this->user()) {
if (!$user = $this->user()) {
throw new UserNotDefinedException;
}

Expand Down Expand Up @@ -123,10 +143,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 +411,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 All @@ -399,7 +429,7 @@ protected function validateSubject()
{
// If the provider doesn't have the necessary method
// to get the underlying model name then allow.
if (! method_exists($this->provider, 'getModel')) {
if (!method_exists($this->provider, 'getModel')) {
return true;
}

Expand All @@ -415,13 +445,61 @@ protected function validateSubject()
*/
protected function requireToken()
{
if (! $this->jwt->setRequest($this->getRequest())->getToken()) {
if (!$this->jwt->setRequest($this->getRequest())->getToken()) {
throw new JWTException('Token could not be parsed from the request.');
}

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 @@ -95,7 +95,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 PHPOpenSourceSaver\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\Contracts\Auth\UserProvider;
use Illuminate\Http\Request;
use Mockery;
Expand Down Expand Up @@ -41,13 +45,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 @@ -206,6 +221,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 @@ -228,6 +251,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 @@ -248,6 +279,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 @@ -348,6 +387,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 @@ -367,6 +414,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
6 changes: 3 additions & 3 deletions tests/ManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -283,22 +283,22 @@ public function test_if_show_blacklisted_exception_configuration_is_enabled()
{
$this->manager->setBlackListExceptionEnabled(true);

$this->assertIsBool($this->manager->gettBlackListExceptionEnabled());
$this->assertIsBool($this->manager->getBlackListExceptionEnabled());
}

/** @test */
public function test_if_black_listed_exception_is_set_to_true()
{
$this->manager->setBlackListExceptionEnabled(true);

$this->assertTrue($this->manager->gettBlackListExceptionEnabled());
$this->assertTrue($this->manager->getBlackListExceptionEnabled());
}

/** @test */
public function test_if_black_listed_exception_is_set_to_false()
{
$this->manager->setBlackListExceptionEnabled(false);

$this->assertFalse($this->manager->gettBlackListExceptionEnabled());
$this->assertFalse($this->manager->getBlackListExceptionEnabled());
}
}