Browse Source

Implement IndieAuth discovery

microsub
J. King 5 years ago
parent
commit
02330759b4
  1. 19
      docs/en/030_Supported_Protocols/040_Microsub.md
  2. 31
      lib/REST.php
  3. 57
      lib/REST/Microsub/Auth.php

19
docs/en/030_Supported_Protocols/040_Microsub.md

@ -0,0 +1,19 @@
[TOC]
# About
<dl>
<dt>Supported since</dt>
<dd>0.9.0</dd>
<dt>Base URL</dt>
<dd>/</dd>
<dt>API endpoint</dt>
<dd>/u/<var>username</var></dd>
<dd>/microsub</dd>
<dt>Specifications</dt>
<dd><a href="https://indieweb.org/Microsub-spec">Microsub</a>, <a href="https://indieauth.spec.indieweb.org/">IndieAuth</a></dd>
</dl>
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.

31
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/

57
lib/REST/Microsub/Auth.php

@ -0,0 +1,57 @@
<?php
/** @license MIT
* Copyright 2017 J. King, Dustin Wilson et al.
* See LICENSE and AUTHORS files for details */
declare(strict_types=1);
namespace JKingWeb\Arsse\REST\Microsub;
use JKingWeb\Arsse\Misc\URL;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Response\HtmlResponse as Response;
use Zend\Diactoros\Response\EmptyResponse;
class Auth extends \JKingWeb\Arsse\REST\AbstractHandler {
public function __construct() {
}
public function dispatch(ServerRequestInterface $req): ResponseInterface {
// ensure that a user name is specified in the path
// if the path is empty or contains a slash, this is not a URL we handle
$id = parse_url($req->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 = '<meta charset="UTF-8"><link rel="authorization_endpoint" href="'.htmlspecialchars($urlAuth).'"><link rel="token_endpoint" href="'.htmlspecialchars($urlToken).'"><link rel="microsub" href="'.htmlspecialchars($urlService).'">';
return new Response($html, 200, [
"Link: <$urlAuth>; rel=\"authorization_endpoint\"",
"Link: <$urlToken>; rel=\"token_endpoint\"",
"Link: <$urlService>; rel=\"microsub\"",
]);
}
}
Loading…
Cancel
Save