Skip to content

Commit b94205c

Browse files
committed
feature #894 Forward exception code to the response status code (theofidry)
This PR was squashed before being merged into the 2.x branch. Discussion ---------- Forward exception code to the response status code It is not rare for some exceptions to have their status code taken as an HTTP status code. This avoids to have to override/decorate the authentication handler to mutate the returned response Commits ------- 1e1e5ca Forward exception code to the response status code
2 parents ed81a4f + 1e1e5ca commit b94205c

File tree

2 files changed

+76
-1
lines changed

2 files changed

+76
-1
lines changed

Security/Http/Authentication/AuthenticationFailureHandler.php

+18-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Lexik\Bundle\JWTAuthenticationBundle\Events;
77
use Lexik\Bundle\JWTAuthenticationBundle\Response\JWTAuthenticationFailureResponse;
88
use Symfony\Component\HttpFoundation\Request;
9+
use Symfony\Component\HttpFoundation\Response;
910
use Symfony\Component\Security\Core\Exception\AuthenticationException;
1011
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
1112
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
@@ -33,13 +34,29 @@ public function __construct(EventDispatcherInterface $dispatcher)
3334
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
3435
{
3536
$errorMessage = strtr($exception->getMessageKey(), $exception->getMessageData());
37+
$statusCode = self::mapExceptionCodeToStatusCode($exception->getCode());
38+
3639
$event = new AuthenticationFailureEvent(
3740
$exception,
38-
new JWTAuthenticationFailureResponse($errorMessage)
41+
new JWTAuthenticationFailureResponse($errorMessage, $statusCode)
3942
);
4043

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

4346
return $event->getResponse();
4447
}
48+
49+
/**
50+
* @param string|int $exceptionCode
51+
*/
52+
private static function mapExceptionCodeToStatusCode($exceptionCode): int
53+
{
54+
$canMapToStatusCode = is_int($exceptionCode)
55+
&& $exceptionCode >= 400
56+
&& $exceptionCode < 500;
57+
58+
return $canMapToStatusCode
59+
? $exceptionCode
60+
: Response::HTTP_UNAUTHORIZED;
61+
}
4562
}

Tests/Security/Http/Authentication/AuthenticationFailureHandlerTest.php

+58
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Lexik\Bundle\JWTAuthenticationBundle\Tests\Security\Http\Authentication;
44

5+
use Exception;
56
use Lexik\Bundle\JWTAuthenticationBundle\Security\Http\Authentication\AuthenticationFailureHandler;
67
use PHPUnit\Framework\TestCase;
78
use Symfony\Component\Security\Core\Exception\AuthenticationException;
@@ -35,6 +36,63 @@ public function testOnAuthenticationFailure()
3536
$this->assertEquals($authenticationException->getMessageKey(), $content['message']);
3637
}
3738

39+
/**
40+
* test onAuthenticationFailure method.
41+
*/
42+
public function testOnAuthenticationFailureWithANonDefaultHttpFailureStatusCode()
43+
{
44+
$dispatcher = $this
45+
->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')
46+
->disableOriginalConstructor()
47+
->getMock();
48+
49+
$authenticationException = new AuthenticationException('', 403);
50+
51+
$handler = new AuthenticationFailureHandler($dispatcher);
52+
$response = $handler->onAuthenticationFailure($this->getRequest(), $authenticationException);
53+
$content = json_decode($response->getContent(), true);
54+
55+
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
56+
$this->assertEquals(403, $response->getStatusCode());
57+
$this->assertEquals(403, $content['code']);
58+
$this->assertEquals($authenticationException->getMessageKey(), $content['message']);
59+
}
60+
61+
/**
62+
* test onAuthenticationFailure method.
63+
*
64+
* @dataProvider nonHttpStatusCodeProvider
65+
*
66+
* @param string|int $nonHttpStatusCode
67+
*/
68+
public function testOnAuthenticationFailureWithANonHttpStatusCode($nonHttpStatusCode)
69+
{
70+
$dispatcher = $this
71+
->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')
72+
->disableOriginalConstructor()
73+
->getMock();
74+
75+
$authenticationException = new AuthenticationException('', $nonHttpStatusCode);
76+
77+
$handler = new AuthenticationFailureHandler($dispatcher);
78+
$response = $handler->onAuthenticationFailure($this->getRequest(), $authenticationException);
79+
$content = json_decode($response->getContent(), true);
80+
81+
$this->assertInstanceOf('Symfony\Component\HttpFoundation\JsonResponse', $response);
82+
$this->assertEquals(401, $response->getStatusCode());
83+
$this->assertEquals(401, $content['code']);
84+
$this->assertEquals($authenticationException->getMessageKey(), $content['message']);
85+
}
86+
87+
public static function nonHttpStatusCodeProvider(): iterable
88+
{
89+
yield 'server error HTTP status code' => [500];
90+
yield 'redirection HTTP status code' => [500];
91+
yield 'success HTTP status code' => [500];
92+
yield 'non HTTP status code' => [1302];
93+
yield 'default status code' => [0];
94+
}
95+
3896
/**
3997
* @return \PHPUnit_Framework_MockObject_MockObject
4098
*/

0 commit comments

Comments
 (0)