diff --git a/src/Server.php b/src/Server.php index 40b62f2..f37369d 100644 --- a/src/Server.php +++ b/src/Server.php @@ -108,10 +108,13 @@ class Server { protected LoggerInterface $logger; + /** @var callable */ protected $httpGetWithEffectiveUrl; + /** @var callable */ protected $handleAuthenticationRequestCallback; + /** @var callable */ protected $handleNonIndieAuthRequest; protected string $exceptionTemplatePath; @@ -397,7 +400,7 @@ class Server { return new Response(200, [ 'content-type' => 'application/json', 'cache-control' => 'no-store', - ], json_encode(array_filter($tokenData, function ($k) { + ], json_encode(array_filter($tokenData, function (string $k) { // Prevent codes exchanged at the authorization endpoint from returning any information other than // me and profile. return in_array($k, ['me', 'profile']); @@ -438,7 +441,8 @@ class Server { // How most errors are handled depends on whether or not the request has a valid redirect_uri. In // order to know that, we need to also validate, fetch and parse the client_id. // If the request lacks a hash, or if the provided hash was invalid, perform the validation. - if (!array_key_exists(self::HASH_QUERY_STRING_KEY, $queryParams) || !hash_equals(hashAuthorizationRequestParameters($request, $this->secret), $queryParams[self::HASH_QUERY_STRING_KEY])) { + $currentRequestHash = hashAuthorizationRequestParameters($request, $this->secret); + if (is_null($currentRequestHash) or !hash_equals($currentRequestHash, $queryParams[self::HASH_QUERY_STRING_KEY])) { // All we need to know at this stage is whether the redirect_uri is valid. If it // sufficiently matches the client_id, we don’t (yet) need to fetch the client_id. if (!urlComponentsMatch($queryParams['client_id'], $queryParams['redirect_uri'], [PHP_URL_SCHEME, PHP_URL_HOST, PHP_URL_PORT])) { @@ -557,7 +561,7 @@ class Server { } $expectedHash = hashAuthorizationRequestParameters($request, $this->secret); - if (!hash_equals($expectedHash, $queryParams[self::HASH_QUERY_STRING_KEY])) { + if (is_null($expectedHash) or !hash_equals($expectedHash, $queryParams[self::HASH_QUERY_STRING_KEY])) { $this->logger->warning("The hash provided in the URL was invalid!", [ 'expected' => $expectedHash, 'actual' => $queryParams[self::HASH_QUERY_STRING_KEY] @@ -754,7 +758,7 @@ class Server { ], json_encode(array_merge([ // Ensure that the token_type key is present, if tokenStorage doesn’t include it. 'token_type' => 'Bearer' - ], array_filter($tokenData, function ($k) { + ], array_filter($tokenData, function (string $k) { // We should be able to trust the return data from tokenStorage, but there’s no harm in // preventing code_challenges from leaking, per OAuth2. return !in_array($k, ['code_challenge', 'code_challenge_method']); diff --git a/src/functions.php b/src/functions.php index 0ec294d..79fdb52 100644 --- a/src/functions.php +++ b/src/functions.php @@ -46,7 +46,7 @@ function hashAuthorizationRequestParameters(ServerRequestInterface $request, str $hashedParameters = $hashedParameters ?? ['client_id', 'redirect_uri', 'code_challenge', 'code_challenge_method']; $algo = $algo ?? 'sha256'; - $queryParams = $request->getQueryParams() ?? []; + $queryParams = $request->getQueryParams(); $data = ''; foreach ($hashedParameters as $key) { if (!array_key_exists($key, $queryParams)) { @@ -57,25 +57,25 @@ function hashAuthorizationRequestParameters(ServerRequestInterface $request, str return hash_hmac($algo, $data, $secret); } -function isIndieAuthAuthorizationCodeRedeemingRequest(ServerRequestInterface $request) { +function isIndieAuthAuthorizationCodeRedeemingRequest(ServerRequestInterface $request): bool { return strtolower($request->getMethod()) == 'post' && array_key_exists('grant_type', $request->getParsedBody() ?? []) && $request->getParsedBody()['grant_type'] == 'authorization_code'; } -function isIndieAuthAuthorizationRequest(ServerRequestInterface $request, $permittedMethods=['get']) { +function isIndieAuthAuthorizationRequest(ServerRequestInterface $request, array $permittedMethods=['get']): bool { return in_array(strtolower($request->getMethod()), array_map('strtolower', $permittedMethods)) && array_key_exists('response_type', $request->getQueryParams() ?? []) && $request->getQueryParams()['response_type'] == 'code'; } -function isAuthorizationApprovalRequest(ServerRequestInterface $request) { +function isAuthorizationApprovalRequest(ServerRequestInterface $request): bool { return strtolower($request->getMethod()) == 'post' && array_key_exists('taproot_indieauth_action', $request->getParsedBody() ?? []) && $request->getParsedBody()[Server::APPROVE_ACTION_KEY] == Server::APPROVE_ACTION_VALUE; } -function buildQueryString(array $parameters) { +function buildQueryString(array $parameters): string { $qs = []; foreach ($parameters as $k => $v) { $qs[] = urlencode($k) . '=' . urlencode($v); @@ -83,7 +83,7 @@ function buildQueryString(array $parameters) { return join('&', $qs); } -function urlComponentsMatch($url1, $url2, ?array $components=null): bool { +function urlComponentsMatch(string $url1, string $url2, ?array $components=null): bool { $validComponents = [PHP_URL_HOST, PHP_URL_PASS, PHP_URL_PATH, PHP_URL_PORT, PHP_URL_USER, PHP_URL_QUERY, PHP_URL_SCHEME, PHP_URL_FRAGMENT]; $components = $components ?? $validComponents;