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

Added new event when token is authenticated #69

Merged
merged 1 commit into from
Jun 10, 2015
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
2 changes: 1 addition & 1 deletion Encoder/JWTEncoderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public function encode(array $data);
/**
* @param string $token
*
* @return array
* @return bool|array
*/
public function decode($token);
}
65 changes: 65 additions & 0 deletions Event/JWTAuthenticatedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php


namespace Lexik\Bundle\JWTAuthenticationBundle\Event;

use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;

/**
* JWTCreatedEvent
*/
class JWTAuthenticatedEvent extends Event
{
/**
* @var array
*/
protected $payload;

/**
* @var TokenInterface
*/
protected $token;

/**
* @var Request
*/
protected $request;

/**
* @param array $payload
* @param TokenInterface $token
*/
public function __construct(array $payload, TokenInterface $token)
{
$this->payload = $payload;
$this->token = $token;
}

/**
* @return array
*/
public function getPayload()
{
return $this->payload;
}

/**
* @param array $payload
*/
public function setPayload(array $payload)
{
$this->payload = $payload;
}

/**
* Get token
*
* @return TokenInterface
*/
public function getToken()
{
return $this->token;
}
}
6 changes: 6 additions & 0 deletions Events.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,10 @@ final class Events
* Hook into this event to perform additional validation on the received payload.
*/
const JWT_DECODED = 'lexik_jwt_authentication.on_jwt_decoded';

/**
* Dispatched after the token payload has been authenticated by the provider.
* Hook into this event to perform additional modification to the authenticated token using the payload.
*/
const JWT_AUTHENTICATED = 'lexik_jwt_authentication.on_jwt_authenticated';
}
1 change: 1 addition & 0 deletions Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
<call method="setUserIdentityField">
<argument>%lexik_jwt_authentication.user_identity_field%</argument>
</call>
<argument type="service" id="event_dispatcher"/>
</service>
<!-- JWT Security Authentication Listener -->
<service id="lexik_jwt_authentication.security.authentication.listener" class="%lexik_jwt_authentication.security.authentication.listener.class%" public="false">
Expand Down
38 changes: 36 additions & 2 deletions Resources/doc/2-data-customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class JWTCreatedListener

#### Events::JWT_DECODED - validate data in the JWT payload

You can access the jwt payload once it has been decoded to perform you own additional validation.
You can access the jwt payload once it has been decoded to perform you own additional validation.

``` yaml
# services.yml
Expand Down Expand Up @@ -106,6 +106,40 @@ class JWTDecodedListener
}
```

#### Events::JWT_AUTHENTICATED - customize your authenticated token

You can add attributes to the token once it has been authenticated to allow JWT properties to be used by your application.

``` yaml
# services.yml
services:
acme_api.event.jwt_authenticated_listener:
class: Acme\Bundle\ApiBundle\EventListener\JWTAuthenticatedListener
tags:
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_authenticated, method: onJWTAuthenticated }
```

Example 4 : Keep a UUID that was set into the JWT in the authenticated token

``` php
// Acme\Bundle\ApiBundle\EventListener\JWTAuthenticatedListener.php
class JWTAuthenticatedListener
{
/**
* @param JWTAuthenticatedEvent $event
*
* @return void
*/
public function onJWTAuthenticated(JWTAuthenticatedEvent $event)
{
$token = $event->getToken();
$payload = $event->getPayload();

$token->setAttribute('uuid', $payload['uuid']);
}
}
```

#### Events::AUTHENTICATION_SUCCESS - add public data to the JWT response

By default, the authentication response is just a json containing the JWT but you can add your own public data to it.
Expand All @@ -119,7 +153,7 @@ services:
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_authentication_success, method: onAuthenticationSuccessResponse }
```

Example 4 : add user roles to the response
Example 5 : add user roles to the response

``` php
// Acme\Bundle\ApiBundle\EventListener\AuthenticationSuccessListener.php
Expand Down
14 changes: 13 additions & 1 deletion Security/Authentication/Provider/JWTProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@

use Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\JWTUserToken;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTManagerInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Events;
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTAuthenticatedEvent;
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
* JWTProvider
Expand All @@ -26,6 +29,11 @@ class JWTProvider implements AuthenticationProviderInterface
*/
protected $jwtManager;

/**
* @var EventDispatcherInterface
*/
protected $dispatcher;

/**
* @var string
*/
Expand All @@ -35,10 +43,11 @@ class JWTProvider implements AuthenticationProviderInterface
* @param UserProviderInterface $userProvider
* @param JWTManagerInterface $jwtManager
*/
public function __construct(UserProviderInterface $userProvider, JWTManagerInterface $jwtManager)
public function __construct(UserProviderInterface $userProvider, JWTManagerInterface $jwtManager, EventDispatcherInterface $dispatcher)
{
$this->userProvider = $userProvider;
$this->jwtManager = $jwtManager;
$this->dispatcher = $dispatcher;
$this->userIdentityField = 'username';
}

