Files
OSJ/lib/pkp/classes/security/authorization/internal/ApiTokenDecodingMiddleware.php
T
CHIEFSOFT\ameye df3a033196 first commit
2024-06-08 17:09:23 -04:00

172 lines
5.2 KiB
PHP

<?php
/**
* @file classes/security/authorization/internal/ApiTokenDecodingMiddleware.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class ApiTokenDecodingMiddleware
*
* @ingroup security_authorization
*
* @brief Slim middleware which decodes and validates JSON Web Tokens
*/
namespace PKP\security\authorization\internal;
use Exception;
use Firebase\JWT\JWT;
use Firebase\JWT\SignatureInvalidException;
use PKP\config\Config;
use PKP\core\APIResponse;
use PKP\handler\APIHandler;
use Slim\Http\Request as SlimRequest;
class ApiTokenDecodingMiddleware
{
/** @var APIHandler $handler Reference to api handler */
protected $_handler = null;
/**
* Constructor
*
*/
public function __construct(APIHandler $handler)
{
$this->_handler = $handler;
}
/**
* Decodes the request's JSON Web Token
*
* @param SlimRequest $slimRequest
*
* @return bool|string
*/
protected function _decode($slimRequest)
{
try {
$jwt = $this->getJWT($slimRequest);
} catch (Exception $e) {
$request = $this->_handler->getRequest();
return $request->getRouter()
->handleAuthorizationFailure(
$request,
$e->getMessage()
);
}
if (!$jwt) {
/**
* If we don't have a token, it's for the authentication logic to handle if it's a problem.
*/
return true;
}
$secret = Config::getVar('security', 'api_key_secret', '');
if (!$secret) {
$request = $this->_handler->getRequest();
return $request->getRouter()
->handleAuthorizationFailure(
$request,
'api.500.apiSecretKeyMissing'
);
}
try {
$apiToken = JWT::decode($jwt, $secret, ['HS256']);
/**
* Compatibility with old API keys
*
* @link https://github.com/pkp/pkp-lib/issues/6462
*/
if (substr($apiToken, 0, 2) === '""') {
$apiToken = json_decode($apiToken);
}
$this->_handler->setApiToken($apiToken);
} catch (Exception $e) {
/**
* If JWT decoding fails, it throws an 'UnexpectedValueException'.
* If JSON decoding fails (of the JWT payload), it throws a 'DomainException'.
* If token couldn't verified, it throws a 'SignatureInvalidException'.
*/
if ($e instanceof SignatureInvalidException) {
$request = $this->_handler->getRequest();
return $request->getRouter()
->handleAuthorizationFailure(
$request,
'api.400.invalidApiToken'
);
}
if ($e instanceof \UnexpectedValueException ||
$e instanceof \DomainException
) {
$request = $this->_handler->getRequest();
return $request->getRouter()
->handleAuthorizationFailure(
$request,
'api.400.tokenCouldNotBeDecoded',
[
'error' => $e->getMessage()
]
);
}
throw $e;
}
return true;
}
/**
* Middleware invokable function
*
* @param SlimRequest $request request
* @param APIResponse $response response
* @param callable $next Next middleware
*
* @return bool|string|APIResponse
*/
public function __invoke($request, $response, $next)
{
$result = $this->_decode($request);
if ($result !== true) {
return $result;
}
$response = $next($request, $response);
return $response;
}
protected function getJWT(SlimRequest $slimRequest)
{
$authHeader = $slimRequest->getHeader('Authorization');
if (!count($authHeader) || empty($authHeader[0])) {
// DEPRECATED as of 3.4.0. Use the Authorization header.
return $slimRequest->getQueryParam('apiToken');
}
// Several authorization methods may be supplied with commas between them.
// For example: Basic basic_auth_string_here, Bearer api_key_here
// JWT uses the Bearer scheme with an API key. Ignore the others.
$clauses = explode(',', $authHeader[0]);
foreach ($clauses as $clause) {
// Split the authorization scheme and parameters and look for the Bearer scheme.
$parts = explode(' ', trim($clause));
if (count($parts) == 2 && $parts[0] == 'Bearer') {
// Found bearer authorization; return the token.
return $parts[1];
}
}
return null;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\ApiTokenDecodingMiddleware', '\ApiTokenDecodingMiddleware');
}