Skip to content

Commit

Permalink
Add JWTAuthenticationResponse
Browse files Browse the repository at this point in the history
CS Fixes

[WIP] Use the JWTResponse in authentication handlers

Add authentication failure/success Response classes

Fix tests
  • Loading branch information
chalasr committed May 20, 2016
1 parent f6f7722 commit 9069a5f
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 40 deletions.
63 changes: 63 additions & 0 deletions Response/JWTAuthenticationFailureResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

namespace Lexik\Bundle\JWTAuthenticationBundle\Response;

use Symfony\Component\HttpFoundation\JsonResponse;

/**
* JWTAuthenticationFailureResponse.
*
* Response sent on failed JWT authentication (can be replaced by a custom Response).
*
* @internal
*
* @author Robin Chalas <[email protected]>
*/
final class JWTAuthenticationFailureResponse extends JsonResponse
{
/**
* The response message.
*
* @var string
*/
private $message;

/**
* @param string $message A failure message passed in the response body
*/
public function __construct($message = 'Bad credentials')
{
$this->message = $message;

parent::__construct(null, self::HTTP_UNAUTHORIZED, ['WWW-Authenticate' => 'Bearer']);

$this->setData([
'code' => $this->statusCode,
'message' => $this->message,
]);
}

/**
* Sets the failure message.
*
* @param string $message
*
* @return JWTAuthenticationFailureResponse
*/
public function setMessage($message)
{
$this->message = $message;

return $this;
}

/**
* Gets the failure message.
*
* @return string
*/
public function getMessage()
{
return $this->message;
}
}
89 changes: 89 additions & 0 deletions Response/JWTAuthenticationSuccessResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

namespace Lexik\Bundle\JWTAuthenticationBundle\Response;

use Symfony\Component\HttpFoundation\JsonResponse;

/**
* Response sent on successful JWT authentication.
*
* @internal
*
* @author Robin Chalas <[email protected]>
*/
final class JWTAuthenticationSuccessResponse extends JsonResponse
{
/**
* The Json Web Token.
*
* Immutable property.
*
* @var string
*/
private $token;

/**
* @param string $token Json Web Token
* @param array $data Extra data passed to the response body.
* @param array $headers HTTP headers
*/
public function __construct($token, array $extraData = [])
{
$this->token = $token;
$this->extraData = $extraData;

parent::__construct();

$this->setBody();
}

/**
* Gets the Json Web Token.
*
* @return string
*/
public function getToken()
{
return $this->token;
}

/**
* {@inheritdoc}
*/
public function setExtraData(array $extraData = [])
{
$this->extraData = $extraData;

return $this;
}

/**
* {@inheritdoc}
*/
public function getExtraData()
{
return $this->extraData;
}

/**
* Prevents unexpected response content.
*
* @internal
*
* {@inheritdoc}
*/
public function setData($data = [])
{
return $this->setBody();
}

/**
* Creates the response body.
*
* @return JWTAuthenticationSuccessResponse
*/
private function setBody()
{
parent::setData(['token' => $this->token] + $this->extraData);
}
}
10 changes: 2 additions & 8 deletions Security/Firewall/JWTListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
use Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\JWTUserToken;
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTInvalidEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Events;
use Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationFailureResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
Expand Down Expand Up @@ -91,13 +91,7 @@ public function handle(GetResponseEvent $event)
throw $failed;
}

$data = [
'code' => 401,
'message' => $failed->getMessage(),
];

$response = new JsonResponse($data, $data['code']);
$response->headers->set('WWW-Authenticate', 'Bearer');
$response = new JWTAuthenticationFailureResponse($failed->getMessage());

