Skip to content

Commit

Permalink
Merge pull request #40 from lexik/provider-overwrite
Browse files Browse the repository at this point in the history
Add flexibilty to the provider and manager
slashfan committed Oct 29, 2014

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
2 parents 09151cd + 364c8a6 commit 9ed5318
Showing 10 changed files with 187 additions and 48 deletions.
4 changes: 4 additions & 0 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
@@ -41,6 +41,10 @@ public function getConfigTreeBuilder()
->scalarNode('encoder_service')
->defaultValue('lexik_jwt_authentication.jwt_encoder')
->end()
->scalarNode('user_identity_field')
->defaultValue('username')
->cannotBeEmpty()
->end()
->end();

return $treeBuilder;
2 changes: 2 additions & 0 deletions DependencyInjection/LexikJWTAuthenticationExtension.php
Original file line number Diff line number Diff line change
@@ -33,6 +33,8 @@ public function load(array $configs, ContainerBuilder $container)
$container->setParameter('lexik_jwt_authentication.public_key_path', $config['public_key_path']);
$container->setParameter('lexik_jwt_authentication.pass_phrase', $config['pass_phrase']);
$container->setParameter('lexik_jwt_authentication.token_ttl', $config['token_ttl']);
$container->setParameter('lexik_jwt_authentication.user_identity_field', $config['user_identity_field']);

$container->setAlias('lexik_jwt_authentication.encoder', $config['encoder_service']);
}

