diff --git a/docs/en/030_Supported_Protocols/040_Microsub.md b/docs/en/030_Supported_Protocols/040_Microsub.md new file mode 100644 index 00000000..f7c659bf --- /dev/null +++ b/docs/en/030_Supported_Protocols/040_Microsub.md @@ -0,0 +1,19 @@ +[TOC] + +# About + +
+
Supported since
+
0.9.0
+
Base URL
+
/
+
API endpoint
+
/u/username
+
/microsub
+
Specifications
+
Microsub, IndieAuth
+
+ +The Microsub protocol is a facet of the [IndieWeb movement](https://indieweb.org/), and unlike other protocols is designed as a first-class interface between news-reading clients and servers. + +As IndieWeb technology is sprawling and complex, The Arsse only implements enough so that Microsub clients can be used with The Arsse. Consequently The Arsse does not function as a generic IndieAuth authorizer, will not work with arbitrary IndieWeb identifier URLs, does not implement any Micropub functionality, and so on. diff --git a/lib/REST.php b/lib/REST.php index 989205f4..fa2b1ec4 100644 --- a/lib/REST.php +++ b/lib/REST.php @@ -17,32 +17,41 @@ use Zend\Diactoros\Response\EmptyResponse; class REST { const API_LIST = [ 'ncn' => [ // NextCloud News version enumerator - 'match' => '/index.php/apps/news/api', - 'strip' => '/index.php/apps/news/api', + 'match' => "/index.php/apps/news/api", + 'strip' => "/index.php/apps/news/api", 'class' => REST\NextCloudNews\Versions::class, ], 'ncn_v1-2' => [ // NextCloud News v1-2 https://github.com/nextcloud/news/blob/master/docs/externalapi/Legacy.md - 'match' => '/index.php/apps/news/api/v1-2/', - 'strip' => '/index.php/apps/news/api/v1-2', + 'match' => "/index.php/apps/news/api/v1-2/", + 'strip' => "/index.php/apps/news/api/v1-2", 'class' => REST\NextCloudNews\V1_2::class, ], 'ttrss_api' => [ // Tiny Tiny RSS https://git.tt-rss.org/git/tt-rss/wiki/ApiReference - 'match' => '/tt-rss/api', - 'strip' => '/tt-rss/api', + 'match' => "/tt-rss/api", + 'strip' => "/tt-rss/api", 'class' => REST\TinyTinyRSS\API::class, ], 'ttrss_icon' => [ // Tiny Tiny RSS feed icons - 'match' => '/tt-rss/feed-icons/', - 'strip' => '/tt-rss/feed-icons/', + 'match' => "/tt-rss/feed-icons/", + 'strip' => "/tt-rss/feed-icons/", 'class' => REST\TinyTinyRSS\Icon::class, ], 'fever' => [ // Fever https://web.archive.org/web/20161217042229/https://feedafever.com/api - 'match' => '/fever/', - 'strip' => '/fever/', + 'match' => "/fever/", + 'strip' => "/fever/", 'class' => REST\Fever\API::class, ], + 'microsub' => [ // Microsub https://indieweb.org/Microsub + 'match' => "/microsub", + 'strip' => "", + 'class' => REST\Microsub\API::class, + ], + 'microsub_auth' => [ // IndieAuth for Microsub https://indieauth.spec.indieweb.org/ + 'match' => "/u/", + 'strip' => "/u/", + 'class' => REST\Microsub\Auth::class, + ], // Other candidates: - // Microsub https://indieweb.org/Microsub // Google Reader http://feedhq.readthedocs.io/en/latest/api/index.html // Feedbin v2 https://github.com/feedbin/feedbin-api // CommaFeed https://www.commafeed.com/api/ diff --git a/lib/REST/Microsub/Auth.php b/lib/REST/Microsub/Auth.php new file mode 100644 index 00000000..c4e7f07b --- /dev/null +++ b/lib/REST/Microsub/Auth.php @@ -0,0 +1,57 @@ +getRequestTarget())['path'] ?? ""; + if (!strlen($id) || strpos($id, "/") !== false) { + return new EmptyResponse(404); + } + $id = rawurldecode($id); + // gather the query parameters and act on the "proc" parameter + $method = "do".ucfirst(strtolower($req->getQueryParams()['proc'] ?? "discovery")); + if (!method_exists($this, $method)) { + return new EmptyResponse(404); + } else { + return $this->$method($id, $req); + } + } + + protected function doDiscovery(string $user, ServerRequestInterface $req): ResponseInterface { + // construct the base user identifier URL; the user is never checked against the database + // as this route is publicly accessible, for reasons of privacy requests for user discovery work regardless of whether the user exists + $s = $req->getServerParams(); + $https = (strlen($s['HTTPS'] ?? "") && $s['HTTPS'] !== "off"); + $port = (int) $s['SERVER_PORT']; + $port = (!$port || ($https && $port == 443) || (!$https && $port == 80)) ? "" : ":$port"; + $base = URL::normalize(($https ? "https" : "http")."://".$s['HTTP_HOST'].$port."/"); + $id = $base."u/".rawurlencode($user); + // prepare authroizer, token, and Microsub endpoint URLs + $urlAuth = $id."?proc=login"; + $urlToken = $id."?proc=issue"; + $urlService = $base."microsub"; + // output an extremely basic identity resource + $html = ''; + return new Response($html, 200, [ + "Link: <$urlAuth>; rel=\"authorization_endpoint\"", + "Link: <$urlToken>; rel=\"token_endpoint\"", + "Link: <$urlService>; rel=\"microsub\"", + ]); + } +}