_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'); }