5 changes: 4 additions & 1 deletion DependencyInjection/Security/Factory/JWTFactory.php
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ public function create(ContainerBuilder $container, $id, $config, $userProvider,
{
$providerId = 'security.authentication.provider.jwt.' . $id;
$container
->setDefinition($providerId, new DefinitionDecorator('lexik_jwt_authentication.security.authentication.provider'))
->setDefinition($providerId, new DefinitionDecorator($config['authentication_provider']))
->replaceArgument(0, new Reference($userProvider));

$listenerId = 'security.authentication.listener.jwt.' . $id;
@@ -116,6 +116,9 @@ public function addConfiguration(NodeDefinition $node)
->booleanNode('create_entry_point')
->defaultTrue()
->end()
->scalarNode('authentication_provider')
->defaultValue('lexik_jwt_authentication.security.authentication.provider')
->end()
->end();
}

6 changes: 3 additions & 3 deletions Encoder/JWTEncoder.php
Original file line number Diff line number Diff line change
@@ -14,17 +14,17 @@ class JWTEncoder implements JWTEncoderInterface
/**
* @var string
*/
private $privateKey;
protected $privateKey;

/**
* @var string
*/
private $publicKey;
protected $publicKey;

/**
* @var string
*/
private $passPhrase;
protected $passPhrase;

/**
* @param string $privateKey
12 changes: 9 additions & 3 deletions Resources/config/services.xml
Original file line number Diff line number Diff line change
@@ -17,22 +17,25 @@
</parameters>

<services>
<!-- JWT Encoder / Decoder -->
<!-- JWT Encoder / Decoder / Default implementation -->
<service id="lexik_jwt_authentication.jwt_encoder" class="%lexik_jwt_authentication.jwt_encoder.class%">
<argument>%lexik_jwt_authentication.private_key_path%</argument>
<argument>%lexik_jwt_authentication.public_key_path%</argument>
<argument>%lexik_jwt_authentication.pass_phrase%</argument>
</service>
<!-- JWT Manager -->
<!-- JWT Manager / Default implementation -->
<service id="lexik_jwt_authentication.jwt_manager" class="%lexik_jwt_authentication.jwt_manager.class%">
<argument type="service" id="lexik_jwt_authentication.encoder"/>
<argument type="service" id="event_dispatcher"/>
<argument>%lexik_jwt_authentication.token_ttl%</argument>
<call method="setRequest">
<argument type="service" id="request" on-invalid="null" strict="false" />
</call>
<call method="setUserIdentityField">
<argument>%lexik_jwt_authentication.user_identity_field%</argument>
</call>
</service>
<!-- JWT Authentication response interceptor -->
<!-- JWT Authentication response interceptor -->
<service id="lexik_jwt_authentication.handler.authentication_success" class="%lexik_jwt_authentication.handler.authentication_success.class%">
<argument type="service" id="lexik_jwt_authentication.jwt_manager"/>
<argument type="service" id="event_dispatcher"/>
@@ -46,6 +49,9 @@
<service id="lexik_jwt_authentication.security.authentication.provider" class="%lexik_jwt_authentication.security.authentication.provider.class%" public="false">
<argument /> <!-- User Provider -->
<argument type="service" id="lexik_jwt_authentication.jwt_manager" />
<call method="setUserIdentityField">
<argument>%lexik_jwt_authentication.user_identity_field%</argument>
</call>
</service>
<!-- JWT Security Authentication Listener -->
<service id="lexik_jwt_authentication.security.authentication.listener" class="%lexik_jwt_authentication.security.authentication.listener.class%" public="false">
61 changes: 49 additions & 12 deletions Security/Authentication/Provider/JWTProvider.php
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@
namespace Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Provider;

use Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\JWTUserToken;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTManager;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTManagerInterface;
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
@@ -19,47 +19,84 @@ class JWTProvider implements AuthenticationProviderInterface
/**
* @var UserProviderInterface
*/
private $userProvider;
protected $userProvider;

/**
* @var JWTManager
* @var JWTManagerInterface
*/
private $jwtManager;
protected $jwtManager;

/**
* @var string
*/
protected $userIdentityField;

/**
* @param UserProviderInterface $userProvider
* @param JWTManager $jwtManager
* @param JWTManagerInterface $jwtManager
*/
public function __construct(UserProviderInterface $userProvider, JWTManager $jwtManager)
public function __construct(UserProviderInterface $userProvider, JWTManagerInterface $jwtManager)
{
$this->userProvider = $userProvider;
$this->jwtManager = $jwtManager;
$this->userProvider = $userProvider;
$this->jwtManager = $jwtManager;
$this->userIdentityField = 'username';
}

/**
* {@inheritdoc}
*/
public function authenticate(TokenInterface $token)
{
$payload = $this->jwtManager->decode($token);

if (!$payload || !isset($payload['username'])) {
if (!($payload = $this->jwtManager->decode($token))) {
throw new AuthenticationException('Invalid JWT Token');
}

$user = $this->userProvider->loadUserByUsername($payload['username']);
$user = $this->getUserFromPayload($payload);

$authToken = new JWTUserToken($user->getRoles());
$authToken->setUser($user);

return $authToken;
}

/**
* Load user from payload, using username by default.
* Override this to load by another property.
*
* @param array $payload
*
* @return \Symfony\Component\Security\Core\User\UserInterface
*/
protected function getUserFromPayload(array $payload)
{
if (!isset($payload[$this->userIdentityField])) {
throw new AuthenticationException('Invalid JWT Token');
}

return $this->userProvider->loadUserByUsername($payload[$this->userIdentityField]);
}

/**
* {@inheritdoc}
*/
public function supports(TokenInterface $token)
{
return $token instanceof JWTUserToken;
}

/**
* @return string
*/
public function getUserIdentityField()
{
return $this->userIdentityField;
}

/**
* @param string $userIdentityField
*/
public function setUserIdentityField($userIdentityField)
{
$this->userIdentityField = $userIdentityField;
}
}
13 changes: 6 additions & 7 deletions Security/Http/Authentication/AuthenticationFailureHandler.php
Original file line number Diff line number Diff line change
@@ -17,6 +17,9 @@
*/
class AuthenticationFailureHandler implements AuthenticationFailureHandlerInterface
{
const RESPONSE_CODE = 401;
const RESPONSE_MESSAGE = 'Bad credentials';

/**
* @var EventDispatcherInterface
*/
@@ -35,17 +38,13 @@ public function __construct(EventDispatcherInterface $dispatcher)
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$statusCode = 401;

$data = array(
'code' => $statusCode,
'message' => 'Bad credentials',
'code' => self::RESPONSE_CODE,
'message' => self::RESPONSE_MESSAGE,
);

$response = new JsonResponse($data, $statusCode);

$event = new AuthenticationFailureEvent($request);
$event->setResponse($response);
$event->setResponse(new JsonResponse($data, self::RESPONSE_CODE));

$this->dispatcher->dispatch(Events::AUTHENTICATION_FAILURE, $event);

91 changes: 69 additions & 22 deletions Services/JWTManager.php
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@
*
* @author Nicolas Cabot <n.cabot@lexik.fr>
*/
class JWTManager
class JWTManager implements JWTManagerInterface
{
/**
* @var JWTEncoderInterface
@@ -28,10 +28,20 @@ class JWTManager
*/
protected $dispatcher;

/**
* @var integer
*/
protected $ttl;

/**
* @var string
*/
protected $userIdentityField;

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

/**
* @param JWTEncoderInterface $encoder
@@ -40,29 +50,22 @@ class JWTManager
*/
public function __construct(JWTEncoderInterface $encoder, EventDispatcherInterface $dispatcher, $ttl)
{
$this->jwtEncoder = $encoder;
$this->dispatcher = $dispatcher;
$this->ttl = $ttl;
}

/**
* @param Request $request
*/
public function setRequest(Request $request = null)
{
$this->request = $request;
$this->jwtEncoder = $encoder;
$this->dispatcher = $dispatcher;
$this->ttl = $ttl;
$this->userIdentityField = 'username';
}

/**
* @param UserInterface $user
*
* @return string
* {@inheritdoc}
*/
public function create(UserInterface $user)
{
$payload = array();
$payload['exp'] = time() + $this->ttl;
$payload['username'] = $user->getUsername();
$payload = array(
'exp' => time() + $this->ttl
);

$this->addUserIdentityToPayload($user, $payload);

$event = new JWTCreatedEvent($payload, $user, $this->request);
$this->dispatcher->dispatch(Events::JWT_CREATED, $event);
@@ -71,9 +74,7 @@ public function create(UserInterface $user)
}

/**
* @param TokenInterface $token
*
* @return bool|string
* {@inheritdoc}
*/
public function decode(TokenInterface $token)
{
@@ -90,4 +91,50 @@ public function decode(TokenInterface $token)

return $payload;
}

/**
* Add user identity to payload, username by default.
* Override this if you need to identify it by another property.
*
* @param UserInterface $user
* @param array $payload
*/
protected function addUserIdentityToPayload(UserInterface $user, array &$payload)
{

This comment has been minimized.

Copy link
@victuxbb

victuxbb Nov 10, 2014

Hi! I have seen that there is a param in yaml config to set "user_identity_field" but this method forces the field to be "username".
Personally I need a token with "email" and not "username", I have rewritten this method using ReflectionClass...

$reflectionClass = new \ReflectionClass($user);
$reflectionProperty = $reflectionClass->getProperty($this->userIdentityField);
$reflectionProperty->setAccessible(true);
$payload[$this->userIdentityField] = $reflectionProperty->getValue($user);

With this code, the "user_identity_field" is full configurable.
If you want to add me like a collaborator it would be great!
Thanks!

$payload[$this->userIdentityField] = $user->getUsername();
}

/**
* @return string
*/
public function getUserIdentityField()
{
return $this->userIdentityField;
}

/**
* @param string $userIdentityField
*/
public function setUserIdentityField($userIdentityField)
{
$this->userIdentityField = $userIdentityField;
}

/**
* @param Request $request
*/
public function setRequest(Request $request = null)
{
$this->request = $request;
}

/**
* Get request
*
* @return Request
*/
public function getRequest()
{
return $this->request;
}
}
28 changes: 28 additions & 0 deletions Services/JWTManagerInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Lexik\Bundle\JWTAuthenticationBundle\Services;

use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserInterface;

/**
* JWTManagerInterface
*
* @author Nicolas Cabot <n.cabot@lexik.fr>
*/
interface JWTManagerInterface
{
/**
* @param UserInterface $user
*
* @return string
*/
public function create(UserInterface $user);

/**
* @param TokenInterface $token
*
* @return bool|string
*/
public function decode(TokenInterface $token);
}
13 changes: 13 additions & 0 deletions Tests/Security/Authentication/Provider/JWTProviderTest.php
Original file line number Diff line number Diff line change
@@ -133,6 +133,19 @@ public function testAuthenticate()
'Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\JWTUserToken',
$provider->authenticate($jwtUserToken)
);

// test changing user identity field

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

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

$this->assertInstanceOf(
'Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\JWTUserToken',
$provider->authenticate($jwtUserToken)
);
}

/**

0 comments on commit 9ed5318

Please sign in to comment.