Browse Source

use IndieAuth Client instead of doing it manually

still shows debug messages when missing endpoints to help people get started
pull/108/head
Aaron Parecki 6 years ago
parent
commit
acc8a8e673
No known key found for this signature in database GPG Key ID: 276C2817346D6056
  1. 156
      controllers/auth.php

156
controllers/auth.php

@ -1,27 +1,65 @@
<?php <?php
use Abraham\TwitterOAuth\TwitterOAuth; use Abraham\TwitterOAuth\TwitterOAuth;
function buildRedirectURI() {
return Config::$base_url . 'auth/callback';
}
IndieAuth\Client::$clientID = Config::$base_url;
IndieAuth\Client::$redirectURL = Config::$base_url.'auth/callback';
$app->get('/auth/start', function() use($app) { $app->get('/auth/start', function() use($app) {
$req = $app->request(); $req = $app->request();
$params = $req->params(); $params = $req->params();
// the "me" parameter is user input, and may be in a couple of different forms:
// aaronparecki.com http://aaronparecki.com http://aaronparecki.com/
if(!array_key_exists('me', $params) || !($me = IndieAuth\Client::normalizeMeURL($params['me']))) {
$defaultScope = 'create update media';
list($authorizationURL, $error) = IndieAuth\Client::begin($params['me'], $defaultScope);
// Double check for a micropub endpoint here for debugging purposes
if(!$error) {
$me = $_SESSION['indieauth_url']; // set by IndieAuth\Client::begin(), will be the normalized and resolved URL
$micropubEndpoint = $_SESSION['indieauth']['micropub_endpoint'] = IndieAuth\Client::discoverMicropubEndpoint($me);
if(!$micropubEndpoint) {
$error['error'] = 'missing_micropub_endpoint';
}
}
if($error && in_array($error['error'], ['missing_authorization_endpoint','missing_token_endpoint','missing_micropub_endpoint'])) {
// Display debug info for these particular errors
$me = $_SESSION['indieauth_url']; // set by IndieAuth\Client::begin(), will be the normalized and resolved URL
$micropubEndpoint = $_SESSION['indieauth']['micropub_endpoint'] = IndieAuth\Client::discoverMicropubEndpoint($me);
$tokenEndpoint = $_SESSION['indieauth']['token_endpoint'] = IndieAuth\Client::discoverTokenEndpoint($me);
$authorizationEndpoint = $_SESSION['indieauth']['authorization_endpoint'] = IndieAuth\Client::discoverAuthorizationEndpoint($me);
$html = render('auth_start', array(
'title' => 'Sign In',
'me' => $me,
'authorizing' => $me,
'meParts' => parse_url($me),
'tokenEndpoint' => $tokenEndpoint,
'micropubEndpoint' => $micropubEndpoint,
'authorizationEndpoint' => $authorizationEndpoint,
'authorizationURL' => false
));
$app->response()->body($html);
return;
}
// Handle other errors like connection errors by showing a generic error page
if($error) {
$html = render('auth_error', array( $html = render('auth_error', array(
'title' => 'Sign In', 'title' => 'Sign In',
'error' => 'Invalid "me" Parameter',
'errorDescription' => 'The URL you entered, "<strong>' . $params['me'] . '</strong>" is not valid.'
'error' => $error['error'],
'errorDescription' => $error['error_description'],
)); ));
$app->response()->body($html); $app->response()->body($html);
return; return;
} }
$me = $_SESSION['indieauth_url']; // set by IndieAuth\Client::begin(), will be the normalized and resolved URL
$micropubEndpoint = $_SESSION['indieauth']['micropub_endpoint'] = IndieAuth\Client::discoverMicropubEndpoint($me);
$tokenEndpoint = $_SESSION['indieauth']['token_endpoint'] = IndieAuth\Client::discoverTokenEndpoint($me);
$authorizationEndpoint = $_SESSION['indieauth']['authorization_endpoint'] = IndieAuth\Client::discoverAuthorizationEndpoint($me);
if(k($params, 'redirect')) { if(k($params, 'redirect')) {
$_SESSION['redirect_after_login'] = $params['redirect']; $_SESSION['redirect_after_login'] = $params['redirect'];
} }
@ -29,26 +67,6 @@ $app->get('/auth/start', function() use($app) {
$_SESSION['reply'] = $params['reply']; $_SESSION['reply'] = $params['reply'];
} }
$_SESSION['attempted_me'] = $me;
$_SESSION['indieauth'] = [
'authorization_endpoint' => ($authorizationEndpoint=IndieAuth\Client::discoverAuthorizationEndpoint($me)),
'token_endpoint' => ($tokenEndpoint=IndieAuth\Client::discoverTokenEndpoint($me)),
'micropub_endpoint' => ($micropubEndpoint=IndieAuth\Client::discoverMicropubEndpoint($me)),
];
$defaultScope = 'create update media';
if($tokenEndpoint && $micropubEndpoint && $authorizationEndpoint) {
// Generate a "state" parameter for the request
$state = IndieAuth\Client::generateStateParameter();
$_SESSION['auth_state'] = $state;
$authorizationURL = IndieAuth\Client::buildAuthorizationURL($authorizationEndpoint, $me, buildRedirectURI(), Config::$base_url, $state, $defaultScope);
} else {
$authorizationURL = false;
}
// If the user has already signed in before and has a micropub access token, // If the user has already signed in before and has a micropub access token,
// and the endpoints are all the same, skip the debugging screens and redirect // and the endpoints are all the same, skip the debugging screens and redirect
// immediately to the auth endpoint. // immediately to the auth endpoint.
@ -60,13 +78,6 @@ $app->get('/auth/start', function() use($app) {
&& $user->authorization_endpoint == $authorizationEndpoint && $user->authorization_endpoint == $authorizationEndpoint
&& !array_key_exists('restart', $params)) { && !array_key_exists('restart', $params)) {
// TODO: fix this by caching the endpoints maybe in the session instead of writing them to the DB here.
// Then remove the line below that blanks out the access token
$user->micropub_endpoint = $micropubEndpoint;
$user->authorization_endpoint = $authorizationEndpoint;
$user->token_endpoint = $tokenEndpoint;
$user->save();
// Request whatever scope was previously granted // Request whatever scope was previously granted
$authorizationURL = parse_url($authorizationURL); $authorizationURL = parse_url($authorizationURL);
$authorizationURL['scope'] = $user->micropub_scope; $authorizationURL['scope'] = $user->micropub_scope;
@ -104,6 +115,8 @@ $app->get('/auth/redirect', function() use($app) {
$req = $app->request(); $req = $app->request();
$params = $req->params(); $params = $req->params();
// Override scope from the form the user selects
if(!isset($params['scope'])) if(!isset($params['scope']))
$params['scope'] = ''; $params['scope'] = '';
@ -121,87 +134,28 @@ $app->get('/auth/callback', function() use($app) {
$req = $app->request(); $req = $app->request();
$params = $req->params(); $params = $req->params();
// If there is no state in the session, start the login again
if(!array_key_exists('auth_state', $_SESSION)) {
$html = render('auth_error', array(
'title' => 'Auth Callback',
'error' => 'Missing session state',
'errorDescription' => 'Something went wrong, please try signing in again, and make sure cookies are enabled for this domain.'
));
$app->response()->body($html);
return;
}
if(!array_key_exists('code', $params) || trim($params['code']) == '') {
$html = render('auth_error', array(
'title' => 'Auth Callback',
'error' => 'Missing authorization code',
'errorDescription' => 'No authorization code was provided in the request.'
));
$app->response()->body($html);
return;
}
// Verify the state came back and matches what we set in the session
// Should only fail for malicious attempts, ok to show a not as nice error message
if(!array_key_exists('state', $params)) {
$html = render('auth_error', array(
'title' => 'Auth Callback',
'error' => 'Missing state parameter',
'errorDescription' => 'No state parameter was provided in the request. This shouldn\'t happen. It is possible this is a malicious authorization attempt, or your authorization server failed to pass back the "state" parameter.'
));
$app->response()->body($html);
return;
}
if($params['state'] != $_SESSION['auth_state']) {
$html = render('auth_error', array(
'title' => 'Auth Callback',
'error' => 'Invalid state',
'errorDescription' => 'The state parameter provided did not match the state provided at the start of authorization. This is most likely caused by a malicious authorization attempt.'
));
$app->response()->body($html);
return;
}
list($token, $error) = IndieAuth\Client::complete($params, true);
if(!isset($_SESSION['attempted_me'])) {
if($error) {
$html = render('auth_error', [ $html = render('auth_error', [
'title' => 'Auth Callback', 'title' => 'Auth Callback',
'error' => 'Missing data',
'errorDescription' => 'We forgot who was logging in. It\'s possible you took too long to finish signing in, or something got mixed up by signing in in another tab.'
'error' => $error['error'],
'errorDescription' => $error['error_description'],
]); ]);
$app->response()->body($html); $app->response()->body($html);
return; return;
} }
$me = $_SESSION['attempted_me'];
// Now the basic sanity checks have passed. Time to start providing more helpful messages when there is an error.
// An authorization code is in the query string, and we want to exchange that for an access token at the token endpoint.
$me = $token['me'];
// Discover the endpoints
// Use the discovered endpoints saved in the session
$micropubEndpoint = $_SESSION['indieauth']['micropub_endpoint']; $micropubEndpoint = $_SESSION['indieauth']['micropub_endpoint'];
$tokenEndpoint = $_SESSION['indieauth']['token_endpoint']; $tokenEndpoint = $_SESSION['indieauth']['token_endpoint'];
if($tokenEndpoint) {
$token = IndieAuth\Client::getAccessToken($tokenEndpoint, $params['code'], $me, buildRedirectURI(), Config::$base_url, true);
} else {
$token = array('auth'=>false, 'response'=>false);
}
$redirectToDashboardImmediately = false; $redirectToDashboardImmediately = false;
// If a valid access token was returned, store the token info in the session and they are signed in // If a valid access token was returned, store the token info in the session and they are signed in
if(k($token['auth'], array('me','access_token','scope'))) { if(k($token['auth'], array('me','access_token','scope'))) {
// Double check that the domain of the returned "me" matches the expected
if(!\p3k\url\host_matches($token['auth']['me'], $me)) {
$html = render('auth_error', [
'title' => 'Error Signing In',
'error' => 'Invalid user',
'errorDescription' => 'The user URL that was returned from the token endpoint (<code>'.$token['auth']['me'].'</code>) did not match the domain of the user signing in (<code>'.$me.'</code>).'
]);
$app->response()->body($html);
return;
}
$_SESSION['auth'] = $token['auth']; $_SESSION['auth'] = $token['auth'];
$_SESSION['me'] = $me = $token['auth']['me']; $_SESSION['me'] = $me = $token['auth']['me'];
@ -233,8 +187,6 @@ $app->get('/auth/callback', function() use($app) {
get_micropub_config($user, ['q'=>'config']); get_micropub_config($user, ['q'=>'config']);
} }
unset($_SESSION['auth_state']);
unset($_SESSION['attempted_me']);
unset($_SESSION['indieauth']); unset($_SESSION['indieauth']);
if($redirectToDashboardImmediately || k($_SESSION, 'dontask')) { if($redirectToDashboardImmediately || k($_SESSION, 'dontask')) {

Loading…
Cancel
Save