API: expect JWT in the Authorization header

Relates to https://github.com/shaarli/Shaarli/pull/731

Added:
- require the presence of the 'Authorization' header

Changed:
- use the HTTP Bearer Token authorization schema

See:
- https://jwt.io/introduction/#how-do-json-web-tokens-work-
- https://tools.ietf.org/html/rfc6750
- http://security.stackexchange.com/q/108662

Signed-off-by: VirtualTam <virtualtam@flibidi.net>
This commit is contained in:
VirtualTam 2017-01-07 22:23:47 +01:00 committed by VirtualTam
parent 37ab940599
commit 63ef549749
2 changed files with 34 additions and 6 deletions

View file

@ -98,8 +98,7 @@ protected function checkRequest($request)
* @throws ApiAuthorizationException The token couldn't be validated. * @throws ApiAuthorizationException The token couldn't be validated.
*/ */
protected function checkToken($request) { protected function checkToken($request) {
$jwt = $request->getHeaderLine('jwt'); if (! $request->hasHeader('Authorization')) {
if (empty($jwt)) {
throw new ApiAuthorizationException('JWT token not provided'); throw new ApiAuthorizationException('JWT token not provided');
} }
@ -107,7 +106,13 @@ protected function checkToken($request) {
throw new ApiAuthorizationException('Token secret must be set in Shaarli\'s administration'); throw new ApiAuthorizationException('Token secret must be set in Shaarli\'s administration');
} }
ApiUtils::validateJwtToken($jwt, $this->conf->get('api.secret')); $authorization = $request->getHeaderLine('Authorization');
if (! preg_match('/^Bearer (.*)/i', $authorization, $matches)) {
throw new ApiAuthorizationException('Invalid JWT header');
}
ApiUtils::validateJwtToken($matches[1], $this->conf->get('api.secret'));
} }
/** /**

View file

@ -143,7 +143,7 @@ public function testInvokeMiddlewareNoSecretSetDebug()
$env = Environment::mock([ $env = Environment::mock([
'REQUEST_METHOD' => 'GET', 'REQUEST_METHOD' => 'GET',
'REQUEST_URI' => '/echo', 'REQUEST_URI' => '/echo',
'HTTP_JWT'=> 'jwt', 'HTTP_AUTHORIZATION'=> 'Bearer jwt',
]); ]);
$request = Request::createFromEnvironment($env); $request = Request::createFromEnvironment($env);
$response = new Response(); $response = new Response();
@ -157,7 +157,30 @@ public function testInvokeMiddlewareNoSecretSetDebug()
} }
/** /**
* Invoke the middleware without an invalid JWT token (debug): * Invoke the middleware with an invalid JWT token header
*/
public function testInvalidJwtAuthHeaderDebug()
{
$this->conf->set('dev.debug', true);
$mw = new ApiMiddleware($this->container);
$env = Environment::mock([
'REQUEST_METHOD' => 'GET',
'REQUEST_URI' => '/echo',
'HTTP_AUTHORIZATION'=> 'PolarBearer jwt',
]);
$request = Request::createFromEnvironment($env);
$response = new Response();
/** @var Response $response */
$response = $mw($request, $response, null);
$this->assertEquals(401, $response->getStatusCode());
$body = json_decode((string) $response->getBody());
$this->assertEquals('Not authorized: Invalid JWT header', $body->message);
$this->assertContains('ApiAuthorizationException', $body->stacktrace);
}
/**
* Invoke the middleware with an invalid JWT token (debug):
* should return a 401 error Unauthorized - with a specific message and a stacktrace. * should return a 401 error Unauthorized - with a specific message and a stacktrace.
* *
* Note: specific JWT errors tests are handled in ApiUtilsTest. * Note: specific JWT errors tests are handled in ApiUtilsTest.
@ -169,7 +192,7 @@ public function testInvokeMiddlewareInvalidJwtDebug()
$env = Environment::mock([ $env = Environment::mock([
'REQUEST_METHOD' => 'GET', 'REQUEST_METHOD' => 'GET',
'REQUEST_URI' => '/echo', 'REQUEST_URI' => '/echo',
'HTTP_JWT'=> 'bad jwt', 'HTTP_AUTHORIZATION'=> 'Bearer jwt',
]); ]);
$request = Request::createFromEnvironment($env); $request = Request::createFromEnvironment($env);
$response = new Response(); $response = new Response();