function getContent() { $this->gatekeeper(); $user = Idno::site()->session()->currentUser(); $code = $this->getInput('code'); $state = $this->getInput('state'); $me = $this->getInput('me'); $token_endpoint = IndieAuthClient::discoverTokenEndpoint($me); $micropub_endpoint = IndieAuthClient::discoverMicropubEndpoint($me); $hcard = IndieAuthClient::representativeHCard($me); $client_id = Idno::site()->config()->getDisplayURL(); $redirect_uri = Idno::site()->config()->getDisplayURL() . 'account/indiesyndicate/cb'; $result = IndieAuthClient::getAccessToken($token_endpoint, $code, $me, $redirect_uri, $client_id, $state); if (isset($result['me']) && isset($result['access_token'])) { $me = $result['me']; $token = $result['access_token']; $name = $me; if (!empty($hcard['properties']['name'])) { $name = $hcard['properties']['name'][0]; } else { $name = $me; } $user->indiesyndicate[$me] = ['name' => $name, 'access_token' => $token, 'micropub_endpoint' => $micropub_endpoint, 'method' => 'micropub']; $user->save(); Idno::site()->session()->addMessage('Successfully authorized ' . $me); } else { Idno::site()->session()->addErrorMessage('Authorization was declined or failed for ' . $me); } $this->forward(Idno::site()->config()->getDisplayURL() . 'account/indiesyndicate'); }
function postContent() { $this->gatekeeper(); $user = Idno::site()->session()->currentUser(); $url = $this->getInput('url'); if ($micropub_endpoint = IndieAuthClient::discoverMicropubEndpoint($url)) { $auth_endpoint = IndieAuthClient::discoverAuthorizationEndpoint($url); $client_id = Idno::site()->config()->getDisplayURL(); $redirect_uri = Idno::site()->config()->getDisplayURL() . 'account/indiesyndicate/cb'; $auth_url = IndieAuthClient::buildAuthorizationURL($auth_endpoint, $url, $redirect_uri, $client_id, 'TODO123', 'post'); $this->forward($auth_url); } else { $mention_client = new MentionClient(); if ($webmention_endpoint = $mention_client->discoverWebmentionEndpoint($url)) { $name = $this->parseTitle($url); $user->indiesyndicate[$url] = ['name' => $name ? $name : $url, 'method' => 'webmention']; $user->save(); Idno::site()->session()->addMessage('Added webmention target ' . $me); } else { Idno::site()->session()->addMessage('Endpoint does not appear to support Micropub or Webmention: ' . $me); } $this->forward(Idno::site()->config()->getDisplayURL() . 'account/indiesyndicate'); } }
/** * Token Server * * Create a micropub server app + token provider — creates token-providing and authorizing endpoints, allows users to * authorize other apps to make requests to this one on their behalf, authenticates requests based on access tokens. * $dataToToken and $dataFromToken map the access token granted to+used by apps and a user+client id+granted scopes * * Adds routes: * /token — clients POST to this URL with a bunch of details, including encrypted state, to gain an access token. * * Adds ->before() handler which attaches data about the current user, which app they’re using, what scopes they’re * granted on this server to $request. * * @param \Silex\Application $app * @param callable|null $dataToToken * @param callable|null $dataFromToken * @return \Symfony\Component\Routing\RouteCollection */ function server($app, $dataToToken = null, $dataFromToken = null) { $auth = $app['controllers_factory']; if ($dataToToken === null) { $dataToToken = function ($data) use($app) { return $app['encryption']->encrypt($data); }; } if ($dataFromToken === null) { $dataFromToken = function ($token) use($app) { return $app['encryption']->decrypt($token); }; } $auth->post('/token/', function (Http\Request $request) use($app, $dataToToken) { $f = $request->request; $me = $f->get('me'); $code = $f->get('code'); $clientId = $f->get('client_id'); $redirectUri = $f->get('redirect_uri'); $state = $f->get('state'); // TODO: handle this being false. $authorizationEndpoint = IndieAuth\Client::discoverAuthorizationEndpoint(ensureUrlHasHttp($me)); $auth = IndieAuth\Client::verifyIndieAuthCode($authorizationEndpoint, $code, $me, $redirectUri, $clientId, $state); if (isset($auth['error'])) { $app['logger']->warning('Got an error whilst verifying an authorization token', ['error' => $auth['error'], 'description' => $auth['error_description'], 'authorizationEndpoint' => $authorizationEndpoint]); } $tokenData = ['date_issued' => date(DateTime::W3C), 'me' => $auth['me'], 'client_id' => $clientId, 'scope' => isset($auth['scope']) ? $auth['scope'] : '', 'nonce' => mt_rand(1000000, pow(2, 31))]; $token = $dataToToken($tokenData); return Http\Response::create(http_build_query(['me' => $tokenData['me'], 'scope' => $tokenData['scope'], 'access_token' => $token]), 200, ['Content-type' => 'application/x-www-form-urlencoded']); })->bind('indieauth.token'); // TODO: this needs to look in auth headers as well as in the request body $app->before(function ($request) use($app, $dataFromToken) { if ($request->request->has('access_token')) { $app['logger']->info('Authenticating using access token'); // The user is authenticating using the full indieauth flow. // This request is presumably coming from an app which the user has authorized with some access to this site. $tokenStr = $request->request->get('access_token'); try { // $token also contains information e.g. the scopes the current user has. // Client app filtering could also be done here. $token = $dataFromToken($tokenStr); // This token has no particularly sensitive information in so can be logged as-is. $app['logger']->info('Access token decrypted to', ['data' => $token]); $request->attributes->set('indieauth.server.token', $token); } catch (Exception $e) { $app['logger']->warning("Caught an unhandled exception whilst executing \$dataFromToken — consider updating your handler to deal with these appropriately.", ['exception class' => get_class($e), 'message' => $e->getMessage()]); } } }); return $auth; }