Expand All @@ -57,6 +66,9 @@ public function authenticate(TokenInterface $token)
$authToken->setUser($user);
$authToken->setRawToken($token->getCredentials());

$event = new JWTAuthenticatedEvent($payload, $authToken);
$this->dispatcher->dispatch(Events::JWT_AUTHENTICATED, $event);

return $authToken;
}

Expand Down
4 changes: 2 additions & 2 deletions Services/JWTManagerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
*
* @author Nicolas Cabot <n.cabot@lexik.fr>
*/
interface JWTManagerInterface
interface JWTManagerInterface
{
/**
* @param UserInterface $user
Expand All @@ -22,7 +22,7 @@ public function create(UserInterface $user);
/**
* @param TokenInterface $token
*
* @return bool|string
* @return bool|array
*/
public function decode(TokenInterface $token);
}
28 changes: 22 additions & 6 deletions Tests/Security/Authentication/Provider/JWTProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class JWTProviderTest extends \PHPUnit_Framework_TestCase
*/
public function testSupports()
{
$provider = new JWTProvider($this->getUserProviderMock(), $this->getJWTManagerMock());
$provider = new JWTProvider($this->getUserProviderMock(), $this->getJWTManagerMock(), $this->getEventDispatcherMock());

/** @var TokenInterface $usernamePasswordToken */
$usernamePasswordToken = $this
Expand Down Expand Up @@ -51,11 +51,12 @@ public function testAuthenticateWithInvalidJWT()
->getMock();

$userProvider = $this->getUserProviderMock();
$eventDispatcher = $this->getEventDispatcherMock();

$jwtManager = $this->getJWTManagerMock();
$jwtManager->expects($this->any())->method('decode')->will($this->returnValue(false));

$provider = new JWTProvider($userProvider, $jwtManager);
$provider = new JWTProvider($userProvider, $jwtManager, $eventDispatcher);
$provider->authenticate($jwtUserToken);
}

Expand All @@ -73,11 +74,12 @@ public function testAuthenticateWithoutUsername()
->getMock();

$userProvider = $this->getUserProviderMock();
$eventDispatcher = $this->getEventDispatcherMock();

$jwtManager = $this->getJWTManagerMock();
$jwtManager->expects($this->any())->method('decode')->will($this->returnValue(array('foo' => 'bar')));

$provider = new JWTProvider($userProvider, $jwtManager);
$provider = new JWTProvider($userProvider, $jwtManager, $eventDispatcher);
$provider->authenticate($jwtUserToken);
}

Expand All @@ -97,10 +99,12 @@ public function testAuthenticateWithNotExistingUser()
$userProvider = $this->getUserProviderMock();
$userProvider->expects($this->any())->method('loadUserByUsername')->willThrowException(new UsernameNotFoundException());

$eventDispatcher = $this->getEventDispatcherMock();

$jwtManager = $this->getJWTManagerMock();
$jwtManager->expects($this->any())->method('decode')->will($this->returnValue(array('username' => 'user')));

$provider = new JWTProvider($userProvider, $jwtManager);
$provider = new JWTProvider($userProvider, $jwtManager, $eventDispatcher);
$provider->authenticate($jwtUserToken);
}

Expand All @@ -124,10 +128,12 @@ public function testAuthenticate()
$userProvider = $this->getUserProviderMock();
$userProvider->expects($this->any())->method('loadUserByUsername')->will($this->returnValue($user));

$eventDispatcher = $this->getEventDispatcherMock();

$jwtManager = $this->getJWTManagerMock();
$jwtManager->expects($this->any())->method('decode')->will($this->returnValue(array('username' => 'user')));

$provider = new JWTProvider($userProvider, $jwtManager);
$provider = new JWTProvider($userProvider, $jwtManager, $eventDispatcher);

$this->assertInstanceOf(
'Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\JWTUserToken',
Expand All @@ -139,7 +145,7 @@ public function testAuthenticate()
$jwtManager = $this->getJWTManagerMock();
$jwtManager->expects($this->any())->method('decode')->will($this->returnValue(array('uid' => 'user')));

$provider = new JWTProvider($userProvider, $jwtManager);
$provider = new JWTProvider($userProvider, $jwtManager, $eventDispatcher);
$provider->setUserIdentityField('uid');

$this->assertInstanceOf(
Expand Down Expand Up @@ -177,4 +183,14 @@ protected function getUserProviderMock()
->disableOriginalConstructor()
->getMock();
}

/**
* @return \PHPUnit_Framework_MockObject_MockObject
*/
protected function getEventDispatcherMock()
{
return $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcher')
->disableOriginalConstructor()
->getMock();
}
}