diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php
index 80618cdb..dd329835 100644
--- a/DependencyInjection/Configuration.php
+++ b/DependencyInjection/Configuration.php
@@ -38,10 +38,6 @@ public function getConfigTreeBuilder()
->defaultValue(86400)
->cannotBeEmpty()
->end()
- ->scalarNode('header_prefix')
- ->defaultValue('Bearer')
- ->cannotBeEmpty()
- ->end()
->end();
return $treeBuilder;
diff --git a/DependencyInjection/LexikJWTAuthenticationExtension.php b/DependencyInjection/LexikJWTAuthenticationExtension.php
index c855fed0..73d9dc72 100644
--- a/DependencyInjection/LexikJWTAuthenticationExtension.php
+++ b/DependencyInjection/LexikJWTAuthenticationExtension.php
@@ -29,6 +29,5 @@ 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.header_prefix', trim($config['header_prefix']));
}
}
diff --git a/DependencyInjection/Security/Factory/JWTFactory.php b/DependencyInjection/Security/Factory/JWTFactory.php
index e8f3254a..8573463b 100644
--- a/DependencyInjection/Security/Factory/JWTFactory.php
+++ b/DependencyInjection/Security/Factory/JWTFactory.php
@@ -20,13 +20,40 @@ class JWTFactory implements SecurityFactoryInterface
*/
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
- $providerId = 'security.authentication.provider.jwt.'.$id;
+ $providerId = 'security.authentication.provider.jwt.' . $id;
$container
- ->setDefinition($providerId, new DefinitionDecorator('jwt.security.authentication.provider'))
+ ->setDefinition($providerId, new DefinitionDecorator('lexik_jwt_authentication.security.authentication.provider'))
->replaceArgument(0, new Reference($userProvider));
- $listenerId = 'security.authentication.listener.jwt.'.$id;
- $container->setDefinition($listenerId, new DefinitionDecorator('jwt.security.authentication.listener'));
+ $listenerId = 'security.authentication.listener.jwt.' . $id;
+ $container
+ ->setDefinition($listenerId, new DefinitionDecorator('lexik_jwt_authentication.security.authentication.listener'));
+
+ if ($config['authorization_header']['enabled']) {
+
+ $authorizationHeaderExtractorId = 'lexik_jwt_authentication.extractor.authorization_header_extractor.' . $id;
+ $container
+ ->setDefinition($authorizationHeaderExtractorId, new DefinitionDecorator('lexik_jwt_authentication.extractor.authorization_header_extractor'))
+ ->replaceArgument(0, $config['authorization_header']['prefix']);
+
+ $container
+ ->getDefinition($listenerId)
+ ->addMethodCall('addTokenExtractor', array(new Reference($authorizationHeaderExtractorId)));
+
+ }
+
+ if ($config['query_parameter']['enabled']) {
+
+ $queryParameterExtractorId = 'lexik_jwt_authentication.extractor.query_parameter_extractor.' . $id;
+ $container
+ ->setDefinition($queryParameterExtractorId, new DefinitionDecorator('lexik_jwt_authentication.extractor.query_parameter_extractor'))
+ ->replaceArgument(0, $config['query_parameter']['name']);
+
+ $container
+ ->getDefinition($listenerId)
+ ->addMethodCall('addTokenExtractor', array(new Reference($queryParameterExtractorId)));
+
+ }
return array($providerId, $listenerId, $defaultEntryPoint);
}
@@ -44,13 +71,38 @@ public function getPosition()
*/
public function getKey()
{
- return 'jwt';
+ return 'lexik_jwt';
}
/**
* {@inheritdoc}
*/
- public function addConfiguration(NodeDefinition $builder)
+ public function addConfiguration(NodeDefinition $node)
{
+ $node
+ ->children()
+ ->arrayNode('authorization_header')
+ ->addDefaultsIfNotSet()
+ ->children()
+ ->booleanNode('enabled')
+ ->defaultTrue()
+ ->end()
+ ->scalarNode('prefix')
+ ->defaultValue('Bearer')
+ ->end()
+ ->end()
+ ->end()
+ ->arrayNode('query_parameter')
+ ->addDefaultsIfNotSet()
+ ->children()
+ ->booleanNode('enabled')
+ ->defaultFalse()
+ ->end()
+ ->scalarNode('name')
+ ->defaultValue('bearer')
+ ->end()
+ ->end()
+ ->end()
+ ->end();
}
}
diff --git a/LexikJWTAuthenticationBundle.php b/LexikJWTAuthenticationBundle.php
index 8130edc0..b86f5a59 100644
--- a/LexikJWTAuthenticationBundle.php
+++ b/LexikJWTAuthenticationBundle.php
@@ -3,6 +3,7 @@
namespace Lexik\Bundle\JWTAuthenticationBundle;
use Lexik\Bundle\JWTAuthenticationBundle\DependencyInjection\Security\Factory\JWTFactory;
+use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
@@ -20,6 +21,7 @@ public function build(ContainerBuilder $container)
{
parent::build($container);
+ /** @var SecurityExtension $extension */
$extension = $container->getExtension('security');
$extension->addSecurityListenerFactory(new JWTFactory());
}
diff --git a/README.md b/README.md
index fae39218..84098b9f 100644
--- a/README.md
+++ b/README.md
@@ -47,20 +47,20 @@ Then in your `config.yml` :
lexik_jwt_authentication:
private_key_path: 'app/var/jwt/private.pem' # path to the private key
public_key_path: 'app/var/jwt/public.pem' # path to the public key
- pass_phrase: '' # pass phrase, defaults to ''
- token_ttl: 86400 # token ttl in seconds, defaults to 86400
- header_prefix: 'Bearer' # authorization header value prefix, defaults to 'Bearer'
+ pass_phrase: '' # optional - pass phrase, defaults to ''
+ token_ttl: 86400 # optional - token ttl, defaults to 86400
```
Usage
-----
-First of all, you need to authenticate the user using its credentials through form login or http basic. Set the `lexik_jwt_authentication.handler.authentication_success` service as success handler, which will generate the JWT token and send it as the body of a JsonResponse (along with some non-encrypted optionnal data, see example below).
+First of all, you need to authenticate the user using its credentials through form login or http basic.
+Set the `lexik_jwt_authentication.handler.authentication_success` service as success handler, which will generate the JWT token and send it as the body of a JsonResponse.
Store the token in your client application (using cookie, localstorage or whatever - the token is encrypted).
+Now, you only need to pass the token on each future request, either as an authorization header or as a query string parameter.
-Now, you only need to pass it as an Authorization header on each future request. If it results in a 401 response, your token is invalid (most likely its ttl has expired - 86400 seconds by default).
-
+If it results in a 401 response, your token is invalid (most likely its ttl has expired - 86400 seconds by default).
Redo the authentication process to get a fresh token.
### Example of possible `security.yml` :
@@ -77,14 +77,25 @@ firewalls:
require_previous_session: false
username_parameter: username
password_parameter: password
- success_handler: lexik_jwt_authentication.handler.authentication_success # sends a 200 response with the token and optionnal extra data as body
- failure_handler: lexik_jwt_authentication.handler.authentication_failure # sends a 401 response
+ success_handler: lexik_jwt_authentication.handler.authentication_success # generate the jwt token and send it as 200 response body
+ failure_handler: lexik_jwt_authentication.handler.authentication_failure # send a 401 response
- # protected firewall, where a user will be authenticated by its jwt token (passed as an authorization header)
+ # protected firewall, where a user will be authenticated by its jwt token
api:
pattern: ^/api
stateless: true
- jwt: true
+
+ # default configuration
+ lexik_jwt: ~ # check token in Authorization Header, with a value prefix of 'Bearer'
+
+ # advanced configuration
+ lexik_jwt:
+ authorization_header: # check token in Authorization Header
+ enabled: true
+ prefix: Bearer
+ query_parameter: # check token in query string parameter
+ enabled: true
+ name: bearer
access_control:
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
@@ -131,7 +142,7 @@ public function onAuthenticationSuccessResponse(AuthenticationSuccessEvent $even
}
```
-### Functionnal tests (example)
+### Using jwt authentication in functional tests
Generate some test specific keys, for example :
@@ -146,13 +157,12 @@ Override the bundle configuration in your `config_test.yml` :
lexik_jwt_authentication:
private_key_path: %kernel.cache_dir%/jwt/private.pem
public_key_path: %kernel.cache_dir%/jwt/jwt/public.pem
- pass_phrase: 'test'
```
-In your functionnal tests, create an authenticated client :
+In your functional tests, create an authenticated client :
``` php
-protected function createAuthenticatedClient($username = 'admin@acme.tld')
+protected function createAuthenticatedClient($username = 'user@acme.tld')
{
$client = static::createClient();
@@ -169,5 +179,5 @@ protected function createAuthenticatedClient($username = 'admin@acme.tld')
TODO
----
-* Add the possibility to get the token from query string parameter
-* Add an optionnal IP restriction
+* Add IP to encrypted paypload ?
+* Add encryption algorithm option ?
diff --git a/Resources/config/services.xml b/Resources/config/services.xml
index 4247593b..d896b0c4 100644
--- a/Resources/config/services.xml
+++ b/Resources/config/services.xml
@@ -8,8 +8,10 @@
Lexik\Bundle\JWTAuthenticationBundle\Encoder\JWTEncoder
Lexik\Bundle\JWTAuthenticationBundle\Security\Http\Authentication\AuthenticationSuccessHandler
Lexik\Bundle\JWTAuthenticationBundle\Security\Http\Authentication\AuthenticationFailureHandler
- Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Provider\JWTProvider
- Lexik\Bundle\JWTAuthenticationBundle\Security\Firewall\JWTListener
+ Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Provider\JWTProvider
+ Lexik\Bundle\JWTAuthenticationBundle\Security\Firewall\JWTListener
+ Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\AuthorizationHeaderTokenExtractor
+ Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\QueryParameterTokenExtractor
@@ -30,15 +32,22 @@
-
+
-
+
- %lexik_jwt_authentication.header_prefix%
+
+
+
+
+
diff --git a/Security/Firewall/JWTListener.php b/Security/Firewall/JWTListener.php
index a6b0a831..1edc423e 100644
--- a/Security/Firewall/JWTListener.php
+++ b/Security/Firewall/JWTListener.php
@@ -2,6 +2,7 @@
namespace Lexik\Bundle\JWTAuthenticationBundle\Security\Firewall;
+use Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\TokenExtractorInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\JWTUserToken;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -29,20 +30,19 @@ class JWTListener implements ListenerInterface
protected $authenticationManager;
/**
- * @var string
+ * @var array
*/
- protected $headerPrefix;
+ protected $tokenExtractors;
/**
* @param SecurityContextInterface $securityContext
* @param AuthenticationManagerInterface $authenticationManager
- * @param string $headerPrefix
*/
- public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, $headerPrefix)
+ public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager)
{
$this->securityContext = $securityContext;
$this->authenticationManager = $authenticationManager;
- $this->headerPrefix = $headerPrefix;
+ $this->tokenExtractors = array();
}
/**
@@ -50,12 +50,12 @@ public function __construct(SecurityContextInterface $securityContext, Authentic
*/
public function handle(GetResponseEvent $event)
{
- if (!($raw = $this->getRawTokenFromRequest($event->getRequest()))) {
+ if (!($requestToken = $this->getRequestToken($event->getRequest()))) {
return;
}
$token = new JWTUserToken();
- $token->setRawToken($raw);
+ $token->setRawToken($requestToken);
try {
@@ -73,23 +73,28 @@ public function handle(GetResponseEvent $event)
}
}
+ /**
+ * @param TokenExtractorInterface $extractor
+ */
+ public function addTokenExtractor(TokenExtractorInterface $extractor)
+ {
+ $this->tokenExtractors[] = $extractor;
+ }
+
/**
* @param Request $request
*
* @return boolean|string
*/
- protected function getRawTokenFromRequest(Request $request)
+ protected function getRequestToken(Request $request)
{
- if (!$request->headers->has('Authorization')) {
- return false;
- }
-
- $headerParts = explode(' ', $request->headers->get('Authorization'));
-
- if (!(count($headerParts) === 2 && $headerParts[0] === $this->headerPrefix)) {
- return false;
+ /** @var TokenExtractorInterface $tokenExtractor */
+ foreach ($this->tokenExtractors as $tokenExtractor) {
+ if (($token = $tokenExtractor->extract($request))) {
+ return $token;
+ }
}
- return $headerParts[1];
+ return false;
}
}
diff --git a/Tests/Security/Authentication/Firewall/JWTListenerTest.php b/Tests/Security/Authentication/Firewall/JWTListenerTest.php
new file mode 100644
index 00000000..33522c8e
--- /dev/null
+++ b/Tests/Security/Authentication/Firewall/JWTListenerTest.php
@@ -0,0 +1,123 @@
+
+ */
+class JWTListenerTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * test handle method
+ */
+ public function testHandle()
+ {
+ // no token extractor : should return void
+
+ $listener = new JWTListener($this->getSecurityContextMock(), $this->getAuthenticationManagerMock());
+ $this->assertNull($listener->handle($this->getEvent()));
+
+ // one token extractor with no result : should return void
+
+ $listener = new JWTListener($this->getSecurityContextMock(), $this->getAuthenticationManagerMock());
+ $listener->addTokenExtractor($this->getAuthorizationHeaderTokenExtractorMock(false));
+ $this->assertNull($listener->handle($this->getEvent()));
+
+ // request token found : should enter authentication process
+
+ $authenticationManager = $this->getAuthenticationManagerMock();
+ $authenticationManager->expects($this->once())->method('authenticate');
+
+ $listener = new JWTListener($this->getSecurityContextMock(), $authenticationManager);
+ $listener->addTokenExtractor($this->getAuthorizationHeaderTokenExtractorMock('token'));
+ $listener->handle($this->getEvent());
+
+ // request token found : authentication fail
+
+ $authenticationManager = $this->getAuthenticationManagerMock();
+ $authenticationManager
+ ->expects($this->once())
+ ->method('authenticate');
+ $authenticationManager
+ ->expects($this->once())
+ ->method('authenticate')
+ ->will($this->throwException(new \Symfony\Component\Security\Core\Exception\AuthenticationException));
+
+ $listener = new JWTListener($this->getSecurityContextMock(), $authenticationManager);
+ $listener->addTokenExtractor($this->getAuthorizationHeaderTokenExtractorMock('token'));
+
+ $event = $this->getEvent();
+ $event->expects($this->once())->method('setResponse');
+
+ $listener->handle($event);
+ }
+
+ /**
+ * @return \PHPUnit_Framework_MockObject_MockObject
+ */
+ public function getAuthenticationManagerMock()
+ {
+ return $this
+ ->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ /**
+ * @return \PHPUnit_Framework_MockObject_MockObject
+ */
+ public function getSecurityContextMock()
+ {
+ return $this
+ ->getMockBuilder('Symfony\Component\Security\Core\SecurityContext')
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ /**
+ * @param mixed $returnValue
+ *
+ * @return \PHPUnit_Framework_MockObject_MockObject
+ */
+ protected function getAuthorizationHeaderTokenExtractorMock($returnValue)
+ {
+ $extractor = $this
+ ->getMockBuilder('Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\AuthorizationHeaderTokenExtractor')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $extractor
+ ->expects($this->any())
+ ->method('extract')
+ ->will($this->returnValue($returnValue));
+
+ return $extractor;
+ }
+
+ /**
+ * @return \PHPUnit_Framework_MockObject_MockObject
+ */
+ protected function getEvent()
+ {
+ $request = $this
+ ->getMockBuilder('Symfony\Component\HttpFoundation\Request')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $event = $this
+ ->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $event
+ ->expects($this->any())
+ ->method('getRequest')
+ ->will($this->returnValue($request));
+
+ return $event;
+ }
+}
diff --git a/Tests/Security/Authentication/Provider/JWTProviderTest.php b/Tests/Security/Authentication/Provider/JWTProviderTest.php
new file mode 100644
index 00000000..0a9eb618
--- /dev/null
+++ b/Tests/Security/Authentication/Provider/JWTProviderTest.php
@@ -0,0 +1,89 @@
+
+ */
+class JWTProviderTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * test supports method
+ */
+ public function testSupports()
+ {
+ $provider = new JWTProvider($this->getUserProviderMock(), $this->getJWTEncoderMock());
+
+ /** @var TokenInterface $usernamePasswordToken */
+ $usernamePasswordToken = $this
+ ->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertFalse($provider->supports($usernamePasswordToken));
+
+ /** @var TokenInterface $jwtUserToken */
+ $jwtUserToken = $this
+ ->getMockBuilder('Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\JWTUserToken')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->assertTrue($provider->supports($jwtUserToken));
+ }
+
+ /**
+ * test authenticate method
+ */
+ public function testAuthenticate()
+ {
+ /** @var TokenInterface $jwtUserToken */
+ $jwtUserToken = $this
+ ->getMockBuilder('Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\JWTUserToken')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jwtEncoder = $this->getJWTEncoderMock();
+ $jwtEncoder->expects($this->any())->method('decode')->will($this->returnValue(array('username' => 'user')));
+
+ $user = $this
+ ->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')
+ ->getMock();
+
+ $user->expects($this->any())->method('getRoles')->will($this->returnValue(array()));
+
+ $userProvider = $this->getUserProviderMock();
+ $userProvider->expects($this->any())->method('loadUserByUsername')->will($this->returnValue($user));
+
+ $provider = new JWTProvider($userProvider, $jwtEncoder);
+
+ $this->assertInstanceOf(
+ 'Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\JWTUserToken',
+ $provider->authenticate($jwtUserToken)
+ );
+ }
+
+ /**
+ * @return \PHPUnit_Framework_MockObject_MockObject
+ */
+ protected function getJWTEncoderMock()
+ {
+ return $this->getMockBuilder('Lexik\Bundle\JWTAuthenticationBundle\Encoder\JWTEncoder')
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ /**
+ * @return \PHPUnit_Framework_MockObject_MockObject
+ */
+ protected function getUserProviderMock()
+ {
+ return $this->getMockBuilder('Symfony\Component\Security\Core\User\InMemoryUserProvider')
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+}
diff --git a/Tests/Security/Http/Authentication/AuthenticationFailureHandlerTest.php b/Tests/Security/Http/Authentication/AuthenticationFailureHandlerTest.php
new file mode 100644
index 00000000..bd0941aa
--- /dev/null
+++ b/Tests/Security/Http/Authentication/AuthenticationFailureHandlerTest.php
@@ -0,0 +1,47 @@
+
+ */
+class AuthenticationFailureHandlerTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * test onAuthenticationSuccess method
+ */
+ public function testOnAuthenticationFailure()
+ {
+ $handler = new AuthenticationFailureHandler();
+ $response = $handler->onAuthenticationFailure($this->getRequest(), $this->getAuthenticationException());
+
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
+ $this->assertEquals(401, $response->getStatusCode());
+ }
+
+ /**
+ * @return \PHPUnit_Framework_MockObject_MockObject
+ */
+ protected function getRequest()
+ {
+ return $this
+ ->getMockBuilder('Symfony\Component\HttpFoundation\Request')
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ /**
+ * @return \PHPUnit_Framework_MockObject_MockObject
+ */
+ protected function getAuthenticationException()
+ {
+ return $this
+ ->getMockBuilder('Symfony\Component\Security\Core\Exception\AuthenticationException')
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+}
diff --git a/Tests/Security/Http/Authentication/AuthenticationSuccessHandlerTest.php b/Tests/Security/Http/Authentication/AuthenticationSuccessHandlerTest.php
new file mode 100644
index 00000000..6dd3c4be
--- /dev/null
+++ b/Tests/Security/Http/Authentication/AuthenticationSuccessHandlerTest.php
@@ -0,0 +1,101 @@
+
+ */
+class AuthenticationSuccessHandlerTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * test onAuthenticationSuccess method
+ */
+ public function testOnAuthenticationSuccess()
+ {
+ $request = $this->getRequest();
+ $token = $this->getToken();
+
+ $response = $this->getHandler()->onAuthenticationSuccess($request, $token);
+ $this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
+ $this->assertEquals(200, $response->getStatusCode());
+
+ $content = json_decode($response->getContent(), true);
+ $this->assertArrayHasKey('token', $content);
+ $this->assertEquals('tokenstring', $content['token']);
+ }
+
+ /**
+ * @return AuthenticationSuccessHandler
+ */
+ protected function getHandler()
+ {
+ $jws = $this
+ ->getMockBuilder('Namshi\JOSE\JWS')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $jws
+ ->expects($this->any())
+ ->method('getTokenString')
+ ->will($this->returnValue('tokenstring'));
+
+ $encoder = $this->getMockBuilder('Lexik\Bundle\JWTAuthenticationBundle\Encoder\JWTEncoder')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $encoder
+ ->expects($this->any())
+ ->method('encode')
+ ->will($this->returnValue($jws));
+
+ $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcher')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ return new AuthenticationSuccessHandler($encoder, $dispatcher, 3600);
+ }
+
+ /**
+ * @return \PHPUnit_Framework_MockObject_MockObject
+ */
+ protected function getRequest()
+ {
+ $request = $this
+ ->getMockBuilder('Symfony\Component\HttpFoundation\Request')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ return $request;
+ }
+
+ /**
+ * @return \PHPUnit_Framework_MockObject_MockObject
+ */
+ protected function getToken()
+ {
+ $user = $this
+ ->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')
+ ->getMock();
+
+ $user
+ ->expects($this->any())
+ ->method('getUsername')
+ ->will($this->returnValue('username'));
+
+ $token = $this
+ ->getMockBuilder('Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\JWTUserToken')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $token
+ ->expects($this->any())
+ ->method('getUser')
+ ->will($this->returnValue($user));
+
+ return $token;
+ }
+}
diff --git a/Tests/TokenExtractor/AuthorizationHeaderTokenExtractorTest.php b/Tests/TokenExtractor/AuthorizationHeaderTokenExtractorTest.php
new file mode 100644
index 00000000..1536ac82
--- /dev/null
+++ b/Tests/TokenExtractor/AuthorizationHeaderTokenExtractorTest.php
@@ -0,0 +1,30 @@
+
+ */
+class AuthorizationHeaderTokenExtractorTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * test getRequestToken
+ */
+ public function testGetTokenRequest()
+ {
+ $extractor = new AuthorizationHeaderTokenExtractor('Bearer');
+
+ $request = new Request();
+ $request->headers->set('Authorization', 'Bear testtoken');
+ $this->assertFalse($extractor->extract($request));
+
+ $request = new Request();
+ $request->headers->set('Authorization', 'Bearer testtoken');
+ $this->assertEquals('testtoken', $extractor->extract($request));
+ }
+}
diff --git a/Tests/TokenExtractor/QueryParameterTokenExtractorTest.php b/Tests/TokenExtractor/QueryParameterTokenExtractorTest.php
new file mode 100644
index 00000000..e6bc22ec
--- /dev/null
+++ b/Tests/TokenExtractor/QueryParameterTokenExtractorTest.php
@@ -0,0 +1,28 @@
+
+ */
+class QueryParameterTokenExtractorTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * test getRequestToken
+ */
+ public function testGetTokenRequest()
+ {
+ $extractor = new QueryParameterTokenExtractor('bearer');
+
+ $request = new Request(array('bear' => 'testtoken'));
+ $this->assertFalse($extractor->extract($request));
+
+ $request = new Request(array('bearer' => 'testtoken'));
+ $this->assertEquals('testtoken', $extractor->extract($request));
+ }
+}
diff --git a/TokenExtractor/AuthorizationHeaderTokenExtractor.php b/TokenExtractor/AuthorizationHeaderTokenExtractor.php
new file mode 100644
index 00000000..ae73b78b
--- /dev/null
+++ b/TokenExtractor/AuthorizationHeaderTokenExtractor.php
@@ -0,0 +1,46 @@
+
+ */
+class AuthorizationHeaderTokenExtractor implements TokenExtractorInterface
+{
+ /**
+ * @var string
+ */
+ protected $prefix;
+
+ /**
+ * @param string $prefix
+ */
+ public function __construct($prefix)
+ {
+ $this->prefix = $prefix;
+ }
+
+ /**
+ * @param Request $request
+ *
+ * @return string
+ */
+ public function extract(Request $request)
+ {
+ if (!$request->headers->has('Authorization')) {
+ return false;
+ }
+
+ $headerParts = explode(' ', $request->headers->get('Authorization'));
+
+ if (!(count($headerParts) === 2 && $headerParts[0] === $this->prefix)) {
+ return false;
+ }
+
+ return $headerParts[1];
+ }
+}
diff --git a/TokenExtractor/QueryParameterTokenExtractor.php b/TokenExtractor/QueryParameterTokenExtractor.php
new file mode 100644
index 00000000..3894e213
--- /dev/null
+++ b/TokenExtractor/QueryParameterTokenExtractor.php
@@ -0,0 +1,36 @@
+
+ */
+class QueryParameterTokenExtractor implements TokenExtractorInterface
+{
+ /**
+ * @var string
+ */
+ protected $parameterName;
+
+ /**
+ * @param string $parameterName
+ */
+ public function __construct($parameterName)
+ {
+ $this->parameterName = $parameterName;
+ }
+
+ /**
+ * @param Request $request
+ *
+ * @return string
+ */
+ public function extract(Request $request)
+ {
+ return $request->query->get($this->parameterName, false);
+ }
+}
diff --git a/TokenExtractor/TokenExtractorInterface.php b/TokenExtractor/TokenExtractorInterface.php
new file mode 100644
index 00000000..bbc25264
--- /dev/null
+++ b/TokenExtractor/TokenExtractorInterface.php
@@ -0,0 +1,20 @@
+
+ */
+interface TokenExtractorInterface
+{
+ /**
+ * @param Request $request
+ *
+ * @return string
+ */
+ public function extract(Request $request);
+}