From dd3e1432126d78e97b3f550d516740ae9ff47fae Mon Sep 17 00:00:00 2001 From: "J. King" Date: Tue, 10 Sep 2019 17:48:38 -0400 Subject: [PATCH] Complete IndieAuth authorizer --- lib/Misc/URL.php | 28 ++++++++++++++++++++++++++++ lib/REST/Microsub/Auth.php | 12 +++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/lib/Misc/URL.php b/lib/Misc/URL.php index 6b9fcce4..1f515ff6 100644 --- a/lib/Misc/URL.php +++ b/lib/Misc/URL.php @@ -6,10 +6,18 @@ declare(strict_types=1); namespace JKingWeb\Arsse\Misc; +use function GuzzleHttp\Psr7\parse_header; + /** * A collection of functions for manipulating URLs */ class URL { + + /** Returns whether a URL is absolute i.e. has a scheme */ + public static function absolute(string $url): bool { + return (bool) strlen((string) parse_url($url, \PHP_URL_SCHEME)); + } + /** Normalizes a URL * * Normalizations performed are: @@ -137,4 +145,24 @@ class URL { $out = ($absolute ? "/" : "").$out.($index ? "/" : ""); return str_replace("//", "/", $out); } + + /** Appends data to a URL's query component + * + * @param string $url The input URL + * @param string $data The data to append. This should already be escaped where necessary and not start with any delimiter + * @param string $glue The query subcomponent delimiter, usually "&". If the URL has no query, "?" will be prepended instead + */ + public function queryAppend(string $url, string $data, string $glue = "&"): string { + $insPos = strpos($url, "#"); + $insPos = $insPos === false ? strlen($url) : $insPos; + $hasQuery = strpos($url, "?") !== false; + $glue = $hasQuery ? $glue : "?"; + if ($hasQuery && $insPos > 0) { + if ($url[$insPos - 1] === $glue) { + // if the URL already has excess glue, use it + $glue = ""; + } + } + return substr($url, 0, $insPos).$glue.$data.substr($url, $insPos); + } } diff --git a/lib/REST/Microsub/Auth.php b/lib/REST/Microsub/Auth.php index 087b50e7..4a85a113 100644 --- a/lib/REST/Microsub/Auth.php +++ b/lib/REST/Microsub/Auth.php @@ -8,6 +8,7 @@ namespace JKingWeb\Arsse\REST\Microsub; use JKingWeb\Arsse\Arsse; use JKingWeb\Arsse\Misc\URL; +use JKingWeb\Arsse\Misc\Date; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; use Zend\Diactoros\Response\HtmlResponse as Response; @@ -75,7 +76,16 @@ class Auth extends \JKingWeb\Arsse\REST\AbstractHandler { if ($user !== $id || URL::normalize($query['me']) !== $url) { return new EmptyResponse(403); } else { - // redirect + $redir = URL::normalize(rawurldecode($query['redirect_uri'])); + $state = $query['state'] ?? ""; + // check that the redirect URL is an absolute one + if (!URL::absolute($redir)) { + return new EmptyResponse(400); + } + // issue an authorization code and build the redirect URL + $code = Arsse::$db->tokenCreate($id, "microsub.auth", null, Date::add("PT2M")); + $next = URL::queryAppend($redir, "code=$code&state=$state"); + return new EmptyResponse(302, ["Location: $next"]); } } }