}); $app->get('/auth/start', function () use($app) { $req = $app->request(); $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/ // Normlize the value now (move this into a function in IndieAuth\Client later) if (!array_key_exists('me', $params) || !($me = normalizeMeURL($params['me']))) { $html = render('auth_error', array('title' => 'Sign In', 'error' => 'Invalid "me" Parameter', 'errorDescription' => 'The URL you entered, "<strong>' . $params['me'] . '</strong>" is not valid.')); $app->response()->body($html); return; } if (k($params, 'redirect')) { $_SESSION['redirect_after_login'] = $params['redirect']; } $authorizationEndpoint = IndieAuth\Client::discoverAuthorizationEndpoint($me); $tokenEndpoint = IndieAuth\Client::discoverTokenEndpoint($me); $micropubEndpoint = IndieAuth\Client::discoverMicropubEndpoint($me); if ($tokenEndpoint && $micropubEndpoint && $authorizationEndpoint) { // Generate a "state" parameter for the request $state = IndieAuth\Client::generateStateParameter(); $_SESSION['auth_state'] = $state; $scope = 'post'; $authorizationURL = IndieAuth\Client::buildAuthorizationURL($authorizationEndpoint, $me, buildRedirectURI(), clientID(), $state, $scope); } else { $authorizationURL = false; } // 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 // immediately to the auth endpoint. // This will still generate a new access token when they finish logging in.
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, <code>state</code> and <code>code</code> and <code>me</code>.'])); 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 <code>state</code> parameter was not valid.'])); return $response; } } catch (Exception $e) { $response->setContent(view('login', ['title' => 'Sign In to Telegraph', 'error' => 'Invalid State', 'error_description' => 'The <code>state</code> parameter was invalid:<br>' . 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:<br>' . htmlspecialchars(json_encode($token))])); return $response; } // Create or load the user $user = ORM::for_table('users')->where('url', $token['auth']['me'])->find_one(); if (!$user) { $user = ORM::for_table('users')->create(); $user->url = $token['auth']['me']; $user->created_at = date('Y-m-d H:i:s'); $user->last_login = date('Y-m-d H:i:s'); $user->save(); // Create a site for them with the default role $site = ORM::for_table('sites')->create(); $site->name = 'My Website'; $site->url = $token['auth']['me']; $site->created_by = $user->id; $site->created_at = date('Y-m-d H:i:s'); $site->save(); $role = ORM::for_table('roles')->create(); $role->site_id = $site->id; $role->user_id = $user->id; $role->role = 'owner'; $role->token = random_string(32); $role->save(); } else { $user->last_login = date('Y-m-d H:i:s'); $user->save(); } q()->queue('Telegraph\\ProfileFetcher', 'fetch', [$user->id]); session_start(); $_SESSION['user_id'] = $user->id; $response->setStatusCode(302); $response->headers->set('Location', $state->return_to ?: '/dashboard'); return $response; }