$jwtInvalidEvent = new JWTInvalidEvent($request, $failed, $response);
$this->dispatcher->dispatch(Events::JWT_INVALID, $jwtInvalidEvent);
Expand Down
13 changes: 2 additions & 11 deletions Security/Http/Authentication/AuthenticationFailureHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationFailureEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Events;
use Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationFailureResponse;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
Expand All @@ -17,9 +17,6 @@
*/
class AuthenticationFailureHandler implements AuthenticationFailureHandlerInterface
{
const RESPONSE_CODE = 401;
const RESPONSE_MESSAGE = 'Bad credentials';

/**
* @var EventDispatcherInterface
*/
Expand All @@ -38,13 +35,7 @@ public function __construct(EventDispatcherInterface $dispatcher)
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$data = [
'code' => self::RESPONSE_CODE,
'message' => self::RESPONSE_MESSAGE,
];

$response = new JsonResponse($data, self::RESPONSE_CODE);
$event = new AuthenticationFailureEvent($request, $exception, $response);
$event = new AuthenticationFailureEvent($request, $exception, new JWTAuthenticationFailureResponse());

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

Expand Down
11 changes: 5 additions & 6 deletions Security/Http/Authentication/AuthenticationSuccessHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationSuccessEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Events;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTManager;
use Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationSuccessResponse;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
Expand Down Expand Up @@ -43,14 +43,13 @@ public function __construct(JWTManager $jwtManager, EventDispatcherInterface $di
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$user = $token->getUser();
$jwt = $this->jwtManager->create($user);

$response = new JsonResponse();
$user = $token->getUser();
$jwt = $this->jwtManager->create($user);
$response = new JWTAuthenticationSuccessResponse($jwt);
$event = new AuthenticationSuccessEvent(['token' => $jwt], $user, $request, $response);

$this->dispatcher->dispatch(Events::AUTHENTICATION_SUCCESS, $event);
$response->setData($event->getData());
$response->setExtraData($event->getData());

return $response;
}
Expand Down
12 changes: 2 additions & 10 deletions Security/Http/EntryPoint/JWTEntryPoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

namespace Lexik\Bundle\JWTAuthenticationBundle\Security\Http\EntryPoint;

use Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationFailureResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;

Expand All @@ -19,15 +19,7 @@ class JWTEntryPoint implements AuthenticationEntryPointInterface
*/
public function start(Request $request, AuthenticationException $authException = null)
{
$statusCode = 401;

$data = [
'code' => $statusCode,
'message' => 'Invalid credentials',
];

$response = new JsonResponse($data, $statusCode);
$response->headers->set('WWW-Authenticate', 'Bearer');
$response = new JWTAuthenticationFailureResponse();

return $response;
}
Expand Down
30 changes: 30 additions & 0 deletions Tests/Response/JWTAuthenticationFailureResponseTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace Lexik\Bundle\JWTAuthenticationBundle\Tests\Response;

use Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationFailureResponse;

/**
* Tests the JWTAuthenticationFailureResponse
*
* @author Robin Chalas <[email protected]>
*/
final class JWTAuthenticationFailureResponseTest extends \PHPUnit_Framework_TestCase
{
public function testResponse()
{
$expected = [
'code' => 401,
'message' => 'message',
];

$response = new JWTAuthenticationFailureResponse($expected['message']);

$this->assertSame($expected['message'], $response->getMessage());
$this->assertSame($expected['code'], $response->getStatusCode());
$this->assertSame('Bearer', $response->headers->get('WWW-Authenticate'));
$this->assertSame(json_encode($expected), $response->getContent());

return $response;
}
}
48 changes: 48 additions & 0 deletions Tests/Response/JWTAuthenticationSuccessResponseTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace Lexik\Bundle\JWTAuthenticationBundle\Tests\Response;

use Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationSuccessResponse;

/**
* Tests the JWTAuthenticationSuccessResponse.
*
* @author Robin Chalas <[email protected]>
*/
final class JWTAuthenticationSuccessResponseTest extends \PHPUnit_Framework_TestCase
{
public function testResponse()
{
$extraData = [
'username' => 'foobar',
'email' => '[email protected]'
];
$expected = ['token' => 'jwt'] + $extraData;

$response = new JWTAuthenticationSuccessResponse($expected['token'], $extraData);

$this->assertSame($expected['token'], $response->getToken());
$this->assertSame(200, $response->getStatusCode());
$this->assertSame($extraData, $response->getExtraData());

$this->assertSame(json_encode($expected), $response->getContent());

return $response;
}

/**
* @depends testResponse
*/
public function testReplaceData(JWTAuthenticationSuccessResponse $response)
{
$replacementData = ['foo' => 'bar'];
$response->setData($replacementData);

// Test that the previous method call has no effect on the original body
$this->assertNotEquals(json_encode($replacementData), $response->getContent());
$this->assertSame(
json_encode(['token' => $response->getToken()] + $response->getExtraData()),
$response->getContent()
);
}
}
6 changes: 2 additions & 4 deletions Tests/Security/Authentication/Firewall/JWTListenerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Lexik\Bundle\JWTAuthenticationBundle\Tests\Security\Authentication\Firewall;

use Lexik\Bundle\JWTAuthenticationBundle\Security\Firewall\JWTListener;
use Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationFailureResponse;

/**
* JWTListenerTest
Expand Down Expand Up @@ -60,10 +61,7 @@ public function testHandle()
$event
->expects($this->once())
->method('setResponse')
->with(new \Symfony\Component\HttpFoundation\JsonResponse([
'code' => 401,
'message' => $invalidTokenException->getMessage(),
], 401, ['WWW-Authenticate' => 'Bearer']));
->with(new JWTAuthenticationFailureResponse($invalidTokenException->getMessage()));

$listener->handle($event);
}
Expand Down
2 changes: 1 addition & 1 deletion Tests/Security/Http/EntryPoint/JWTEntryPointTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public function testStart()

$data = json_decode($response->getContent(), true);
$this->assertEquals($data['code'], '401');
$this->assertEquals($data['message'], 'Invalid credentials');
$this->assertEquals($data['message'], 'Bad credentials');
}

/**
Expand Down

0 comments on commit 9069a5f

Please sign in to comment.