1
0
mirror of https://github.com/e107inc/e107.git synced 2025-08-11 09:04:38 +02:00

HybridAuth upgraded to v2.8.0.

This commit is contained in:
Cameron
2016-12-01 15:57:23 -08:00
parent 876f2f9f28
commit 5dd2ad8b99
13 changed files with 620 additions and 435 deletions

View File

@@ -15,7 +15,7 @@
*/ */
class Hybrid_Auth { class Hybrid_Auth {
public static $version = "2.6.0"; public static $version = "2.8.0";
/** /**
* Configuration array * Configuration array
@@ -352,6 +352,9 @@ class Hybrid_Auth {
* @param string $mode PHP|JS * @param string $mode PHP|JS
*/ */
public static function redirect($url, $mode = "PHP") { public static function redirect($url, $mode = "PHP") {
if(!$mode){
$mode = 'PHP';
}
Hybrid_Logger::info("Enter Hybrid_Auth::redirect( $url, $mode )"); Hybrid_Logger::info("Enter Hybrid_Auth::redirect( $url, $mode )");
// Ensure session is saved before sending response, see https://github.com/symfony/symfony/pull/12341 // Ensure session is saved before sending response, see https://github.com/symfony/symfony/pull/12341

View File

@@ -27,7 +27,7 @@ class Hybrid_Endpoint {
// with /index.php?hauth.done={provider}?{args}... // with /index.php?hauth.done={provider}?{args}...
// >here we need to parse $_SERVER[QUERY_STRING] // >here we need to parse $_SERVER[QUERY_STRING]
$request = $_REQUEST; $request = $_REQUEST;
if (strrpos($_SERVER["QUERY_STRING"], '?')) { if (isset($_SERVER["QUERY_STRING"]) && strrpos($_SERVER["QUERY_STRING"], '?')) {
$_SERVER["QUERY_STRING"] = str_replace("?", "&", $_SERVER["QUERY_STRING"]); $_SERVER["QUERY_STRING"] = str_replace("?", "&", $_SERVER["QUERY_STRING"]);
parse_str($_SERVER["QUERY_STRING"], $request); parse_str($_SERVER["QUERY_STRING"], $request);
} }

View File

@@ -153,11 +153,26 @@ class Hybrid_Provider_Adapter {
# for default HybridAuth endpoint url hauth_login_start_url # for default HybridAuth endpoint url hauth_login_start_url
# auth.start required the IDp ID # auth.start required the IDp ID
# auth.time optional login request timestamp # auth.time optional login request timestamp
$this->params["login_start"] = $HYBRID_AUTH_URL_BASE . ( strpos($HYBRID_AUTH_URL_BASE, '?') ? '&' : '?' ) . "hauth.start={$this->id}&hauth.time={$this->params["hauth_time"]}"; if (!isset($this->params["login_start"]) ) {
$this->params["login_start"] = $HYBRID_AUTH_URL_BASE . ( strpos($HYBRID_AUTH_URL_BASE, '?') ? '&' : '?' ) . "hauth.start={$this->id}&hauth.time={$this->params["hauth_time"]}";
}
# for default HybridAuth endpoint url hauth_login_done_url # for default HybridAuth endpoint url hauth_login_done_url
# auth.done required the IDp ID # auth.done required the IDp ID
$this->params["login_done"] = $HYBRID_AUTH_URL_BASE . ( strpos($HYBRID_AUTH_URL_BASE, '?') ? '&' : '?' ) . "hauth.done={$this->id}"; if (!isset($this->params["login_done"]) ) {
$this->params["login_done"] = $HYBRID_AUTH_URL_BASE . ( strpos($HYBRID_AUTH_URL_BASE, '?') ? '&' : '?' ) . "hauth.done={$this->id}";
}
# workaround to solve windows live authentication since microsoft disallowed redirect urls to contain any parameters
# http://mywebsite.com/path_to_hybridauth/?hauth.done=Live will not work
if ($this->id=="Live") {
$this->params["login_done"] = $HYBRID_AUTH_URL_BASE."live.php";
}
# Workaround to fix broken callback urls for the Facebook OAuth client
if ($this->adapter->useSafeUrls) {
$this->params['login_done'] = str_replace('hauth.done', 'hauth_done', $this->params['login_done']);
}
if (isset($this->params["hauth_return_to"])) { if (isset($this->params["hauth_return_to"])) {
Hybrid_Auth::storage()->set("hauth_session.{$this->id}.hauth_return_to", $this->params["hauth_return_to"]); Hybrid_Auth::storage()->set("hauth_session.{$this->id}.hauth_return_to", $this->params["hauth_return_to"]);
@@ -173,7 +188,12 @@ class Hybrid_Provider_Adapter {
// move on // move on
Hybrid_Logger::debug("Hybrid_Provider_Adapter::login( {$this->id} ), redirect the user to login_start URL."); Hybrid_Logger::debug("Hybrid_Provider_Adapter::login( {$this->id} ), redirect the user to login_start URL.");
Hybrid_Auth::redirect($this->params["login_start"]); // redirect
if (empty($this->params["redirect_mode"])) {
Hybrid_Auth::redirect($this->params["login_start"]);
} else {
Hybrid_Auth::redirect($this->params["login_start"],$this->params["redirect_mode"]);
}
} }
/** /**
@@ -281,6 +301,12 @@ class Hybrid_Provider_Adapter {
// get the stored callback url // get the stored callback url
$callback_url = Hybrid_Auth::storage()->get("hauth_session.{$this->id}.hauth_return_to"); $callback_url = Hybrid_Auth::storage()->get("hauth_session.{$this->id}.hauth_return_to");
// if the user presses the back button in the browser and we already deleted the hauth_return_to from
// the session in the previous request, we will redirect to '/' instead of displaying a blank page.
if (!$callback_url) {
$callback_url = '/';
}
// remove some unneeded stored data // remove some unneeded stored data
Hybrid_Auth::storage()->delete("hauth_session.{$this->id}.hauth_return_to"); Hybrid_Auth::storage()->delete("hauth_session.{$this->id}.hauth_return_to");
Hybrid_Auth::storage()->delete("hauth_session.{$this->id}.hauth_endpoint"); Hybrid_Auth::storage()->delete("hauth_session.{$this->id}.hauth_endpoint");

View File

@@ -64,6 +64,9 @@ abstract class Hybrid_Provider_Model {
*/ */
public $compressed = false; public $compressed = false;
/** @var bool $useSafeUrls Enable this to replace '.' with '_' characters in the callback urls */
public $useSafeUrls = false;
/** /**
* Common providers adapter constructor * Common providers adapter constructor
* *

View File

@@ -0,0 +1,85 @@
<?php
/*!
* HybridAuth
* http://hybridauth.sourceforge.net | https://github.com/hybridauth/hybridauth
* (c) 2009-2015 HybridAuth authors | hybridauth.sourceforge.net/licenses.html
*/
/**
* Hybrid_Providers_Amazon provider adapter based on OAuth2 protocol
*
* added by skyverge | https://github.com/skyverge
*
* The Provider is very similar to standard Oauth2 providers with a few differences:
* - it sets the Content-Type header explicitly to application/x-www-form-urlencoded
* as required by Amazon
* - it uses a custom OAuth2Client, because the built-in one does not use http_build_query()
* to set curl POST params, which causes cURL to set the Content-Type to multipart/form-data
*
* @property OAuth2Client $api
*/
class Hybrid_Providers_Amazon extends Hybrid_Provider_Model_OAuth2 {
// default permissions
public $scope = 'profile postal_code';
/**
* IDp wrappers initializer
*/
function initialize() {
if ( ! $this->config['keys']['id'] || ! $this->config['keys']['secret'] ) {
throw new Exception( "Your application id and secret are required in order to connect to {$this->providerId}.", 4 );
}
// override requested scope
if ( isset( $this->config['scope'] ) && ! empty( $this->config['scope'] ) ) {
$this->scope = $this->config['scope'];
}
// include OAuth2 client
require_once Hybrid_Auth::$config['path_libraries'] . 'OAuth/OAuth2Client.php';
require_once Hybrid_Auth::$config['path_libraries'] . 'Amazon/AmazonOAuth2Client.php';
// create a new OAuth2 client instance
$this->api = new AmazonOAuth2Client( $this->config['keys']['id'], $this->config['keys']['secret'], $this->endpoint, $this->compressed );
$this->api->api_base_url = 'https://api.amazon.com';
$this->api->authorize_url = 'https://www.amazon.com/ap/oa';
$this->api->token_url = 'https://api.amazon.com/auth/o2/token';
$this->api->curl_header = array( 'Content-Type: application/x-www-form-urlencoded' );
// If we have an access token, set it
if ( $this->token( 'access_token' ) ) {
$this->api->access_token = $this->token('access_token');
$this->api->refresh_token = $this->token('refresh_token');
$this->api->access_token_expires_in = $this->token('expires_in');
$this->api->access_token_expires_at = $this->token('expires_at');
}
// Set curl proxy if exists
if ( isset( Hybrid_Auth::$config['proxy'] ) ) {
$this->api->curl_proxy = Hybrid_Auth::$config['proxy'];
}
}
/**
* load the user profile from the IDp api client
*/
function getUserProfile() {
$data = $this->api->get( '/user/profile' );
if ( ! isset( $data->user_id ) ){
throw new Exception( "User profile request failed! {$this->providerId} returned an invalid response.", 6 );
}
$this->user->profile->identifier = @ $data->user_id;
$this->user->profile->email = @ $data->email;
$this->user->profile->displayName = @ $data->name;
$this->user->profile->zip = @ $data->postal_code;
return $this->user->profile;
}
}

View File

@@ -1,5 +1,8 @@
<?php <?php
use Facebook\Exceptions\FacebookSDKException;
use Facebook\Facebook as FacebookSDK;
/* ! /* !
* HybridAuth * HybridAuth
* http://hybridauth.sourceforge.net | http://github.com/hybridauth/hybridauth * http://hybridauth.sourceforge.net | http://github.com/hybridauth/hybridauth
@@ -8,424 +11,291 @@
/** /**
* Hybrid_Providers_Facebook provider adapter based on OAuth2 protocol * Hybrid_Providers_Facebook provider adapter based on OAuth2 protocol
*
* Hybrid_Providers_Facebook use the Facebook PHP SDK created by Facebook * Hybrid_Providers_Facebook use the Facebook PHP SDK created by Facebook
*
* http://hybridauth.sourceforge.net/userguide/IDProvider_info_Facebook.html * http://hybridauth.sourceforge.net/userguide/IDProvider_info_Facebook.html
*/ */
class Hybrid_Providers_Facebook extends Hybrid_Provider_Model { class Hybrid_Providers_Facebook extends Hybrid_Provider_Model {
/** /**
* default permissions, and a lot of them. You can change them from the configuration by setting the scope to what you want/need * Default permissions, and a lot of them. You can change them from the configuration by setting the scope to what you want/need.
* {@inheritdoc} * For a complete list see: https://developers.facebook.com/docs/facebook-login/permissions
*/ *
public $scope = "email, user_about_me, user_birthday, user_hometown, user_location, user_website, publish_actions, read_custom_friendlists"; * @link https://developers.facebook.com/docs/facebook-login/permissions
* @var array $scope
/** */
* Provider API client public $scope = ['email', 'user_about_me', 'user_birthday', 'user_hometown', 'user_location', 'user_website', 'publish_actions', 'read_custom_friendlists'];
* @var Facebook
*/ /**
public $api; * Provider API client
*
/** * @var \Facebook\Facebook
* {@inheritdoc} */
*/ public $api;
function initialize() {
if (!$this->config["keys"]["id"] || !$this->config["keys"]["secret"]) { public $useSafeUrls = true;
throw new Exception("Your application id and secret are required in order to connect to {$this->providerId}.", 4);
} /**
* {@inheritdoc}
if (!class_exists('FacebookApiException', false)) { */
require_once Hybrid_Auth::$config["path_libraries"] . "Facebook/base_facebook.php"; function initialize() {
require_once Hybrid_Auth::$config["path_libraries"] . "Facebook/facebook.php"; if (!$this->config["keys"]["id"] || !$this->config["keys"]["secret"]) {
} throw new Exception("Your application id and secret are required in order to connect to {$this->providerId}.", 4);
}
if (isset(Hybrid_Auth::$config["proxy"])) {
BaseFacebook::$CURL_OPTS[CURLOPT_PROXY] = Hybrid_Auth::$config["proxy"]; if (isset($this->config['scope'])) {
} $scope = $this->config['scope'];
if (is_string($scope)) {
$trustForwarded = isset($this->config['trustForwarded']) ? (bool) $this->config['trustForwarded'] : false; $scope = explode(",", $scope);
$this->api = new Facebook(array('appId' => $this->config["keys"]["id"], 'secret' => $this->config["keys"]["secret"], 'trustForwarded' => $trustForwarded)); }
$scope = array_map('trim', $scope);
if ($this->token("access_token")) { $this->scope = $scope;
$this->api->setAccessToken($this->token("access_token")); }
$this->api->setExtendedAccessToken();
$access_token = $this->api->getAccessToken(); $trustForwarded = isset($this->config['trustForwarded']) ? (bool)$this->config['trustForwarded'] : false;
if ($access_token) { $this->api = new FacebookSDK([
$this->token("access_token", $access_token); 'app_id' => $this->config["keys"]["id"],
$this->api->setAccessToken($access_token); 'app_secret' => $this->config["keys"]["secret"],
} 'default_graph_version' => 'v2.8',
'trustForwarded' => $trustForwarded,
$this->api->setAccessToken($this->token("access_token")); ]);
} }
$this->api->getUser(); /**
} * {@inheritdoc}
*/
/** function loginBegin() {
* {@inheritdoc}
*/ $this->endpoint = $this->params['login_done'];
function loginBegin() { $helper = $this->api->getRedirectLoginHelper();
$parameters = array("scope" => $this->scope, "redirect_uri" => $this->endpoint, "display" => "page");
$optionals = array("scope", "redirect_uri", "display", "auth_type"); // Use re-request, because this will trigger permissions window if not all permissions are granted.
$url = $helper->getReRequestUrl($this->endpoint, $this->scope);
foreach ($optionals as $parameter) {
if (isset($this->config[$parameter]) && !empty($this->config[$parameter])) { // Redirect to Facebook
$parameters[$parameter] = $this->config[$parameter]; Hybrid_Auth::redirect($url);
}
//If the auth_type parameter is used, we need to generate a nonce and include it as a parameter
if ($parameter == "auth_type") { /**
$nonce = md5(uniqid(mt_rand(), true)); * {@inheritdoc}
$parameters['auth_nonce'] = $nonce; */
function loginFinish() {
Hybrid_Auth::storage()->set('fb_auth_nonce', $nonce);
} $helper = $this->api->getRedirectLoginHelper();
} try {
} $accessToken = $helper->getAccessToken();
} catch (Facebook\Exceptions\FacebookResponseException $e) {
if (isset($this->config['force']) && $this->config['force'] === true) { throw new Hybrid_Exception('Facebook Graph returned an error: ' . $e->getMessage());
$parameters['auth_type'] = 'reauthenticate'; } catch (Facebook\Exceptions\FacebookSDKException $e) {
$parameters['auth_nonce'] = md5(uniqid(mt_rand(), true)); throw new Hybrid_Exception('Facebook SDK returned an error: ' . $e->getMessage());
}
Hybrid_Auth::storage()->set('fb_auth_nonce', $parameters['auth_nonce']);
} if (!isset($accessToken)) {
if ($helper->getError()) {
// get the login url throw new Hybrid_Exception(sprintf("Could not authorize user, reason: %s (%d)", $helper->getErrorDescription(), $helper->getErrorCode()));
$url = $this->api->getLoginUrl($parameters); } else {
throw new Hybrid_Exception("Could not authorize user. Bad request");
// redirect to facebook }
Hybrid_Auth::redirect($url); }
}
try {
/** // Validate token
* {@inheritdoc} $oAuth2Client = $this->api->getOAuth2Client();
*/ $tokenMetadata = $oAuth2Client->debugToken($accessToken);
function loginFinish() { $tokenMetadata->validateAppId($this->config["keys"]["id"]);
// in case we get error_reason=user_denied&error=access_denied $tokenMetadata->validateExpiration();
if (isset($_REQUEST['error']) && $_REQUEST['error'] == "access_denied") {
throw new Exception("Authentication failed! The user denied your request.", 5); // Exchanges a short-lived access token for a long-lived one
} if (!$accessToken->isLongLived()) {
$accessToken = $oAuth2Client->getLongLivedAccessToken($accessToken);
// in case we are using iOS/Facebook reverse authentication }
if (isset($_REQUEST['access_token'])) { } catch (FacebookSDKException $e) {
$this->token("access_token", $_REQUEST['access_token']); throw new Hybrid_Exception($e->getMessage(), 0, $e);
$this->api->setAccessToken($this->token("access_token")); }
$this->api->setExtendedAccessToken();
$access_token = $this->api->getAccessToken(); $this->setUserConnected();
$this->token("access_token", $accessToken->getValue());
if ($access_token) { }
$this->token("access_token", $access_token);
$this->api->setAccessToken($access_token); /**
} * {@inheritdoc}
*/
$this->api->setAccessToken($this->token("access_token")); function logout() {
} parent::logout();
}
// if auth_type is used, then an auth_nonce is passed back, and we need to check it. /**
if (isset($_REQUEST['auth_nonce'])) { * {@inheritdoc}
*/
$nonce = Hybrid_Auth::storage()->get('fb_auth_nonce'); function getUserProfile() {
try {
//Delete the nonce $fields = [
Hybrid_Auth::storage()->delete('fb_auth_nonce'); 'id',
'name',
if ($_REQUEST['auth_nonce'] != $nonce) { 'first_name',
throw new Exception("Authentication failed! Invalid nonce used for reauthentication.", 5); 'last_name',
} 'link',
} 'website',
'gender',
// try to get the UID of the connected user from fb, should be > 0 'locale',
if (!$this->api->getUser()) { 'about',
throw new Exception("Authentication failed! {$this->providerId} returned an invalid user id.", 5); 'email',
} 'hometown',
'location',
// set user as logged in 'birthday'
$this->setUserConnected(); ];
$response = $this->api->get('/me?fields=' . implode(',', $fields), $this->token('access_token'));
// store facebook access token $data = $response->getDecodedBody();
$this->token("access_token", $this->api->getAccessToken()); } catch (FacebookSDKException $e) {
} throw new Exception("User profile request failed! {$this->providerId} returned an error: {$e->getMessage()}", 6, $e);
}
/**
* {@inheritdoc} // Store the user profile.
*/ $this->user->profile->identifier = (array_key_exists('id', $data)) ? $data['id'] : "";
function logout() { $this->user->profile->displayName = (array_key_exists('name', $data)) ? $data['name'] : "";
$this->api->destroySession(); $this->user->profile->firstName = (array_key_exists('first_name', $data)) ? $data['first_name'] : "";
parent::logout(); $this->user->profile->lastName = (array_key_exists('last_name', $data)) ? $data['last_name'] : "";
} $this->user->profile->photoURL = !empty($this->user->profile->identifier) ? "https://graph.facebook.com/" . $this->user->profile->identifier . "/picture?width=150&height=150" : '';
$this->user->profile->profileURL = (array_key_exists('link', $data)) ? $data['link'] : "";
/** $this->user->profile->webSiteURL = (array_key_exists('website', $data)) ? $data['website'] : "";
* {@inheritdoc} $this->user->profile->gender = (array_key_exists('gender', $data)) ? $data['gender'] : "";
*/ $this->user->profile->language = (array_key_exists('locale', $data)) ? $data['locale'] : "";
function getUserProfile() { $this->user->profile->description = (array_key_exists('about', $data)) ? $data['about'] : "";
// request user profile from fb api $this->user->profile->email = (array_key_exists('email', $data)) ? $data['email'] : "";
try { $this->user->profile->emailVerified = (array_key_exists('email', $data)) ? $data['email'] : "";
$fields = array( $this->user->profile->region = (array_key_exists("location", $data) && array_key_exists("name", $data['location'])) ? $data['location']["name"] : "";
'id', 'name', 'first_name', 'last_name', 'link', 'website',
'gender', 'locale', 'about', 'email', 'hometown', 'location', if (!empty($this->user->profile->region)) {
'birthday' $regionArr = explode(',', $this->user->profile->region);
); if (count($regionArr) > 1) {
$this->user->profile->city = trim($regionArr[0]);
$data = $this->api->api('/me?fields=' . implode(',', $fields)); $this->user->profile->country = trim($regionArr[1]);
} catch (FacebookApiException $e) { }
throw new Exception("User profile request failed! {$this->providerId} returned an error: {$e->getMessage()}", 6, $e); }
}
if (array_key_exists('birthday', $data)) {
// if the provider identifier is not received, we assume the auth has failed $birtydayPieces = explode('/', $data['birthday']);
if (!isset($data["id"])) {
throw new Exception("User profile request failed! {$this->providerId} api returned an invalid response: " . Hybrid_Logger::dumpData( $data ), 6); if (count($birtydayPieces) == 1) {
} $this->user->profile->birthYear = (int)$birtydayPieces[0];
} elseif (count($birtydayPieces) == 2) {
# store the user profile. $this->user->profile->birthMonth = (int)$birtydayPieces[0];
$this->user->profile->identifier = (array_key_exists('id', $data)) ? $data['id'] : ""; $this->user->profile->birthDay = (int)$birtydayPieces[1];
$this->user->profile->username = (array_key_exists('username', $data)) ? $data['username'] : ""; } elseif (count($birtydayPieces) == 3) {
$this->user->profile->displayName = (array_key_exists('name', $data)) ? $data['name'] : ""; $this->user->profile->birthMonth = (int)$birtydayPieces[0];
$this->user->profile->firstName = (array_key_exists('first_name', $data)) ? $data['first_name'] : ""; $this->user->profile->birthDay = (int)$birtydayPieces[1];
$this->user->profile->lastName = (array_key_exists('last_name', $data)) ? $data['last_name'] : ""; $this->user->profile->birthYear = (int)$birtydayPieces[2];
$this->user->profile->photoURL = "https://graph.facebook.com/" . $this->user->profile->identifier . "/picture?width=150&height=150"; }
$this->user->profile->coverInfoURL = "https://graph.facebook.com/" . $this->user->profile->identifier . "?fields=cover&access_token=" . $this->api->getAccessToken(); }
$this->user->profile->profileURL = (array_key_exists('link', $data)) ? $data['link'] : "";
$this->user->profile->webSiteURL = (array_key_exists('website', $data)) ? $data['website'] : ""; return $this->user->profile;
$this->user->profile->gender = (array_key_exists('gender', $data)) ? $data['gender'] : ""; }
$this->user->profile->language = (array_key_exists('locale', $data)) ? $data['locale'] : "";
$this->user->profile->description = (array_key_exists('about', $data)) ? $data['about'] : ""; /**
$this->user->profile->email = (array_key_exists('email', $data)) ? $data['email'] : ""; * Since the Graph API 2.0, the /friends endpoint only returns friend that also use your Facebook app.
$this->user->profile->emailVerified = (array_key_exists('email', $data)) ? $data['email'] : ""; * {@inheritdoc}
$this->user->profile->region = (array_key_exists("location", $data) && array_key_exists("name", $data['location'])) ? $data['location']["name"] : ""; */
function getUserContacts() {
if (!empty($this->user->profile->region)) { $apiCall = '?fields=link,name';
$regionArr = explode(',', $this->user->profile->region); $returnedContacts = [];
if (count($regionArr) > 1) { $pagedList = true;
$this->user->profile->city = trim($regionArr[0]);
$this->user->profile->country = trim($regionArr[1]); while ($pagedList) {
} try {
} $response = $this->api->get('/me/friends' . $apiCall, $this->token('access_token'));
$response = $response->getDecodedBody();
if (array_key_exists('birthday', $data)) { } catch (FacebookSDKException $e) {
list($birthday_month, $birthday_day, $birthday_year) = explode("/", $data['birthday']); throw new Hybrid_Exception("User contacts request failed! {$this->providerId} returned an error {$e->getMessage()}", 0, $e);
}
$this->user->profile->birthDay = (int) $birthday_day;
$this->user->profile->birthMonth = (int) $birthday_month; // Prepare the next call if paging links have been returned
$this->user->profile->birthYear = (int) $birthday_year; if (array_key_exists('paging', $response) && array_key_exists('next', $response['paging'])) {
} $pagedList = true;
$next_page = explode('friends', $response['paging']['next']);
return $this->user->profile; $apiCall = $next_page[1];
} } else {
$pagedList = false;
/** }
* Attempt to retrieve the url to the cover image given the coverInfoURL
* // Add the new page contacts
* @param string $coverInfoURL coverInfoURL variable $returnedContacts = array_merge($returnedContacts, $response['data']);
* @return string url to the cover image OR blank string }
*/
function getCoverURL($coverInfoURL) { $contacts = [];
try {
$headers = get_headers($coverInfoURL); foreach ($returnedContacts as $item) {
if (substr($headers[0], 9, 3) != "404") {
$coverOBJ = json_decode(file_get_contents($coverInfoURL)); $uc = new Hybrid_User_Contact();
if (array_key_exists('cover', $coverOBJ)) { $uc->identifier = (array_key_exists("id", $item)) ? $item["id"] : "";
return $coverOBJ->cover->source; $uc->displayName = (array_key_exists("name", $item)) ? $item["name"] : "";
} $uc->profileURL = (array_key_exists("link", $item)) ? $item["link"] : "https://www.facebook.com/profile.php?id=" . $uc->identifier;
} $uc->photoURL = "https://graph.facebook.com/" . $uc->identifier . "/picture?width=150&height=150";
} catch (Exception $e) {
$contacts[] = $uc;
} }
return ""; return $contacts;
} }
/** /**
* {@inheritdoc} * Load the user latest activity, needs 'read_stream' permission
*/ *
function getUserContacts() { * @param string $stream Which activity to fetch:
$apiCall = '?fields=link,name'; * - timeline : all the stream
$returnedContacts = array(); * - me : the user activity only
$pagedList = false; * {@inheritdoc}
*/
do { function getUserActivity($stream = 'timeline') {
try { try {
$response = $this->api->api('/me/friends' . $apiCall); if ($stream == "me") {
} catch (FacebookApiException $e) { $response = $this->api->get('/me/feed', $this->token('access_token'));
throw new Exception("User contacts request failed! {$this->providerId} returned an error {$e->getMessage()}", 0, $e); } else {
} $response = $this->api->get('/me/home', $this->token('access_token'));
}
// Prepare the next call if paging links have been returned } catch (FacebookSDKException $e) {
if (array_key_exists('paging', $response) && array_key_exists('next', $response['paging'])) { throw new Hybrid_Exception("User activity stream request failed! {$this->providerId} returned an error: {$e->getMessage()}", 0, $e);
$pagedList = true; }
$next_page = explode('friends', $response['paging']['next']);
$apiCall = $next_page[1]; if (!$response || !count($response['data'])) {
} else { return [];
$pagedList = false; }
}
$activities = [];
// Add the new page contacts
$returnedContacts = array_merge($returnedContacts, $response['data']); foreach ($response['data'] as $item) {
} while ($pagedList == true);
$ua = new Hybrid_User_Activity();
$contacts = array();
$ua->id = (array_key_exists("id", $item)) ? $item["id"] : "";
foreach ($returnedContacts as $item) { $ua->date = (array_key_exists("created_time", $item)) ? strtotime($item["created_time"]) : "";
$uc = new Hybrid_User_Contact(); if ($item["type"] == "video") {
$uc->identifier = (array_key_exists("id", $item)) ? $item["id"] : ""; $ua->text = (array_key_exists("link", $item)) ? $item["link"] : "";
$uc->displayName = (array_key_exists("name", $item)) ? $item["name"] : ""; }
$uc->profileURL = (array_key_exists("link", $item)) ? $item["link"] : "https://www.facebook.com/profile.php?id=" . $uc->identifier;
$uc->photoURL = "https://graph.facebook.com/" . $uc->identifier . "/picture?width=150&height=150"; if ($item["type"] == "link") {
$ua->text = (array_key_exists("link", $item)) ? $item["link"] : "";
$contacts[] = $uc; }
}
if (empty($ua->text) && isset($item["story"])) {
return $contacts; $ua->text = (array_key_exists("link", $item)) ? $item["link"] : "";
} }
/** if (empty($ua->text) && isset($item["message"])) {
* Update user status $ua->text = (array_key_exists("message", $item)) ? $item["message"] : "";
* }
* @param mixed $status An array describing the status, or string
* @param string $pageid (optional) User page id if (!empty($ua->text)) {
* @return array $ua->user->identifier = (array_key_exists("id", $item["from"])) ? $item["from"]["id"] : "";
* @throw Exception $ua->user->displayName = (array_key_exists("name", $item["from"])) ? $item["from"]["name"] : "";
*/ $ua->user->profileURL = "https://www.facebook.com/profile.php?id=" . $ua->user->identifier;
function setUserStatus($status, $pageid = null) { $ua->user->photoURL = "https://graph.facebook.com/" . $ua->user->identifier . "/picture?type=square";
if (!is_array($status)) {
$status = array('message' => $status); $activities[] = $ua;
} }
}
if (is_null($pageid)) {
$pageid = 'me'; return $activities;
}
// if post on page, get access_token page
} else {
$access_token = null;
foreach ($this->getUserPages(true) as $p) {
if (isset($p['id']) && intval($p['id']) == intval($pageid)) {
$access_token = $p['access_token'];
break;
}
}
if (is_null($access_token)) {
throw new Exception("Update user page failed, page not found or not writable!");
}
$status['access_token'] = $access_token;
}
try {
$response = $this->api->api('/' . $pageid . '/feed', 'post', $status);
} catch (FacebookApiException $e) {
throw new Exception("Update user status failed! {$this->providerId} returned an error {$e->getMessage()}", 0, $e);
}
return $response;
}
/**
* {@inheridoc}
*/
function getUserStatus($postid) {
try {
$postinfo = $this->api->api("/" . $postid);
} catch (FacebookApiException $e) {
throw new Exception("Cannot retrieve user status! {$this->providerId} returned an error: {$e->getMessage()}", 0, $e);
}
return $postinfo;
}
/**
* {@inheridoc}
*/
function getUserPages($writableonly = false) {
if (( isset($this->config['scope']) && strpos($this->config['scope'], 'manage_pages') === false ) || (!isset($this->config['scope']) && strpos($this->scope, 'manage_pages') === false ))
throw new Exception("User status requires manage_page permission!");
try {
$pages = $this->api->api("/me/accounts", 'get');
} catch (FacebookApiException $e) {
throw new Exception("Cannot retrieve user pages! {$this->providerId} returned an error: {$e->getMessage()}", 0, $e);
}
if (!isset($pages['data'])) {
return array();
}
if (!$writableonly) {
return $pages['data'];
}
$wrpages = array();
foreach ($pages['data'] as $p) {
if (isset($p['perms']) && in_array('CREATE_CONTENT', $p['perms'])) {
$wrpages[] = $p;
}
}
return $wrpages;
}
/**
* load the user latest activity
* - timeline : all the stream
* - me : the user activity only
* {@inheritdoc}
*/
function getUserActivity($stream) {
try {
if ($stream == "me") {
$response = $this->api->api('/me/feed');
} else {
$response = $this->api->api('/me/home');
}
} catch (FacebookApiException $e) {
throw new Exception("User activity stream request failed! {$this->providerId} returned an error: {$e->getMessage()}", 0, $e);
}
if (!$response || !count($response['data'])) {
return array();
}
$activities = array();
foreach ($response['data'] as $item) {
if ($stream == "me" && $item["from"]["id"] != $this->api->getUser()) {
continue;
}
$ua = new Hybrid_User_Activity();
$ua->id = (array_key_exists("id", $item)) ? $item["id"] : "";
$ua->date = (array_key_exists("created_time", $item)) ? strtotime($item["created_time"]) : "";
if ($item["type"] == "video") {
$ua->text = (array_key_exists("link", $item)) ? $item["link"] : "";
}
if ($item["type"] == "link") {
$ua->text = (array_key_exists("link", $item)) ? $item["link"] : "";
}
if (empty($ua->text) && isset($item["story"])) {
$ua->text = (array_key_exists("link", $item)) ? $item["link"] : "";
}
if (empty($ua->text) && isset($item["message"])) {
$ua->text = (array_key_exists("message", $item)) ? $item["message"] : "";
}
if (!empty($ua->text)) {
$ua->user->identifier = (array_key_exists("id", $item["from"])) ? $item["from"]["id"] : "";
$ua->user->displayName = (array_key_exists("name", $item["from"])) ? $item["from"]["name"] : "";
$ua->user->profileURL = "https://www.facebook.com/profile.php?id=" . $ua->user->identifier;
$ua->user->photoURL = "https://graph.facebook.com/" . $ua->user->identifier . "/picture?type=square";
$activities[] = $ua;
}
}
return $activities;
}
} }

View File

@@ -158,12 +158,20 @@ class Hybrid_Providers_Google extends Hybrid_Provider_Model_OAuth2 {
} else { } else {
$this->user->profile->webSiteURL = ''; $this->user->profile->webSiteURL = '';
} }
// google API returns age ranges or min. age only (with plus.login scope) // google API returns age ranges min and/or max as of https://developers.google.com/+/web/api/rest/latest/people#resource
if (property_exists($response, 'ageRange')) { if (property_exists($response, 'ageRange')) {
if (property_exists($response->ageRange, 'min') && property_exists($response->ageRange, 'max')) { if (property_exists($response->ageRange, 'min') && property_exists($response->ageRange, 'max')) {
$this->user->profile->age = $response->ageRange->min . ' - ' . $response->ageRange->max; $this->user->profile->age = $response->ageRange->min . ' - ' . $response->ageRange->max;
} else { } else {
$this->user->profile->age = '> ' . $response->ageRange->min; if (property_exists($response->ageRange, 'min')) {
$this->user->profile->age = '>= ' . $response->ageRange->min;
} else {
if (property_exists($response->ageRange, 'max')) {
$this->user->profile->age = '<= ' . $response->ageRange->max;
} else {
$this->user->profile->age = '';
}
}
} }
} else { } else {
$this->user->profile->age = ''; $this->user->profile->age = '';

View File

@@ -28,6 +28,22 @@ class Hybrid_Providers_LinkedIn extends Hybrid_Provider_Model {
if (!$this->config["keys"]["key"] || !$this->config["keys"]["secret"]) { if (!$this->config["keys"]["key"] || !$this->config["keys"]["secret"]) {
throw new Exception("Your application key and secret are required in order to connect to {$this->providerId}.", 4); throw new Exception("Your application key and secret are required in order to connect to {$this->providerId}.", 4);
} }
if (empty($this->config['fields'])) {
$this->config['fields'] = array(
'id',
'first-name',
'last-name',
'public-profile-url',
'picture-url',
'email-address',
'date-of-birth',
'phone-numbers',
'summary',
'positions'
);
}
if (!class_exists('OAuthConsumer', false)) { if (!class_exists('OAuthConsumer', false)) {
require_once Hybrid_Auth::$config["path_libraries"] . "OAuth/OAuth.php"; require_once Hybrid_Auth::$config["path_libraries"] . "OAuth/OAuth.php";
} }
@@ -97,7 +113,7 @@ class Hybrid_Providers_LinkedIn extends Hybrid_Provider_Model {
function getUserProfile() { function getUserProfile() {
try { try {
// http://developer.linkedin.com/docs/DOC-1061 // http://developer.linkedin.com/docs/DOC-1061
$response = $this->api->profile('~:(id,first-name,last-name,public-profile-url,picture-url,email-address,date-of-birth,phone-numbers,summary)'); $response = $this->api->profile('~:('. implode(',', $this->config['fields']) .')');
} catch (LinkedInException $e) { } catch (LinkedInException $e) {
throw new Exception("User profile request failed! {$this->providerId} returned an error: {$e->getMessage()}", 6, $e); throw new Exception("User profile request failed! {$this->providerId} returned an error: {$e->getMessage()}", 6, $e);
} }
@@ -117,7 +133,22 @@ class Hybrid_Providers_LinkedIn extends Hybrid_Provider_Model {
$this->user->profile->email = (string) $data->{'email-address'}; $this->user->profile->email = (string) $data->{'email-address'};
$this->user->profile->emailVerified = (string) $data->{'email-address'}; $this->user->profile->emailVerified = (string) $data->{'email-address'};
$this->user->profile->photoURL = (string) $data->{'picture-url'}; if ($data->{'positions'}) {
$this->user->profile->job_title = (string) $data->{'positions'}->{'position'}->{'title'};
$this->user->profile->organization_name = (string) $data->{'positions'}->{'position'}->{'company'}->{'name'};
}
if (isset($data->{'picture-url'})) {
$this->user->profile->photoURL = (string) $data->{'picture-url'};
} elseif (isset($data->{'picture-urls'})) {
// picture-urls::(original)
$this->user->profile->photoURL = (string) $data->{'picture-urls'}->{'picture-url'};
} else {
$this->user->profile->photoURL = "";
}
$this->user->profile->profileURL = (string) $data->{'public-profile-url'}; $this->user->profile->profileURL = (string) $data->{'public-profile-url'};
$this->user->profile->description = (string) $data->{'summary'}; $this->user->profile->description = (string) $data->{'summary'};

View File

@@ -131,6 +131,7 @@ class Hybrid_Providers_Twitter extends Hybrid_Provider_Model_OAuth1 {
$this->user->profile->webSiteURL = (property_exists($response, 'url')) ? $response->url : ""; $this->user->profile->webSiteURL = (property_exists($response, 'url')) ? $response->url : "";
$this->user->profile->region = (property_exists($response, 'location')) ? $response->location : ""; $this->user->profile->region = (property_exists($response, 'location')) ? $response->location : "";
if($includeEmail) $this->user->profile->email = (property_exists($response, 'email')) ? $response->email : ""; if($includeEmail) $this->user->profile->email = (property_exists($response, 'email')) ? $response->email : "";
if($includeEmail) $this->user->profile->emailVerified = (property_exists($response, 'email')) ? $response->email : "";
return $this->user->profile; return $this->user->profile;
} }

View File

@@ -149,4 +149,15 @@ class Hybrid_User_Profile {
*/ */
public $zip = null; public $zip = null;
/**
* Job title
* @var string
*/
public $job_title = null;
/**
* Organization name
* @var string
*/
public $organization_name = null;
} }

View File

@@ -0,0 +1,125 @@
<?php
/**
* HybridAuth
* http://hybridauth.sourceforge.net | http://github.com/hybridauth/hybridauth
* (c) 2009-2015, HybridAuth authors | http://hybridauth.sourceforge.net/licenses.html
*/
/**
* A service client for the Amazon ID OAuth 2 flow.
*
* The sole purpose of this subclass is to make sure the POST params
* for cURL are provided as an urlencoded string rather than an array.
* This is because Amazon requires COntent-Type header to be application/x-www-form-urlencoded,
* which cURL overrides to multipart/form-data when POST fields are provided as an array
*
* The only difference from Oauth2CLient in authenticate() method is http_build_query()
* wrapped around $params. request() and parseRequestResult() methods are exact copies
* from Oauth2Client. They are copied here because private scope does not allow calling them
* from subclass.
*
* @link http://stackoverflow.com/questions/5224790/curl-post-format-for-curlopt-postfields
*
*/
class AmazonOAuth2Client extends OAuth2Client {
public function authenticate( $code ) {
$params = array(
"client_id" => $this->client_id,
"client_secret" => $this->client_secret,
"grant_type" => 'authorization_code',
"redirect_uri" => $this->redirect_uri,
"code" => $code,
);
$response = $this->request( $this->token_url, http_build_query($params), $this->curl_authenticate_method );
$response = $this->parseRequestResult( $response );
if ( ! $response || ! isset( $response->access_token ) ){
throw new Exception( "The Authorization Service has return: " . $response->error );
}
if( isset( $response->access_token ) ) $this->access_token = $response->access_token;
if( isset( $response->refresh_token ) ) $this->refresh_token = $response->refresh_token;
if( isset( $response->expires_in ) ) $this->access_token_expires_in = $response->expires_in;
// calculate when the access token expire
if( isset($response->expires_in)) {
$this->access_token_expires_at = time() + $response->expires_in;
}
return $response;
}
private function request( $url, $params=false, $type="GET" )
{
Hybrid_Logger::info( "Enter OAuth2Client::request( $url )" );
Hybrid_Logger::debug( "OAuth2Client::request(). dump request params: ", serialize( $params ) );
if( $type == "GET" ){
$url = $url . ( strpos( $url, '?' ) ? '&' : '?' ) . http_build_query($params, '', '&');
}
$this->http_info = array();
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL , $url );
curl_setopt($ch, CURLOPT_RETURNTRANSFER , 1 );
curl_setopt($ch, CURLOPT_TIMEOUT , $this->curl_time_out );
curl_setopt($ch, CURLOPT_USERAGENT , $this->curl_useragent );
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT , $this->curl_connect_time_out );
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER , $this->curl_ssl_verifypeer );
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST , $this->curl_ssl_verifyhost );
curl_setopt($ch, CURLOPT_HTTPHEADER , $this->curl_header );
if ($this->curl_compressed){
curl_setopt($ch, CURLOPT_ENCODING, "gzip,deflate");
}
if($this->curl_proxy){
curl_setopt( $ch, CURLOPT_PROXY , $this->curl_proxy);
}
if( $type == "POST" ){
curl_setopt($ch, CURLOPT_POST, 1);
if($params) curl_setopt( $ch, CURLOPT_POSTFIELDS, $params );
}
if( $type == "DELETE" ){
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
}
if( $type == "PATCH" ){
curl_setopt($ch, CURLOPT_POST, 1);
if($params) curl_setopt( $ch, CURLOPT_POSTFIELDS, $params );
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
}
$response = curl_exec($ch);
if( $response === false ) {
Hybrid_Logger::error( "OAuth2Client::request(). curl_exec error: ", curl_error($ch) );
}
Hybrid_Logger::debug( "OAuth2Client::request(). dump request info: ", serialize( curl_getinfo($ch) ) );
Hybrid_Logger::debug( "OAuth2Client::request(). dump request result: ", serialize( $response ) );
$this->http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$this->http_info = array_merge($this->http_info, curl_getinfo($ch));
curl_close ($ch);
return $response;
}
private function parseRequestResult( $result )
{
if( json_decode( $result ) ) return json_decode( $result );
parse_str( $result, $output );
$result = new StdClass();
foreach( $output as $k => $v )
$result->$k = $v;
return $result;
}
}

View File

@@ -26,7 +26,6 @@ class OAuth2Client
//-- //--
public $sign_token_name = "access_token"; public $sign_token_name = "access_token";
public $decode_json = true;
public $curl_time_out = 30; public $curl_time_out = 30;
public $curl_connect_time_out = 30; public $curl_connect_time_out = 30;
public $curl_ssl_verifypeer = false; public $curl_ssl_verifypeer = false;
@@ -127,7 +126,7 @@ class OAuth2Client
/** /**
* Format and sign an oauth for provider api * Format and sign an oauth for provider api
*/ */
public function api( $url, $method = "GET", $parameters = array() ) public function api( $url, $method = "GET", $parameters = array(), $decode_json = true )
{ {
if ( strrpos($url, 'http://') !== 0 && strrpos($url, 'https://') !== 0 ) { if ( strrpos($url, 'http://') !== 0 && strrpos($url, 'https://') !== 0 ) {
$url = $this->api_base_url . $url; $url = $this->api_base_url . $url;
@@ -139,9 +138,11 @@ class OAuth2Client
switch( $method ){ switch( $method ){
case 'GET' : $response = $this->request( $url, $parameters, "GET" ); break; case 'GET' : $response = $this->request( $url, $parameters, "GET" ); break;
case 'POST' : $response = $this->request( $url, $parameters, "POST" ); break; case 'POST' : $response = $this->request( $url, $parameters, "POST" ); break;
case 'DELETE' : $response = $this->request( $url, $parameters, "DELETE" ); break;
case 'PATCH' : $response = $this->request( $url, $parameters, "PATCH" ); break;
} }
if( $response && $this->decode_json ){ if( $response && $decode_json ){
return $this->response = json_decode( $response ); return $this->response = json_decode( $response );
} }
@@ -161,17 +162,17 @@ class OAuth2Client
/** /**
* GET wrapper for provider apis request * GET wrapper for provider apis request
*/ */
function get( $url, $parameters = array() ) function get( $url, $parameters = array(), $decode_json = true )
{ {
return $this->api( $url, 'GET', $parameters ); return $this->api( $url, 'GET', $parameters, $decode_json );
} }
/** /**
* POST wrapper for provider apis request * POST wrapper for provider apis request
*/ */
function post( $url, $parameters = array() ) function post( $url, $parameters = array(), $decode_json = true )
{ {
return $this->api( $url, 'POST', $parameters ); return $this->api( $url, 'POST', $parameters, $decode_json );
} }
// -- tokens // -- tokens
@@ -234,7 +235,14 @@ class OAuth2Client
curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POST, 1);
if($params) curl_setopt( $ch, CURLOPT_POSTFIELDS, $params ); if($params) curl_setopt( $ch, CURLOPT_POSTFIELDS, $params );
} }
if( $type == "DELETE" ){
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
}
if( $type == "PATCH" ){
curl_setopt($ch, CURLOPT_POST, 1);
if($params) curl_setopt( $ch, CURLOPT_POSTFIELDS, $params );
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
}
$response = curl_exec($ch); $response = curl_exec($ch);
if( $response === false ) { if( $response === false ) {
Hybrid_Logger::error( "OAuth2Client::request(). curl_exec error: ", curl_error($ch) ); Hybrid_Logger::error( "OAuth2Client::request(). curl_exec error: ", curl_error($ch) );
@@ -263,4 +271,18 @@ class OAuth2Client
return $result; return $result;
} }
/**
* DELETE wrapper for provider apis request
*/
function delete( $url, $parameters = array() )
{
return $this->api( $url, 'DELETE', $parameters );
}
/**
* PATCH wrapper for provider apis request
*/
function patch( $url, $parameters = array() )
{
return $this->api( $url, 'PATCH', $parameters );
}
} }

View File

@@ -2,7 +2,7 @@
/** /**
* HybridAuth * HybridAuth
* http://hybridauth.sourceforge.net | http://github.com/hybridauth/hybridauth * http://hybridauth.sourceforge.net | http://github.com/hybridauth/hybridauth
* (c) 2009-2014, HybridAuth authors | http://hybridauth.sourceforge.net/licenses.html * (c) 2009-2015, HybridAuth authors | http://hybridauth.sourceforge.net/licenses.html
*/ */
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------