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); +}