setContent(view('login', [ 'title' => 'Sign In to Telegraph', 'return_to' => $request->get('return_to') ])); return $response; } public function login_start(Request $request, Response $response) { if(!$request->get('url') || !($me = IndieAuth\Client::normalizeMeURL($request->get('url')))) { $response->setContent(view('login', [ 'title' => 'Sign In to Telegraph', 'error' => 'Invalid URL', 'error_description' => 'The URL you entered, "' . htmlspecialchars($request->get('url')) . '" is not valid.' ])); return $response; } $authorizationEndpoint = IndieAuth\Client::discoverAuthorizationEndpoint($me); $state = JWT::encode([ 'me' => $me, 'authorization_endpoint' => $authorizationEndpoint, 'return_to' => $request->get('return_to'), 'time' => time(), 'exp' => time()+300 // verified by the JWT library ], Config::$secretKey); if($authorizationEndpoint) { // If the user specified only an authorization endpoint, use that $authorizationURL = IndieAuth\Client::buildAuthorizationURL($authorizationEndpoint, $me, self::_buildRedirectURI(), Config::$clientID, $state); } else { // Otherwise, fall back to indieauth.com $authorizationURL = IndieAuth\Client::buildAuthorizationURL(Config::$defaultAuthorizationEndpoint, $me, self::_buildRedirectURI(), Config::$clientID, $state); } $response->setStatusCode(302); $response->headers->set('Location', $authorizationURL); return $response; } public function login_callback(Request $request, Response $response) { if(!$request->get('state') || !$request->get('code') || !$request->get('me')) { $response->setContent(view('login', [ 'title' => 'Sign In to Telegraph', 'error' => 'Missing Parameters', 'error_description' => 'The auth server did not return the necessary parameters, state and code and me.' ])); return $response; } // Validate the "state" parameter to ensure this request originated at this client try { $state = JWT::decode($request->get('state'), Config::$secretKey, ['HS256']); if(!$state) { $response->setContent(view('login', [ 'title' => 'Sign In to Telegraph', 'error' => 'Invalid State', 'error_description' => 'The state parameter was not valid.' ])); return $response; } } catch(Exception $e) { $response->setContent(view('login', [ 'title' => 'Sign In to Telegraph', 'error' => 'Invalid State', 'error_description' => 'The state parameter was invalid:
'.htmlspecialchars($e->getMessage()) ])); return $response; } // Discover the authorization endpoint from the "me" that was returned by the auth server // This allows the auth server to return a different URL than the user originally entered, // for example if the user enters multiusersite.example the auth server can return multiusersite.example/alice if($state->authorization_endpoint) { // only discover the auth endpoint if one was originally found, otherwise use our fallback $authorizationEndpoint = IndieAuth\Client::discoverAuthorizationEndpoint($request->get('me')); } else { $authorizationEndpoint = Config::$defaultAuthorizationEndpoint; } // Verify the code with the auth server $token = IndieAuth\Client::verifyIndieAuthCode($authorizationEndpoint, $request->get('code'), $request->get('me'), self::_buildRedirectURI(), Config::$clientID, $request->get('state'), true); if(!array_key_exists('auth', $token) || !array_key_exists('me', $token['auth'])) { // The auth server didn't return a "me" URL $response->setContent(view('login', [ 'title' => 'Sign In to Telegraph', 'error' => 'Invalid Auth Server Response', 'error_description' => 'The authorization server did not return a valid response:
'.htmlspecialchars(json_encode($token)) ])); return $response; } // Create or load the user session_start(); $_SESSION['me'] = $token['auth']['me']; $response->setStatusCode(302); $response->headers->set('Location', ($state->return_to ?: '/dashboard')); return $response; } private static function _buildRedirectURI() { return 'http' . (Config::$ssl ? 's' : '') . '://' . $_SERVER['SERVER_NAME'] . '/login/callback'; } }