The authentication information in the link you clicked is '.
+ 'invalid or out of date. Make sure you are copy-and-pasting the '.
+ 'entire link into your browser. You can try again, or request '.
+ 'a new email.
');
+
+ return $this->buildStandardPageResponse(
+ $view,
+ array(
+ 'title' => 'Facebook Auth Failed',
+ ));
}
$data = array();
parse_str($response, $data);
$token = $data['access_token'];
}
$user_json = @file_get_contents('https://graph.facebook.com/me?access_token='.$token);
$user_data = json_decode($user_json, true);
$user_id = $user_data['id'];
$known_user = id(new PhabricatorUser())
->loadOneWhere('facebookUID = %d', $user_id);
if ($known_user) {
$session_key = $known_user->establishSession('web');
$request->setCookie('phusr', $known_user->getUsername());
$request->setCookie('phsid', $session_key);
return id(new AphrontRedirectResponse())
->setURI('/');
}
$current_user = $this->getRequest()->getUser();
if ($current_user->getPHID()) {
if ($current_user->getFacebookUID() &&
$current_user->getFacebookUID() != $user_id) {
throw new Exception(
"Your account is already associated with a Facebook user ID other ".
"than the one you just logged in with...?");
}
if ($request->isFormPost()) {
$current_user->setFacebookUID($user_id);
$current_user->save();
// TODO: ship them back to the 'account' page or whatever?
return id(new AphrontRedirectResponse())
->setURI('/');
}
$ph_account = $current_user->getUsername();
$fb_account = phutil_escape_html($user_data['name']);
$form = new AphrontFormView();
$form
->addHiddenInput('token', $token)
->setUser($request->getUser())
- ->setAction('/facebook-connect/')
+ ->setAction('/facebook-auth/')
->appendChild(
'
Do you want to link your '.
"existing Phabricator account ({$ph_account}) ".
"with your Facebook account ({$fb_account}) so ".
- "you can login with Facebook Connect?")
+ "you can login with Facebook?")
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Link Accounts')
->addCancelButton('/login/'));
$panel = new AphrontPanelView();
$panel->setHeader('Link Facebook Account');
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
$panel->appendChild($form);
return $this->buildStandardPageResponse(
$panel,
array(
'title' => 'Link Facebook Account',
));
}
$errors = array();
$e_username = true;
$user = new PhabricatorUser();
$matches = null;
if (preg_match('@/([a-zA-Z0-9]+)$@', $user_data['link'], $matches)) {
$user->setUsername($matches[1]);
}
if ($request->isFormPost()) {
$username = $request->getStr('username');
if (!strlen($username)) {
$e_username = 'Required';
$errors[] = 'Username is required.';
} else if (!preg_match('/^[a-zA-Z0-9]+$/', $username, $matches)) {
$e_username = 'Invalid';
$errors[] = 'Username may only contain letters and numbers.';
}
$user->setUsername($username);
$user->setFacebookUID($user_id);
$user->setEmail($user_data['email']);
if (!$errors) {
$image = @file_get_contents('https://graph.facebook.com/me/picture?access_token='.$token);
$file = PhabricatorFile::newFromFileData(
$image,
array(
'name' => 'fbprofile.jpg'
));
$user->setProfileImagePHID($file->getPHID());
$user->setRealName($user_data['name']);
try {
$user->save();
$session_key = $user->establishSession('web');
$request->setCookie('phusr', $user->getUsername());
$request->setCookie('phsid', $session_key);
return id(new AphrontRedirectResponse())->setURI('/');
} catch (AphrontQueryDuplicateKeyException $exception) {
$key = $exception->getDuplicateKey();
if ($key == 'userName') {
$e_username = 'Duplicate';
$errors[] = 'That username is not unique.';
} else {
throw $exception;
}
}
}
}
$error_view = null;
if ($errors) {
$error_view = new AphrontErrorView();
- $error_view->setTitle('Facebook Connect Failed');
+ $error_view->setTitle('Facebook Auth Failed');
$error_view->setErrors($errors);
}
$form = new AphrontFormView();
$form
->addHiddenInput('token', $token)
->setUser($request->getUser())
- ->setAction('/facebook-connect/')
+ ->setAction('/facebook-auth/')
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Username')
->setName('username')
->setValue($user->getUsername())
->setError($e_username))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Create Account'));
$panel = new AphrontPanelView();
$panel->setHeader('Create New Account');
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
$panel->appendChild($form);
return $this->buildStandardPageResponse(
array(
$error_view,
$panel,
),
array(
'title' => 'Create New Account',
));
}
}
diff --git a/src/applications/auth/controller/facebookconnect/__init__.php b/src/applications/auth/controller/facebookauth/__init__.php
similarity index 71%
rename from src/applications/auth/controller/facebookconnect/__init__.php
rename to src/applications/auth/controller/facebookauth/__init__.php
index 7c02260f35..f5b4a8f1a9 100644
--- a/src/applications/auth/controller/facebookconnect/__init__.php
+++ b/src/applications/auth/controller/facebookauth/__init__.php
@@ -1,22 +1,25 @@
OK';
+ $res_no = 'NO';
+ $res_na = 'N/A';
+
+ $results = array();
+
+ if (!$auth_enabled) {
+ $results['facebook.auth-enabled'] = array(
+ $res_no,
+ 'false',
+ 'Facebook authentication is disabled in the configuration. Edit the '.
+ 'environmental configuration to enable "facebook.auth-enabled".');
+ } else {
+ $results['facebook.auth-enabled'] = array(
+ $res_ok,
+ 'true',
+ 'Facebook authentication is enabled.');
+ }
+
+ if (!$app_id) {
+ $results['facebook.application-id'] = array(
+ $res_no,
+ null,
+ 'No Facebook Application ID is configured. Edit the environmental '.
+ 'configuration to specify an application ID in '.
+ '"facebook.application-id". To generate an ID, sign into Facebook, '.
+ 'install the "Developer" application, and use it to create a new '.
+ 'Facebook application.');
+ } else {
+ $results['facebook.application-id'] = array(
+ $res_ok,
+ $app_id,
+ 'Application ID is set.');
+ }
+
+ if (!$app_secret) {
+ $results['facebook.application-secret'] = array(
+ $res_no,
+ null,
+ 'No Facebook Application secret is configured. Edit the environmental '.
+ 'configuration to specify an Application Secret, in '.
+ '"facebook.application-secret". You can find the application secret '.
+ 'in the Facebook "Developer" application on Facebook.');
+ } else {
+ $results['facebook.application-secret'] = array(
+ $res_ok,
+ "It's a secret!",
+ 'Application secret is set.');
+ }
+
+ $timeout = stream_context_create(
+ array(
+ 'http' => array(
+ 'ignore_errors' => true,
+ 'timeout' => 5,
+ ),
+ ));
+ $timeout_strict = stream_context_create(
+ array(
+ 'http' => array(
+ 'timeout' => 5,
+ ),
+ ));
+
+ $internet = @file_get_contents("http://google.com/", false, $timeout);
+ if ($internet === false) {
+ $results['internet'] = array(
+ $res_no,
+ null,
+ 'Unable to make an HTTP request to Google. Check your outbound '.
+ 'internet connection and firewall/filtering settings.');
+ } else {
+ $results['internet'] = array(
+ $res_ok,
+ null,
+ 'Internet seems OK.');
+ }
+
+ $facebook = @file_get_contents("http://facebook.com/", false, $timeout);
+ if ($facebook === false) {
+ $results['facebook.com'] = array(
+ $res_no,
+ null,
+ 'Unable to make an HTTP request to facebook.com. Facebook may be '.
+ 'down or inaccessible.');
+ } else {
+ $results['facebook.com'] = array(
+ $res_ok,
+ null,
+ 'Made a request to facebook.com.');
+ }
+
+ $graph = @file_get_contents(
+ "https://graph.facebook.com/me",
+ false,
+ $timeout);
+ if ($graph === false) {
+ $results['Facebook Graph'] = array(
+ $res_no,
+ null,
+ "Unable to make an HTTPS request to graph.facebook.com. ".
+ "The Facebook graph may be down or inaccessible.");
+ } else {
+ $results['Facebook Graph'] = array(
+ $res_ok,
+ null,
+ 'Made a request to graph.facebook.com.');
+ }
+
+ $test_uri = new PhutilURI('https://graph.facebook.com/oauth/access_token');
+ $test_uri->setQueryParams(
+ array(
+ 'client_id' => $app_id,
+ 'client_secret' => $app_secret,
+ 'grant_type' => 'client_credentials',
+ ));
+
+ $token_value = @file_get_contents($test_uri, false, $timeout);
+ $token_strict = @file_get_contents($test_uri, false, $timeout_strict);
+ if ($token_value === false) {
+ $results['App Login'] = array(
+ $res_no,
+ null,
+ "Unable to perform an application login with your Application ID and ".
+ "Application Secret. You may have mistyped or misconfigured them; ".
+ "Facebook may have revoked your authorization; or Facebook may be ".
+ "having technical problems.");
+ } else {
+ if ($token_strict) {
+ $results['App Login'] = array(
+ $res_ok,
+ $token_strict,
+ "Raw application login to Facebook works.");
+ } else {
+ $data = json_decode($token_value, true);
+ if (!is_array($data)) {
+ $results['App Login'] = array(
+ $res_no,
+ $token_value,
+ "Application Login failed but the graph server did not respond ".
+ "with valid JSON error information. Facebook may be experiencing ".
+ "technical problems.");
+ } else {
+ $results['App Login'] = array(
+ $res_no,
+ null,
+ "Application Login failed with error: ".$token_value);
+ }
+ }
+ }
+
+ return $this->renderResults($results);
+ }
+
+ private function renderResults($results) {
+
+ $rows = array();
+ foreach ($results as $key => $result) {
+ $rows[] = array(
+ phutil_escape_html($key),
+ $result[0],
+ phutil_escape_html($result[1]),
+ phutil_escape_html($result[2]),
+ );
+ }
+
+ $table_view = new AphrontTableView($rows);
+ $table_view->setHeaders(
+ array(
+ 'Test',
+ 'Result',
+ 'Value',
+ 'Details',
+ ));
+ $table_view->setColumnClasses(
+ array(
+ null,
+ null,
+ null,
+ 'wide',
+ ));
+
+ $panel_view = new AphrontPanelView();
+ $panel_view->setHeader('Facebook Auth Diagnostics');
+ $panel_view->appendChild(
+ '
These tests may be able to '.
+ 'help diagnose the root cause of problems you experience with '.
+ 'Facebook Authentication. Reload the page to run the tests again.
');
- $panel->appendChild($facebook_connect);
+ $fbauth_enabled = PhabricatorEnv::getEnvConfig('facebook.auth-enabled');
+ if ($fbauth_enabled) {
+ $auth_uri = new PhutilURI("https://www.facebook.com/dialog/oauth");
+
+ $user = $request->getUser();
+
+ $redirect_uri = PhabricatorEnv::getURI('/facebook-auth/');
+ $app_id = PhabricatorEnv::getEnvConfig('facebook.application-id');
+
+ // TODO: In theory we should use 'state' to prevent CSRF, but the total
+ // effect of the CSRF attack is that an attacker can cause a user to login
+ // to Phabricator if they're already logged into Facebook. This does not
+ // seem like the most severe threat in the world, and generating CSRF for
+ // logged-out users is vaugely tricky.
+
+ $facebook_auth = new AphrontFormView();
+ $facebook_auth
+ ->setAction($auth_uri)
+ ->addHiddenInput('client_id', $app_id)
+ ->addHiddenInput('redirect_uri', $redirect_uri)
+ ->addHiddenInput('scope', 'email')
+ ->setUser($request->getUser())
+ ->setMethod('GET')
+ ->appendChild(
+ '
Login or register for '.
+ 'Phabricator using your Facebook account.