File manager - Edit - /home/aresglob/public_html/wp/wp-includes/images/smilies/hybridauth.tar
Back
Provider/Google.php 0000644 00000013247 15077512767 0010313 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Provider; use Hybridauth\Adapter\OAuth2; use Hybridauth\Exception\UnexpectedApiResponseException; use Hybridauth\Data; use Hybridauth\User; /** * Google OAuth2 provider adapter. * * Example: * * $config = [ * 'callback' => Hybridauth\HttpClient\Util::getCurrentUrl(), * 'keys' => ['id' => '', 'secret' => ''], * 'scope' => 'https://www.googleapis.com/auth/userinfo.profile', * * // google's custom auth url params * 'authorize_url_parameters' => [ * 'approval_prompt' => 'force', // to pass only when you need to acquire a new refresh token. * 'access_type' => .., // is set to 'offline' by default * 'hd' => .., * 'state' => .., * // etc. * ] * ]; * * $adapter = new Hybridauth\Provider\Google($config); * * try { * $adapter->authenticate(); * * $userProfile = $adapter->getUserProfile(); * $tokens = $adapter->getAccessToken(); * $contacts = $adapter->getUserContacts(['max-results' => 75]); * } catch (\Exception $e) { * echo $e->getMessage() ; * } */ class Google extends OAuth2 { /** * {@inheritdoc} */ // phpcs:ignore protected $scope = 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email'; /** * {@inheritdoc} */ protected $apiBaseUrl = 'https://www.googleapis.com/'; /** * {@inheritdoc} */ protected $authorizeUrl = 'https://accounts.google.com/o/oauth2/v2/auth'; /** * {@inheritdoc} */ protected $accessTokenUrl = 'https://oauth2.googleapis.com/token'; /** * {@inheritdoc} */ protected $apiDocumentation = 'https://developers.google.com/identity/protocols/OAuth2'; /** * {@inheritdoc} */ protected function initialize() { parent::initialize(); $this->AuthorizeUrlParameters += [ 'access_type' => 'offline' ]; if ($this->isRefreshTokenAvailable()) { $this->tokenRefreshParameters += [ 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret ]; } } /** * {@inheritdoc} * * See: https://developers.google.com/identity/protocols/OpenIDConnect#obtainuserinfo */ public function getUserProfile() { $response = $this->apiRequest('oauth2/v3/userinfo'); $data = new Data\Collection($response); if (!$data->exists('sub')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } $userProfile = new User\Profile(); $userProfile->identifier = $data->get('sub'); $userProfile->firstName = $data->get('given_name'); $userProfile->lastName = $data->get('family_name'); $userProfile->displayName = $data->get('name'); $userProfile->photoURL = $data->get('picture'); $userProfile->profileURL = $data->get('profile'); $userProfile->gender = $data->get('gender'); $userProfile->language = $data->get('locale'); $userProfile->email = $data->get('email'); $userProfile->emailVerified = $data->get('email_verified') ? $userProfile->email : ''; if ($this->config->get('photo_size')) { $userProfile->photoURL .= '?sz=' . $this->config->get('photo_size'); } return $userProfile; } /** * {@inheritdoc} */ public function getUserContacts($parameters = []) { $parameters = ['max-results' => 500] + $parameters; // Google Gmail and Android contacts if (false !== strpos($this->scope, '/m8/feeds/') || false !== strpos($this->scope, '/auth/contacts.readonly')) { return $this->getGmailContacts($parameters); } return []; } /** * Retrieve Gmail contacts * * @param array $parameters * * @return array * * @throws \Exception */ protected function getGmailContacts($parameters = []) { $url = 'https://www.google.com/m8/feeds/contacts/default/full?' . http_build_query(array_replace(['alt' => 'json', 'v' => '3.0'], (array)$parameters)); $response = $this->apiRequest($url); if (!$response) { return []; } $contacts = []; if (isset($response->feed->entry)) { foreach ($response->feed->entry as $idx => $entry) { $uc = new User\Contact(); $uc->email = isset($entry->{'gd$email'}[0]->address) ? (string)$entry->{'gd$email'}[0]->address : ''; $uc->displayName = isset($entry->title->{'$t'}) ? (string)$entry->title->{'$t'} : ''; $uc->identifier = ($uc->email != '') ? $uc->email : ''; $uc->description = ''; if (property_exists($response, 'website')) { if (is_array($response->website)) { foreach ($response->website as $w) { if ($w->primary == true) { $uc->webSiteURL = $w->value; } } } else { $uc->webSiteURL = $response->website->value; } } else { $uc->webSiteURL = ''; } $contacts[] = $uc; } } return $contacts; } } Provider/GitHub.php 0000644 00000005556 15077512767 0010265 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Provider; use Hybridauth\Adapter\OAuth2; use Hybridauth\Exception\UnexpectedApiResponseException; use Hybridauth\Data; use Hybridauth\User; /** * Github OAuth2 provider adapter. */ class GitHub extends OAuth2 { /** * {@inheritdoc} */ protected $scope = 'user:email'; /** * {@inheritdoc} */ protected $apiBaseUrl = 'https://api.github.com/'; /** * {@inheritdoc} */ protected $authorizeUrl = 'https://github.com/login/oauth/authorize'; /** * {@inheritdoc} */ protected $accessTokenUrl = 'https://github.com/login/oauth/access_token'; /** * {@inheritdoc} */ protected $apiDocumentation = 'https://developer.github.com/v3/oauth/'; /** * {@inheritdoc} */ public function getUserProfile() { $response = $this->apiRequest('user'); $data = new Data\Collection($response); if (!$data->exists('id')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } $userProfile = new User\Profile(); $userProfile->identifier = $data->get('id'); $userProfile->displayName = $data->get('name'); $userProfile->description = $data->get('bio'); $userProfile->photoURL = $data->get('avatar_url'); $userProfile->profileURL = $data->get('html_url'); $userProfile->email = $data->get('email'); $userProfile->webSiteURL = $data->get('blog'); $userProfile->region = $data->get('location'); $userProfile->displayName = $userProfile->displayName ?: $data->get('login'); if (empty($userProfile->email) && strpos($this->scope, 'user:email') !== false) { try { // user email is not mandatory so keep it quite. $userProfile = $this->requestUserEmail($userProfile); } catch (\Exception $e) { } } return $userProfile; } /** * Request connected user email * * https://developer.github.com/v3/users/emails/ * @param User\Profile $userProfile * * @return User\Profile * * @throws \Exception */ protected function requestUserEmail(User\Profile $userProfile) { $response = $this->apiRequest('user/emails'); foreach ($response as $idx => $item) { if (!empty($item->primary) && $item->primary == 1) { $userProfile->email = $item->email; if (!empty($item->verified) && $item->verified == 1) { $userProfile->emailVerified = $userProfile->email; } break; } } return $userProfile; } } Provider/Twitter.php 0000644 00000017147 15077512767 0010544 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Provider; use Hybridauth\Adapter\OAuth1; use Hybridauth\Exception\UnexpectedApiResponseException; use Hybridauth\Data; use Hybridauth\User; /** * Twitter OAuth1 provider adapter. * Uses OAuth1 not OAuth2 because many Twitter endpoints are built around OAuth1. * * Example: * * $config = [ * 'callback' => Hybridauth\HttpClient\Util::getCurrentUrl(), * 'keys' => ['key' => '', 'secret' => ''], // OAuth1 uses 'key' not 'id' * 'authorize' => true // Needed to perform actions on behalf of users (see below link) * // https://developer.twitter.com/en/docs/authentication/oauth-1-0a/obtaining-user-access-tokens * ]; * * $adapter = new Hybridauth\Provider\Twitter($config); * * try { * $adapter->authenticate(); * * $userProfile = $adapter->getUserProfile(); * $tokens = $adapter->getAccessToken(); * $contacts = $adapter->getUserContacts(['screen_name' =>'andypiper']); // get those of @andypiper * $activity = $adapter->getUserActivity('me'); * } catch (\Exception $e) { * echo $e->getMessage() ; * } */ class Twitter extends OAuth1 { /** * {@inheritdoc} */ protected $apiBaseUrl = 'https://api.twitter.com/1.1/'; /** * {@inheritdoc} */ protected $authorizeUrl = 'https://api.twitter.com/oauth/authenticate'; /** * {@inheritdoc} */ protected $requestTokenUrl = 'https://api.twitter.com/oauth/request_token'; /** * {@inheritdoc} */ protected $accessTokenUrl = 'https://api.twitter.com/oauth/access_token'; /** * {@inheritdoc} */ protected $apiDocumentation = 'https://dev.twitter.com/web/sign-in/implementing'; /** * {@inheritdoc} */ protected function getAuthorizeUrl($parameters = []) { if ($this->config->get('authorize') === true) { $this->authorizeUrl = 'https://api.twitter.com/oauth/authorize'; } return parent::getAuthorizeUrl($parameters); } /** * {@inheritdoc} */ public function getUserProfile() { $response = $this->apiRequest('account/verify_credentials.json', 'GET', [ 'include_email' => $this->config->get('include_email') === false ? 'false' : 'true', ]); $data = new Data\Collection($response); if (!$data->exists('id_str')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } $userProfile = new User\Profile(); $userProfile->identifier = $data->get('id_str'); $userProfile->displayName = $data->get('screen_name'); $userProfile->description = $data->get('description'); $userProfile->firstName = $data->get('name'); $userProfile->email = $data->get('email'); $userProfile->emailVerified = $data->get('email'); $userProfile->webSiteURL = $data->get('url'); $userProfile->region = $data->get('location'); $userProfile->profileURL = $data->exists('screen_name') ? ('https://twitter.com/' . $data->get('screen_name')) : ''; $photoSize = $this->config->get('photo_size') ?: 'original'; $photoSize = $photoSize === 'original' ? '' : "_{$photoSize}"; $userProfile->photoURL = $data->exists('profile_image_url_https') ? str_replace('_normal', $photoSize, $data->get('profile_image_url_https')) : ''; $userProfile->data = [ 'followed_by' => $data->get('followers_count'), 'follows' => $data->get('friends_count'), ]; return $userProfile; } /** * {@inheritdoc} */ public function getUserContacts($parameters = []) { $parameters = ['cursor' => '-1'] + $parameters; $response = $this->apiRequest('friends/ids.json', 'GET', $parameters); $data = new Data\Collection($response); if (!$data->exists('ids')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } if ($data->filter('ids')->isEmpty()) { return []; } $contacts = []; // 75 id per time should be okey $contactsIds = array_chunk((array)$data->get('ids'), 75); foreach ($contactsIds as $chunk) { $parameters = ['user_id' => implode(',', $chunk)]; try { $response = $this->apiRequest('users/lookup.json', 'GET', $parameters); if ($response && count($response)) { foreach ($response as $item) { $contacts[] = $this->fetchUserContact($item); } } } catch (\Exception $e) { continue; } } return $contacts; } /** * @param $item * * @return User\Contact */ protected function fetchUserContact($item) { $item = new Data\Collection($item); $userContact = new User\Contact(); $userContact->identifier = $item->get('id_str'); $userContact->displayName = $item->get('name'); $userContact->photoURL = $item->get('profile_image_url'); $userContact->description = $item->get('description'); $userContact->profileURL = $item->exists('screen_name') ? ('https://twitter.com/' . $item->get('screen_name')) : ''; return $userContact; } /** * {@inheritdoc} */ public function setUserStatus($status) { if (is_string($status)) { $status = ['status' => $status]; } // Prepare request parameters. $params = []; if (isset($status['status'])) { $params['status'] = $status['status']; } if (isset($status['picture'])) { $media = $this->apiRequest('https://upload.twitter.com/1.1/media/upload.json', 'POST', [ 'media' => base64_encode(file_get_contents($status['picture'])), ]); $params['media_ids'] = $media->media_id; } $response = $this->apiRequest('statuses/update.json', 'POST', $params); return $response; } /** * {@inheritdoc} */ public function getUserActivity($stream = 'me') { $apiUrl = ($stream == 'me') ? 'statuses/user_timeline.json' : 'statuses/home_timeline.json'; $response = $this->apiRequest($apiUrl); if (!$response) { return []; } $activities = []; foreach ($response as $item) { $activities[] = $this->fetchUserActivity($item); } return $activities; } /** * @param $item * @return User\Activity */ protected function fetchUserActivity($item) { $item = new Data\Collection($item); $userActivity = new User\Activity(); $userActivity->id = $item->get('id_str'); $userActivity->date = $item->get('created_at'); $userActivity->text = $item->get('text'); $userActivity->user->identifier = $item->filter('user')->get('id_str'); $userActivity->user->displayName = $item->filter('user')->get('name'); $userActivity->user->photoURL = $item->filter('user')->get('profile_image_url'); $userActivity->user->profileURL = $item->filter('user')->get('screen_name') ? ('https://twitter.com/' . $item->filter('user')->get('screen_name')) : ''; return $userActivity; } } Provider/Facebook.php 0000644 00000032157 15077512767 0010611 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Provider; use Hybridauth\Exception\InvalidArgumentException; use Hybridauth\Exception\UnexpectedApiResponseException; use Hybridauth\Adapter\OAuth2; use Hybridauth\Data; use Hybridauth\User; /** * Facebook OAuth2 provider adapter. * * Facebook doesn't use standard OAuth refresh tokens. * Instead it has a "token exchange" system. You exchange the token prior to * expiry, to push back expiry. You start with a short-lived token and each * exchange gives you a long-lived one (90 days). * We control this with the 'exchange_by_expiry_days' option. * * Example: * * $config = [ * 'callback' => Hybridauth\HttpClient\Util::getCurrentUrl(), * 'keys' => ['id' => '', 'secret' => ''], * 'scope' => 'email, user_status, user_posts', * 'exchange_by_expiry_days' => 45, // null for no token exchange * ]; * * $adapter = new Hybridauth\Provider\Facebook($config); * * try { * $adapter->authenticate(); * * $userProfile = $adapter->getUserProfile(); * $tokens = $adapter->getAccessToken(); * $response = $adapter->setUserStatus("Hybridauth test message.."); * } catch (\Exception $e) { * echo $e->getMessage() ; * } */ class Facebook extends OAuth2 { /** * {@inheritdoc} */ protected $scope = 'email, public_profile'; /** * {@inheritdoc} */ protected $apiBaseUrl = 'https://graph.facebook.com/v8.0/'; /** * {@inheritdoc} */ protected $authorizeUrl = 'https://www.facebook.com/dialog/oauth'; /** * {@inheritdoc} */ protected $accessTokenUrl = 'https://graph.facebook.com/oauth/access_token'; /** * {@inheritdoc} */ protected $apiDocumentation = 'https://developers.facebook.com/docs/facebook-login/overview'; /** * @var string Profile URL template as the fallback when no `link` returned from the API. */ protected $profileUrlTemplate = 'https://www.facebook.com/%s'; /** * {@inheritdoc} */ protected function initialize() { parent::initialize(); // Require proof on all Facebook api calls // https://developers.facebook.com/docs/graph-api/securing-requests#appsecret_proof if ($accessToken = $this->getStoredData('access_token')) { $this->apiRequestParameters['appsecret_proof'] = hash_hmac('sha256', $accessToken, $this->clientSecret); } } /** * {@inheritdoc} */ public function maintainToken() { if (!$this->isConnected()) { return; } // Handle token exchange prior to the standard handler for an API request $exchange_by_expiry_days = $this->config->get('exchange_by_expiry_days') ?: 45; if ($exchange_by_expiry_days !== null) { $projected_timestamp = time() + 60 * 60 * 24 * $exchange_by_expiry_days; if (!$this->hasAccessTokenExpired() && $this->hasAccessTokenExpired($projected_timestamp)) { $this->exchangeAccessToken(); } } } /** * Exchange the Access Token with one that expires further in the future. * * @return string Raw Provider API response * @throws \Hybridauth\Exception\HttpClientFailureException * @throws \Hybridauth\Exception\HttpRequestFailedException * @throws \Hybridauth\Exception\InvalidAccessTokenException */ public function exchangeAccessToken() { $exchangeTokenParameters = [ 'grant_type' => 'fb_exchange_token', 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'fb_exchange_token' => $this->getStoredData('access_token'), ]; $response = $this->httpClient->request( $this->accessTokenUrl, 'GET', $exchangeTokenParameters ); $this->validateApiResponse('Unable to exchange the access token'); $this->validateAccessTokenExchange($response); return $response; } /** * {@inheritdoc} */ public function getUserProfile() { $fields = [ 'id', 'name', 'first_name', 'last_name', 'website', 'locale', 'about', 'email', 'hometown', 'birthday', ]; if (strpos($this->scope, 'user_link') !== false) { $fields[] = 'link'; } if (strpos($this->scope, 'user_gender') !== false) { $fields[] = 'gender'; } // Note that en_US is needed for gender fields to match convention. $locale = $this->config->get('locale') ?: 'en_US'; $response = $this->apiRequest('me', 'GET', [ 'fields' => implode(',', $fields), 'locale' => $locale, ]); $data = new Data\Collection($response); if (!$data->exists('id')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } $userProfile = new User\Profile(); $userProfile->identifier = $data->get('id'); $userProfile->displayName = $data->get('name'); $userProfile->firstName = $data->get('first_name'); $userProfile->lastName = $data->get('last_name'); $userProfile->profileURL = $data->get('link'); $userProfile->webSiteURL = $data->get('website'); $userProfile->gender = $data->get('gender'); $userProfile->language = $data->get('locale'); $userProfile->description = $data->get('about'); $userProfile->email = $data->get('email'); // Fallback for profile URL in case Facebook does not provide "pretty" link with username (if user set it). if (empty($userProfile->profileURL)) { $userProfile->profileURL = $this->getProfileUrl($userProfile->identifier); } $userProfile->region = $data->filter('hometown')->get('name'); $photoSize = $this->config->get('photo_size') ?: '150'; $userProfile->photoURL = $this->apiBaseUrl . $userProfile->identifier; $userProfile->photoURL .= '/picture?width=' . $photoSize . '&height=' . $photoSize; $userProfile->emailVerified = $userProfile->email; $userProfile = $this->fetchUserRegion($userProfile); $userProfile = $this->fetchBirthday($userProfile, $data->get('birthday')); return $userProfile; } /** * Retrieve the user region. * * @param User\Profile $userProfile * * @return \Hybridauth\User\Profile */ protected function fetchUserRegion(User\Profile $userProfile) { if (!empty($userProfile->region)) { $regionArr = explode(',', $userProfile->region); if (count($regionArr) > 1) { $userProfile->city = trim($regionArr[0]); $userProfile->country = trim($regionArr[1]); } } return $userProfile; } /** * Retrieve the user birthday. * * @param User\Profile $userProfile * @param string $birthday * * @return \Hybridauth\User\Profile */ protected function fetchBirthday(User\Profile $userProfile, $birthday) { $result = (new Data\Parser())->parseBirthday($birthday); $userProfile->birthYear = (int)$result[0]; $userProfile->birthMonth = (int)$result[1]; $userProfile->birthDay = (int)$result[2]; return $userProfile; } /** * /v2.0/me/friends only returns the user's friends who also use the app. * In the cases where you want to let people tag their friends in stories published by your app, * you can use the Taggable Friends API. * * https://developers.facebook.com/docs/apps/faq#unable_full_friend_list */ public function getUserContacts() { $contacts = []; $apiUrl = 'me/friends?fields=link,name'; do { $response = $this->apiRequest($apiUrl); $data = new Data\Collection($response); if (!$data->exists('data')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } if (!$data->filter('data')->isEmpty()) { foreach ($data->filter('data')->toArray() as $item) { $contacts[] = $this->fetchUserContact($item); } } if ($data->filter('paging')->exists('next')) { $apiUrl = $data->filter('paging')->get('next'); $pagedList = true; } else { $pagedList = false; } } while ($pagedList); return $contacts; } /** * Parse the user contact. * * @param array $item * * @return \Hybridauth\User\Contact */ protected function fetchUserContact($item) { $userContact = new User\Contact(); $item = new Data\Collection($item); $userContact->identifier = $item->get('id'); $userContact->displayName = $item->get('name'); $userContact->profileURL = $item->exists('link') ?: $this->getProfileUrl($userContact->identifier); $userContact->photoURL = $this->apiBaseUrl . $userContact->identifier . '/picture?width=150&height=150'; return $userContact; } /** * {@inheritdoc} */ public function setPageStatus($status, $pageId) { $status = is_string($status) ? ['message' => $status] : $status; // Post on user wall. if ($pageId === 'me') { return $this->setUserStatus($status); } // Retrieve writable user pages and filter by given one. $pages = $this->getUserPages(true); $pages = array_filter($pages, function ($page) use ($pageId) { return $page->id == $pageId; }); if (!$pages) { throw new InvalidArgumentException('Could not find a page with given id.'); } $page = reset($pages); // Use page access token instead of user access token. $headers = [ 'Authorization' => 'Bearer ' . $page->access_token, ]; // Refresh proof for API call. $parameters = $status + [ 'appsecret_proof' => hash_hmac('sha256', $page->access_token, $this->clientSecret), ]; $response = $this->apiRequest("{$pageId}/feed", 'POST', $parameters, $headers); return $response; } /** * {@inheritdoc} */ public function getUserPages($writable = false) { $pages = $this->apiRequest('me/accounts'); if (!$writable) { return $pages->data; } // Filter user pages by CREATE_CONTENT permission. return array_filter($pages->data, function ($page) { return in_array('CREATE_CONTENT', $page->tasks); }); } /** * {@inheritdoc} */ public function getUserActivity($stream = 'me') { $apiUrl = $stream == 'me' ? 'me/feed' : 'me/home'; $response = $this->apiRequest($apiUrl); $data = new Data\Collection($response); if (!$data->exists('data')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } $activities = []; foreach ($data->filter('data')->toArray() as $item) { $activities[] = $this->fetchUserActivity($item); } return $activities; } /** * @param $item * * @return User\Activity */ protected function fetchUserActivity($item) { $userActivity = new User\Activity(); $item = new Data\Collection($item); $userActivity->id = $item->get('id'); $userActivity->date = $item->get('created_time'); if ('video' == $item->get('type') || 'link' == $item->get('type')) { $userActivity->text = $item->get('link'); } if (empty($userActivity->text) && $item->exists('story')) { $userActivity->text = $item->get('link'); } if (empty($userActivity->text) && $item->exists('message')) { $userActivity->text = $item->get('message'); } if (!empty($userActivity->text) && $item->exists('from')) { $userActivity->user->identifier = $item->filter('from')->get('id'); $userActivity->user->displayName = $item->filter('from')->get('name'); $userActivity->user->profileURL = $this->getProfileUrl($userActivity->user->identifier); $userActivity->user->photoURL = $this->apiBaseUrl . $userActivity->user->identifier; $userActivity->user->photoURL .= '/picture?width=150&height=150'; } return $userActivity; } /** * Get profile URL. * * @param int $identity User ID. * @return string|null NULL when identity is not provided. */ protected function getProfileUrl($identity) { if (!is_numeric($identity)) { return null; } return sprintf($this->profileUrlTemplate, $identity); } } Provider/MicrosoftGraph.php 0000644 00000012666 15077512767 0012032 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Provider; use Hybridauth\Adapter\OAuth2; use Hybridauth\Data; use Hybridauth\Exception\UnexpectedApiResponseException; use Hybridauth\User; /** * Microsoft Graph OAuth2 provider adapter. * * Create an "Azure Active Directory" resource at https://portal.azure.com/ * (not from the Visual Studio site). * * The "Supported account types" choice maps to the 'tenant' setting, see "Authority" @ * https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-client-application-configuration * * Example: * * $config = [ * 'callback' => Hybridauth\HttpClient\Util::getCurrentUrl(), * 'keys' => ['id' => '', 'secret' => ''], * 'tenant' => 'user', * // ^ May be 'common', 'organizations' or 'consumers' or a specific tenant ID or a domain * ]; * * $adapter = new Hybridauth\Provider\MicrosoftGraph($config); * * try { * $adapter->authenticate(); * * $userProfile = $adapter->getUserProfile(); * $tokens = $adapter->getAccessToken(); * } catch (\Exception $e) { * echo $e->getMessage() ; * } */ class MicrosoftGraph extends OAuth2 { /** * {@inheritdoc} */ protected $scope = 'openid user.read contacts.read offline_access'; /** * {@inheritdoc} */ protected $apiBaseUrl = 'https://graph.microsoft.com/v1.0/'; /** * {@inheritdoc} */ protected $authorizeUrl = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize'; /** * {@inheritdoc} */ protected $accessTokenUrl = 'https://login.microsoftonline.com/common/oauth2/v2.0/token'; /** * {@inheritdoc} */ protected $apiDocumentation = 'https://developer.microsoft.com/en-us/graph/docs/concepts/php'; /** * {@inheritdoc} */ protected function initialize() { parent::initialize(); $this->AuthorizeUrlParameters += [ 'prompt' => 'consent', ]; $tenant = $this->config->get('tenant'); if (!empty($tenant)) { $adjustedEndpoints = [ 'authorize_url' => str_replace('/common/', '/' . $tenant . '/', $this->authorizeUrl), 'access_token_url' => str_replace('/common/', '/' . $tenant . '/', $this->accessTokenUrl), ]; $this->setApiEndpoints($adjustedEndpoints); } if ($this->isRefreshTokenAvailable()) { $this->tokenRefreshParameters += [ 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, ]; } } /** * {@inheritdoc} */ public function getUserProfile() { $response = $this->apiRequest('me'); $data = new Data\Collection($response); if (!$data->exists('id')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } $userProfile = new User\Profile(); $userProfile->identifier = $data->get('id'); $userProfile->displayName = $data->get('displayName'); $userProfile->firstName = $data->get('givenName'); $userProfile->lastName = $data->get('surname'); $userProfile->language = $data->get('preferredLanguage'); $userProfile->phone = $data->get('mobilePhone'); if (empty($userProfile->phone)) { $businessPhones = $data->get('businessPhones'); if (isset($businessPhones[0])) { $userProfile->phone = $businessPhones[0]; } } $userProfile->email = $data->get('mail'); if (empty($userProfile->email)) { $email = $data->get('userPrincipalName'); if (strpos($email, '@') !== false) { $userProfile->email = $email; } } if(!empty($userProfile->email)){ $userProfile->emailVerified = true; } return $userProfile; } /** * {@inheritdoc} */ public function getUserContacts() { $apiUrl = 'me/contacts?$top=50'; $contacts = []; do { $response = $this->apiRequest($apiUrl); $data = new Data\Collection($response); if (!$data->exists('value')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } foreach ($data->filter('value')->toArray() as $entry) { $entry = new Data\Collection($entry); $userContact = new User\Contact(); $userContact->identifier = $entry->get('id'); $userContact->displayName = $entry->get('displayName'); $emailAddresses = $entry->get('emailAddresses'); if (!empty($emailAddresses)) { $userContact->email = $emailAddresses[0]->address; } // only add to collection if we have usefull data if (!empty($userContact->displayName) || !empty($userContact->email)) { $contacts[] = $userContact; } } if ($data->exists('@odata.nextLink')) { $apiUrl = $data->get('@odata.nextLink'); $pagedList = true; } else { $pagedList = false; } } while ($pagedList); return $contacts; } } Provider/TwitchTV.php 0000644 00000003742 15077512767 0010612 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Provider; use Hybridauth\Adapter\OAuth2; use Hybridauth\Exception\UnexpectedApiResponseException; use Hybridauth\Data; use Hybridauth\User; /** * TwitchTV OAuth2 provider adapter. */ class TwitchTV extends OAuth2 { /** * {@inheritdoc} */ protected $scope = 'user:read:email'; /** * {@inheritdoc} */ protected $apiBaseUrl = 'https://api.twitch.tv/helix/'; /** * {@inheritdoc} */ protected $authorizeUrl = 'https://id.twitch.tv/oauth2/authorize'; /** * {@inheritdoc} */ protected $accessTokenUrl = 'https://id.twitch.tv/oauth2/token'; /** * {@inheritdoc} */ protected $apiDocumentation = 'https://dev.twitch.tv/docs/authentication/'; /** * {@inheritdoc} */ protected function initialize() { parent::initialize(); $this->apiRequestHeaders['Client-ID'] = $this->clientId; } /** * {@inheritdoc} */ public function getUserProfile() { $response = $this->apiRequest('users'); $data = new Data\Collection($response); if (!$data->exists('data')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } $users = $data->filter('data')->values(); $user = new Data\Collection($users[0]); $userProfile = new User\Profile(); $userProfile->identifier = $user->get('id'); $userProfile->displayName = $user->get('display_name'); $userProfile->photoURL = $user->get('profile_image_url'); $userProfile->email = $user->get('email'); $userProfile->description = strip_tags($user->get('description')); $userProfile->profileURL = "https://www.twitch.tv/{$userProfile->displayName}"; return $userProfile; } } Provider/WordPress.php 0000644 00000003445 15077512767 0011026 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Provider; use Hybridauth\Adapter\OAuth2; use Hybridauth\Exception\UnexpectedApiResponseException; use Hybridauth\Data; use Hybridauth\User; /** * WordPress OAuth2 provider adapter. */ class WordPress extends OAuth2 { /** * {@inheritdoc} */ protected $apiBaseUrl = 'https://public-api.wordpress.com/rest/v1/'; /** * {@inheritdoc} */ protected $authorizeUrl = 'https://public-api.wordpress.com/oauth2/authenticate'; /** * {@inheritdoc} */ protected $accessTokenUrl = 'https://public-api.wordpress.com/oauth2/token'; /** * {@inheritdoc} */ protected $apiDocumentation = 'https://developer.wordpress.com/docs/api/'; /** * {@inheritdoc} */ public function getUserProfile() { $response = $this->apiRequest('me/'); $data = new Data\Collection($response); if (!$data->exists('ID')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } $userProfile = new User\Profile(); $userProfile->identifier = $data->get('ID'); $userProfile->displayName = $data->get('display_name'); $userProfile->photoURL = $data->get('avatar_URL'); $userProfile->profileURL = $data->get('profile_URL'); $userProfile->email = $data->get('email'); $userProfile->language = $data->get('language'); $userProfile->displayName = $userProfile->displayName ?: $data->get('username'); $userProfile->emailVerified = $data->get('email_verified') ? $data->get('email') : ''; return $userProfile; } } Provider/Discord.php 0000644 00000004561 15077512767 0010465 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Provider; use Hybridauth\Adapter\OAuth2; use Hybridauth\Exception\UnexpectedApiResponseException; use Hybridauth\Data; use Hybridauth\User; /** * Discord OAuth2 provider adapter. */ class Discord extends OAuth2 { /** * {@inheritdoc} */ protected $scope = 'identify email'; /** * {@inheritdoc} */ protected $apiBaseUrl = 'https://discordapp.com/api/'; /** * {@inheritdoc} */ protected $authorizeUrl = 'https://discordapp.com/api/oauth2/authorize'; /** * {@inheritdoc} */ protected $accessTokenUrl = 'https://discordapp.com/api/oauth2/token'; /** * {@inheritdoc} */ protected $apiDocumentation = 'https://discordapp.com/developers/docs/topics/oauth2'; /** * {@inheritdoc} */ protected function initialize() { parent::initialize(); if ($this->isRefreshTokenAvailable()) { $this->tokenRefreshParameters += [ 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, ]; } } /** * {@inheritdoc} */ public function getUserProfile() { $response = $this->apiRequest('users/@me'); $data = new Data\Collection($response); if (!$data->exists('id')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } // Makes display name more unique. $displayName = $data->get('username') ?: $data->get('login'); if ($discriminator = $data->get('discriminator')) { $displayName .= "#{$discriminator}"; } $userProfile = new User\Profile(); $userProfile->identifier = $data->get('id'); $userProfile->displayName = $displayName; $userProfile->email = $data->get('email'); if ($data->get('verified')) { $userProfile->emailVerified = $data->get('email'); } if ($data->get('avatar')) { $userProfile->photoURL = 'https://cdn.discordapp.com/avatars/'; $userProfile->photoURL .= $data->get('id') . '/' . $data->get('avatar') . '.png'; } return $userProfile; } } Logger/Logger.php 0000644 00000006276 15103446660 0007733 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Logger; use Hybridauth\Exception\RuntimeException; use Hybridauth\Exception\InvalidArgumentException; /** * Debugging and Logging utility. */ class Logger implements LoggerInterface { const NONE = 'none'; // turn logging off const DEBUG = 'debug'; // debug, info and error messages const INFO = 'info'; // info and error messages const ERROR = 'error'; // only error messages /** * Debug level. * * One of Logger::NONE, Logger::DEBUG, Logger::INFO, Logger::ERROR * * @var string */ protected $level; /** * Path to file writeable by the web server. Required if $this->level !== Logger::NONE. * * @var string */ protected $file; /** * @param bool|string $level One of Logger::NONE, Logger::DEBUG, Logger::INFO, Logger::ERROR * @param string $file File where to write messages * * @throws InvalidArgumentException * @throws RuntimeException */ public function __construct($level, $file) { $this->level = self::NONE; if ($level && $level !== self::NONE) { $this->initialize($file); $this->level = $level === true ? Logger::DEBUG : $level; $this->file = $file; } } /** * @param string $file * * @throws InvalidArgumentException * @throws RuntimeException */ protected function initialize($file) { if (!$file) { throw new InvalidArgumentException('Log file is not specified.'); } if (!file_exists($file) && !touch($file)) { throw new RuntimeException(sprintf('Log file %s can not be created.', $file)); } if (!is_writable($file)) { throw new RuntimeException(sprintf('Log file %s is not writeable.', $file)); } } /** * @inheritdoc */ public function info($message, array $context = []) { if (!in_array($this->level, [self::DEBUG, self::INFO])) { return; } $this->log(self::INFO, $message, $context); } /** * @inheritdoc */ public function debug($message, array $context = []) { if (!in_array($this->level, [self::DEBUG])) { return; } $this->log(self::DEBUG, $message, $context); } /** * @inheritdoc */ public function error($message, array $context = []) { if (!in_array($this->level, [self::DEBUG, self::INFO, self::ERROR])) { return; } $this->log(self::ERROR, $message, $context); } /** * @inheritdoc */ public function log($level, $message, array $context = []) { $datetime = new \DateTime(); $datetime = $datetime->format(DATE_ATOM); $content = sprintf('%s -- %s -- %s -- %s', $level, $_SERVER['REMOTE_ADDR'], $datetime, $message); $content .= ($context ? "\n" . print_r($context, true) : ''); $content .= "\n"; file_put_contents($this->file, $content, FILE_APPEND); } } Logger/Psr3LoggerWrapper.php 0000644 00000001730 15103446660 0012032 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Logger; use Psr\Log\LoggerAwareTrait; /** * Wrapper for PSR3 logger. */ class Psr3LoggerWrapper implements LoggerInterface { use LoggerAwareTrait; /** * @inheritdoc */ public function info($message, array $context = []) { $this->logger->info($message, $context); } /** * @inheritdoc */ public function debug($message, array $context = []) { $this->logger->debug($message, $context); } /** * @inheritdoc */ public function error($message, array $context = []) { $this->logger->error($message, $context); } /** * @inheritdoc */ public function log($level, $message, array $context = []) { $this->logger->log($level, $message, $context); } } Logger/LoggerInterface.php 0000644 00000002221 15103446660 0011536 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Logger; /** * Logger interface, forward-compatible with PSR-3. */ interface LoggerInterface { /** * Interesting events. * * Example: User logs in, SQL logs. * * @param string $message * @param array $context */ public function info($message, array $context = array()); /** * Detailed debug information. * * @param string $message * @param array $context */ public function debug($message, array $context = array()); /** * Runtime errors that do not require immediate action but should typically * be logged and monitored. * * @param string $message * @param array $context */ public function error($message, array $context = array()); /** * Logs with an arbitrary level. * * @param mixed $level * @param string $message * @param array $context */ public function log($level, $message, array $context = array()); } index.html 0000644 00000000004 15103446660 0006540 0 ustar 00 403. Hybridauth.php 0000644 00000016271 15103446660 0007374 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth; use Hybridauth\Exception\InvalidArgumentException; use Hybridauth\Exception\UnexpectedValueException; use Hybridauth\Storage\StorageInterface; use Hybridauth\Logger\LoggerInterface; use Hybridauth\Logger\Logger; use Hybridauth\HttpClient\HttpClientInterface; /** * Hybridauth\Hybridauth * * For ease of use of multiple providers, Hybridauth implements the class Hybridauth\Hybridauth, * a sort of factory/façade which acts as an unified interface or entry point, and it expects a * configuration array containing the list of providers you want to use, their respective credentials * and authorized callback. */ class Hybridauth { /** * Hybridauth config. * * @var array */ protected $config; /** * Storage. * * @var StorageInterface */ protected $storage; /** * HttpClient. * * @var HttpClientInterface */ protected $httpClient; /** * Logger. * * @var LoggerInterface */ protected $logger; /** * @param array|string $config Array with configuration or Path to PHP file that will return array * @param HttpClientInterface $httpClient * @param StorageInterface $storage * @param LoggerInterface $logger * * @throws InvalidArgumentException */ public function __construct( $config, HttpClientInterface $httpClient = null, StorageInterface $storage = null, LoggerInterface $logger = null ) { if (is_string($config) && file_exists($config)) { $config = include $config; } elseif (!is_array($config)) { throw new InvalidArgumentException('Hybridauth config does not exist on the given path.'); } $this->config = $config + [ 'debug_mode' => Logger::NONE, 'debug_file' => '', 'curl_options' => null, 'providers' => [] ]; $this->storage = $storage; $this->logger = $logger; $this->httpClient = $httpClient; } /** * Instantiate the given provider and authentication or authorization protocol. * * If not authenticated yet, the user will be redirected to the provider's site for * authentication/authorisation, otherwise it will simply return an instance of * provider's adapter. * * @param string $name adapter's name (case insensitive) * * @return \Hybridauth\Adapter\AdapterInterface * @throws InvalidArgumentException * @throws UnexpectedValueException */ public function authenticate($name) { $adapter = $this->getAdapter($name); $adapter->authenticate(); return $adapter; } /** * Returns a new instance of a provider's adapter by name * * @param string $name adapter's name (case insensitive) * * @return \Hybridauth\Adapter\AdapterInterface * @throws InvalidArgumentException * @throws UnexpectedValueException */ public function getAdapter($name) { $config = $this->getProviderConfig($name); $adapter = isset($config['adapter']) ? $config['adapter'] : sprintf('Hybridauth\\Provider\\%s', $name); if (!class_exists($adapter)) { $adapter = null; $fs = new \FilesystemIterator(__DIR__ . '/Provider/'); /** @var \SplFileInfo $file */ foreach ($fs as $file) { if (!$file->isDir()) { $provider = strtok($file->getFilename(), '.'); if (strtolower($name) === mb_strtolower($provider)) { $adapter = sprintf('Hybridauth\\Provider\\%s', $provider); break; } } } if ($adapter === null) { throw new InvalidArgumentException('Unknown Provider.'); } } return new $adapter($config, $this->httpClient, $this->storage, $this->logger); } /** * Get provider config by name. * * @param string $name adapter's name (case insensitive) * * @throws UnexpectedValueException * @throws InvalidArgumentException * * @return array */ public function getProviderConfig($name) { $name = strtolower($name); $providersConfig = array_change_key_case($this->config['providers'], CASE_LOWER); if (!isset($providersConfig[$name])) { throw new InvalidArgumentException('Unknown Provider.'); } if (!$providersConfig[$name]['enabled']) { throw new UnexpectedValueException('Disabled Provider.'); } $config = $providersConfig[$name]; $config += [ 'debug_mode' => $this->config['debug_mode'], 'debug_file' => $this->config['debug_file'], ]; if (!isset($config['callback']) && isset($this->config['callback'])) { $config['callback'] = $this->config['callback']; } return $config; } /** * Returns a boolean of whether the user is connected with a provider * * @param string $name adapter's name (case insensitive) * * @return bool * @throws InvalidArgumentException * @throws UnexpectedValueException */ public function isConnectedWith($name) { return $this->getAdapter($name)->isConnected(); } /** * Returns a list of enabled adapters names * * @return array */ public function getProviders() { $providers = []; foreach ($this->config['providers'] as $name => $config) { if ($config['enabled']) { $providers[] = $name; } } return $providers; } /** * Returns a list of currently connected adapters names * * @return array * @throws InvalidArgumentException * @throws UnexpectedValueException */ public function getConnectedProviders() { $providers = []; foreach ($this->getProviders() as $name) { if ($this->isConnectedWith($name)) { $providers[] = $name; } } return $providers; } /** * Returns a list of new instances of currently connected adapters * * @return \Hybridauth\Adapter\AdapterInterface[] * @throws InvalidArgumentException * @throws UnexpectedValueException */ public function getConnectedAdapters() { $adapters = []; foreach ($this->getProviders() as $name) { $adapter = $this->getAdapter($name); if ($adapter->isConnected()) { $adapters[$name] = $adapter; } } return $adapters; } /** * Disconnect all currently connected adapters at once */ public function disconnectAllAdapters() { foreach ($this->getProviders() as $name) { $adapter = $this->getAdapter($name); if ($adapter->isConnected()) { $adapter->disconnect(); } } } } Exception/InvalidAccessTokenException.php 0000644 00000000501 15103446660 0014604 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Exception; /** * */ class InvalidAccessTokenException extends InvalidArgumentException implements ExceptionInterface { } Exception/RuntimeException.php 0000644 00000000606 15103446660 0012524 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Exception; /** * RuntimeException * * Exception thrown if an error which can only be found on runtime occurs. */ class RuntimeException extends Exception implements ExceptionInterface { } Exception/InvalidOpenidIdentifierException.php 0000644 00000000506 15103446660 0015630 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Exception; /** * */ class InvalidOpenidIdentifierException extends InvalidArgumentException implements ExceptionInterface { } Exception/AuthorizationDeniedException.php 0000644 00000000502 15103446660 0015045 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Exception; /** * */ class AuthorizationDeniedException extends UnexpectedValueException implements ExceptionInterface { } Exception/InvalidAuthorizationCodeException.php 0000644 00000000507 15103446660 0016043 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Exception; /** * */ class InvalidAuthorizationCodeException extends InvalidArgumentException implements ExceptionInterface { } Exception/InvalidApplicationCredentialsException.php 0000644 00000000514 15103446660 0017027 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Exception; /** * */ class InvalidApplicationCredentialsException extends InvalidArgumentException implements ExceptionInterface { } Exception/InvalidOauthTokenException.php 0000644 00000000500 15103446660 0014462 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Exception; /** * */ class InvalidOauthTokenException extends InvalidArgumentException implements ExceptionInterface { } Exception/HttpRequestFailedException.php 0000644 00000000500 15103446660 0014467 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Exception; /** * */ class HttpRequestFailedException extends UnexpectedValueException implements ExceptionInterface { } Exception/NotImplementedException.php 0000644 00000000473 15103446660 0014027 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Exception; /** * */ class NotImplementedException extends BadMethodCallException implements ExceptionInterface { } Exception/UnexpectedValueException.php 0000644 00000001113 15103446660 0014174 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Exception; /** * UnexpectedValueException * * Exception thrown if a value does not match with a set of values. Typically this happens when a function calls * another function and expects the return value to be of a certain type or value not including arithmetic or * buffer related errors. */ class UnexpectedValueException extends RuntimeException implements ExceptionInterface { } Exception/InvalidAuthorizationStateException.php 0000644 00000000510 15103446660 0016243 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Exception; /** * */ class InvalidAuthorizationStateException extends InvalidArgumentException implements ExceptionInterface { } Exception/Exception.php 0000644 00000004075 15103446660 0011164 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Exception; /** * Hybridauth Base Exception */ class Exception extends \Exception implements ExceptionInterface { /** * Shamelessly Borrowed from Slimframework * * @param $object */ public function debug($object) { $title = 'Hybridauth Exception'; $code = $this->getCode(); $message = $this->getMessage(); $file = $this->getFile(); $line = $this->getLine(); $trace = $this->getTraceAsString(); $html = sprintf('<h1>%s</h1>', $title); $html .= '<p>Hybridauth has encountered the following error:</p>'; $html .= '<h2>Details</h2>'; $html .= sprintf('<div><strong>Exception:</strong> %s</div>', get_class($this)); $html .= sprintf('<div><strong>Message:</strong> <font color="#cc0000">%s</font></div>', $message); $html .= sprintf('<div><strong>File:</strong> %s</div>', $file); $html .= sprintf('<div><strong>Line:</strong> %s</div>', $line); $html .= sprintf('<div><strong>Code:</strong> %s</div>', $code); $html .= '<h2>Trace</h2>'; $html .= sprintf('<pre>%s</pre>', $trace); if ($object) { $html .= '<h2>Debug</h2>'; $obj_dump = print_r($object, true); // phpcs:ignore $html .= sprintf('<b>' . get_class($object) . '</b> extends <b>' . get_parent_class($object) . '</b><pre>%s</pre>', $obj_dump); } $html .= '<h2>Session</h2>'; $session_dump = print_r($_SESSION, true); $html .= sprintf('<pre>%s</pre>', $session_dump); // phpcs:ignore echo sprintf("<html><head><title>%s</title><style>body{margin:0;padding:30px;font:12px/1.5 Helvetica,Arial,Verdana,sans-serif;}h1{margin:0;font-size:48px;font-weight:normal;line-height:48px;}strong{display:inline-block;width:75px;}</style></head><body>%s</body></html>", $title, $html); } } Exception/InvalidArgumentException.php 0000644 00000000622 15103446660 0014170 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Exception; /** * InvalidArgumentException * * Exception thrown if an argument is not of the expected type. */ class InvalidArgumentException extends RuntimeException implements ExceptionInterface { } Exception/UnexpectedApiResponseException.php 0000644 00000000504 15103446660 0015353 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Exception; /** * */ class UnexpectedApiResponseException extends UnexpectedValueException implements ExceptionInterface { } Exception/HttpClientFailureException.php 0000644 00000000500 15103446660 0014460 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Exception; /** * */ class HttpClientFailureException extends UnexpectedValueException implements ExceptionInterface { } Exception/ExceptionInterface.php 0000644 00000003351 15103446660 0013001 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Exception; /** * Hybridauth Exceptions Interface */ interface ExceptionInterface { /* ExceptionInterface Exception extends \Exception implements ExceptionInterface | RuntimeException extends Exception | | UnexpectedValueException extends RuntimeException | | | AuthorizationDeniedException extends UnexpectedValueException | | | HttpClientFailureException extends UnexpectedValueException | | | HttpRequestFailedException extends UnexpectedValueException | | | InvalidAuthorizationCodeException extends UnexpectedValueException | | | InvalidAuthorizationStateException extends UnexpectedValueException | | | InvalidOauthTokenException extends UnexpectedValueException | | | InvalidAccessTokenException extends UnexpectedValueException | | | UnexpectedApiResponseException extends UnexpectedValueException | | | | BadMethodCallException extends RuntimeException | | | NotImplementedException extends BadMethodCallException | | | | InvalidArgumentException extends RuntimeException | | | InvalidApplicationCredentialsException extends InvalidArgumentException | | | InvalidOpenidIdentifierException extends InvalidArgumentException */ } Exception/BadMethodCallException.php 0000644 00000000660 15103446660 0013524 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Exception; /** * BadMethodCallException * * Exception thrown if a callback refers to an undefined method or if some arguments are missing. */ class BadMethodCallException extends RuntimeException implements ExceptionInterface { } version.txt 0000644 00000000006 15103446660 0006773 0 ustar 00 3.11.0 Adapter/AbstractAdapter.php 0000644 00000021031 15103446660 0011703 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Adapter; use Hybridauth\Exception\NotImplementedException; use Hybridauth\Exception\InvalidArgumentException; use Hybridauth\Exception\HttpClientFailureException; use Hybridauth\Exception\HttpRequestFailedException; use Hybridauth\Storage\StorageInterface; use Hybridauth\Storage\Session; use Hybridauth\Logger\LoggerInterface; use Hybridauth\Logger\Logger; use Hybridauth\HttpClient\HttpClientInterface; use Hybridauth\HttpClient\Curl as HttpClient; use Hybridauth\Data; /** * Class AbstractAdapter */ abstract class AbstractAdapter implements AdapterInterface { use DataStoreTrait; /** * Provider ID (unique name). * * @var string */ protected $providerId = ''; /** * Specific Provider config. * * @var mixed */ protected $config = []; /** * Extra Provider parameters. * * @var array */ protected $params; /** * Callback url * * @var string */ protected $callback = ''; /** * Storage. * * @var StorageInterface */ public $storage; /** * HttpClient. * * @var HttpClientInterface */ public $httpClient; /** * Logger. * * @var LoggerInterface */ public $logger; /** * Whether to validate API status codes of http responses * * @var bool */ protected $validateApiResponseHttpCode = true; /** * Common adapters constructor. * * @param array $config * @param HttpClientInterface $httpClient * @param StorageInterface $storage * @param LoggerInterface $logger */ public function __construct( $config = [], HttpClientInterface $httpClient = null, StorageInterface $storage = null, LoggerInterface $logger = null ) { $this->providerId = (new \ReflectionClass($this))->getShortName(); $this->config = new Data\Collection($config); $this->setHttpClient($httpClient); $this->setStorage($storage); $this->setLogger($logger); $this->configure(); $this->logger->debug(sprintf('Initialize %s, config: ', get_class($this)), $config); $this->initialize(); } /** * Load adapter's configuration */ abstract protected function configure(); /** * Adapter initializer */ abstract protected function initialize(); /** * {@inheritdoc} */ abstract public function isConnected(); /** * {@inheritdoc} */ public function apiRequest($url, $method = 'GET', $parameters = [], $headers = [], $multipart = false) { throw new NotImplementedException('Provider does not support this feature.'); } /** * {@inheritdoc} */ public function maintainToken() { // Nothing needed for most providers } /** * {@inheritdoc} */ public function getUserProfile() { throw new NotImplementedException('Provider does not support this feature.'); } /** * {@inheritdoc} */ public function getUserContacts() { throw new NotImplementedException('Provider does not support this feature.'); } /** * {@inheritdoc} */ public function getUserPages() { throw new NotImplementedException('Provider does not support this feature.'); } /** * {@inheritdoc} */ public function getUserActivity($stream) { throw new NotImplementedException('Provider does not support this feature.'); } /** * {@inheritdoc} */ public function setUserStatus($status) { throw new NotImplementedException('Provider does not support this feature.'); } /** * {@inheritdoc} */ public function setPageStatus($status, $pageId) { throw new NotImplementedException('Provider does not support this feature.'); } /** * {@inheritdoc} */ public function disconnect() { $this->clearStoredData(); } /** * {@inheritdoc} */ public function getAccessToken() { $tokenNames = [ 'access_token', 'access_token_secret', 'token_type', 'refresh_token', 'expires_in', 'expires_at', ]; $tokens = []; foreach ($tokenNames as $name) { if ($this->getStoredData($name)) { $tokens[$name] = $this->getStoredData($name); } } return $tokens; } /** * {@inheritdoc} */ public function setAccessToken($tokens = []) { $this->clearStoredData(); foreach ($tokens as $token => $value) { $this->storeData($token, $value); } // Re-initialize token parameters. $this->initialize(); } /** * {@inheritdoc} */ public function setHttpClient(HttpClientInterface $httpClient = null) { $this->httpClient = $httpClient ?: new HttpClient(); if ($this->config->exists('curl_options') && method_exists($this->httpClient, 'setCurlOptions')) { $this->httpClient->setCurlOptions($this->config->get('curl_options')); } } /** * {@inheritdoc} */ public function getHttpClient() { return $this->httpClient; } /** * {@inheritdoc} */ public function setStorage(StorageInterface $storage = null) { $this->storage = $storage ?: new Session(); } /** * {@inheritdoc} */ public function getStorage() { return $this->storage; } /** * {@inheritdoc} */ public function setLogger(LoggerInterface $logger = null) { $this->logger = $logger ?: new Logger( $this->config->get('debug_mode'), $this->config->get('debug_file') ); if (method_exists($this->httpClient, 'setLogger')) { $this->httpClient->setLogger($this->logger); } } /** * {@inheritdoc} */ public function getLogger() { return $this->logger; } /** * Set Adapter's API callback url * * @param string $callback * * @throws InvalidArgumentException */ protected function setCallback($callback) { if (!filter_var($callback, FILTER_VALIDATE_URL)) { throw new InvalidArgumentException('A valid callback url is required.'); } $this->callback = $callback; } /** * Overwrite Adapter's API endpoints * * @param array|Data\Collection $endpoints */ protected function setApiEndpoints($endpoints = null) { if (empty($endpoints)) { return; } $collection = is_array($endpoints) ? new Data\Collection($endpoints) : $endpoints; $this->apiBaseUrl = $collection->get('api_base_url') ?: $this->apiBaseUrl; $this->authorizeUrl = $collection->get('authorize_url') ?: $this->authorizeUrl; $this->accessTokenUrl = $collection->get('access_token_url') ?: $this->accessTokenUrl; } /** * Validate signed API responses Http status code. * * Since the specifics of error responses is beyond the scope of RFC6749 and OAuth Core specifications, * Hybridauth will consider any HTTP status code that is different than '200 OK' as an ERROR. * * @param string $error String to pre append to message thrown in exception * * @throws HttpClientFailureException * @throws HttpRequestFailedException */ protected function validateApiResponse($error = '') { $error .= !empty($error) ? '. ' : ''; if ($this->httpClient->getResponseClientError()) { throw new HttpClientFailureException( $error . 'HTTP client error: ' . $this->httpClient->getResponseClientError() . '.' ); } // if validateApiResponseHttpCode is set to false, we by pass verification of http status code if (!$this->validateApiResponseHttpCode) { return; } $status = $this->httpClient->getResponseHttpCode(); if ($status < 200 || $status > 299) { throw new HttpRequestFailedException( $error . 'HTTP error ' . $this->httpClient->getResponseHttpCode() . '. Raw Provider API response: ' . $this->httpClient->getResponseBody() . '.' ); } } } Adapter/OAuth2.php 0000644 00000054653 15103446660 0007761 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Adapter; use Hybridauth\Exception\Exception; use Hybridauth\Exception\InvalidApplicationCredentialsException; use Hybridauth\Exception\InvalidAuthorizationStateException; use Hybridauth\Exception\InvalidAuthorizationCodeException; use Hybridauth\Exception\AuthorizationDeniedException; use Hybridauth\Exception\InvalidAccessTokenException; use Hybridauth\Data; use Hybridauth\HttpClient; /** * This class can be used to simplify the authorization flow of OAuth 2 based service providers. * * Subclasses (i.e., providers adapters) can either use the already provided methods or override * them when necessary. */ abstract class OAuth2 extends AbstractAdapter implements AdapterInterface { /** * Client Identifier * * RFC6749: client_id REQUIRED. The client identifier issued to the client during * the registration process described by Section 2.2. * * http://tools.ietf.org/html/rfc6749#section-2.2 * * @var string */ protected $clientId = ''; /** * Client Secret * * RFC6749: client_secret REQUIRED. The client secret. The client MAY omit the * parameter if the client secret is an empty string. * * http://tools.ietf.org/html/rfc6749#section-2.2 * * @var string */ protected $clientSecret = ''; /** * Access Token Scope * * RFC6749: The authorization and token endpoints allow the client to specify the * scope of the access request using the "scope" request parameter. * * http://tools.ietf.org/html/rfc6749#section-3.3 * * @var string */ protected $scope = ''; /** * Base URL to provider API * * This var will be used to build urls when sending signed requests * * @var string */ protected $apiBaseUrl = ''; /** * Authorization Endpoint * * RFC6749: The authorization endpoint is used to interact with the resource * owner and obtain an authorization grant. * * http://tools.ietf.org/html/rfc6749#section-3.1 * * @var string */ protected $authorizeUrl = ''; /** * Access Token Endpoint * * RFC6749: The token endpoint is used by the client to obtain an access token by * presenting its authorization grant or refresh token. * * http://tools.ietf.org/html/rfc6749#section-3.2 * * @var string */ protected $accessTokenUrl = ''; /** * TokenInfo endpoint * * Access token validation. OPTIONAL. * * @var string */ protected $accessTokenInfoUrl = ''; /** * IPD API Documentation * * OPTIONAL. * * @var string */ protected $apiDocumentation = ''; /** * Redirection Endpoint or Callback * * RFC6749: After completing its interaction with the resource owner, the * authorization server directs the resource owner's user-agent back to * the client. * * http://tools.ietf.org/html/rfc6749#section-3.1.2 * * @var string */ protected $callback = ''; /** * Authorization Url Parameters * * @var array */ protected $AuthorizeUrlParameters = []; /** * Authorization Url Parameter encoding type * @see https://www.php.net/manual/de/function.http-build-query.php * * @var string */ protected $AuthorizeUrlParametersEncType = PHP_QUERY_RFC1738; /** * Authorization Request State * * @var bool */ protected $supportRequestState = true; /** * Access Token name * * While most providers will use 'access_token' as name for the Access Token attribute, other do not. * On the latter case, this should be set by sub classes. * * @var string */ protected $accessTokenName = 'access_token'; /** * Authorization Request HTTP method. * * @see exchangeCodeForAccessToken() * * @var string */ protected $tokenExchangeMethod = 'POST'; /** * Authorization Request URL parameters. * * Sub classes may change add any additional parameter when necessary. * * @see exchangeCodeForAccessToken() * * @var array */ protected $tokenExchangeParameters = []; /** * Authorization Request HTTP headers. * * Sub classes may add any additional header when necessary. * * @see exchangeCodeForAccessToken() * * @var array */ protected $tokenExchangeHeaders = []; /** * Refresh Token Request HTTP method. * * @see refreshAccessToken() * * @var string */ protected $tokenRefreshMethod = 'POST'; /** * Refresh Token Request URL parameters. * * Sub classes may change add any additional parameter when necessary. * * @see refreshAccessToken() * * @var array|null */ protected $tokenRefreshParameters = null; /** * Refresh Token Request HTTP headers. * * Sub classes may add any additional header when necessary. * * @see refreshAccessToken() * * @var array */ protected $tokenRefreshHeaders = []; /** * Authorization Request URL parameters. * * Sub classes may change add any additional parameter when necessary. * * @see apiRequest() * * @var array */ protected $apiRequestParameters = []; /** * Authorization Request HTTP headers. * * Sub classes may add any additional header when necessary. * * @see apiRequest() * * @var array */ protected $apiRequestHeaders = []; /** * {@inheritdoc} */ protected function configure() { $this->clientId = $this->config->filter('keys')->get('id') ?: $this->config->filter('keys')->get('key'); $this->clientSecret = $this->config->filter('keys')->get('secret'); if (!$this->clientId || !$this->clientSecret) { throw new InvalidApplicationCredentialsException( 'Your application id is required in order to connect to ' . $this->providerId ); } $this->scope = $this->config->exists('scope') ? $this->config->get('scope') : $this->scope; if ($this->config->exists('tokens')) { $this->setAccessToken($this->config->get('tokens')); } if ($this->config->exists('supportRequestState')) { $this->supportRequestState = $this->config->get('supportRequestState'); } $this->setCallback($this->config->get('callback')); $this->setApiEndpoints($this->config->get('endpoints')); } /** * {@inheritdoc} */ protected function initialize() { $this->AuthorizeUrlParameters = [ 'response_type' => 'code', 'client_id' => $this->clientId, 'redirect_uri' => $this->callback, 'scope' => $this->scope, ]; $this->tokenExchangeParameters = [ 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'grant_type' => 'authorization_code', 'redirect_uri' => $this->callback ]; $refreshToken = $this->getStoredData('refresh_token'); if (!empty($refreshToken)) { $this->tokenRefreshParameters = [ 'grant_type' => 'refresh_token', 'refresh_token' => $refreshToken, ]; } $this->apiRequestHeaders = [ 'Authorization' => 'Bearer ' . $this->getStoredData('access_token') ]; } /** * {@inheritdoc} */ public function authenticate() { $this->logger->info(sprintf('%s::authenticate()', get_class($this))); if ($this->isConnected()) { return true; } try { $this->authenticateCheckError(); $code = filter_input($_SERVER['REQUEST_METHOD'] === 'POST' ? INPUT_POST : INPUT_GET, 'code'); if (empty($code)) { $this->authenticateBegin(); } else { $this->authenticateFinish(); } } catch (Exception $e) { $this->clearStoredData(); throw $e; } return null; } /** * {@inheritdoc} */ public function isConnected() { if ((bool)$this->getStoredData('access_token')) { return (!$this->hasAccessTokenExpired() || $this->isRefreshTokenAvailable()); } return false; } /** * If we can use a refresh token, then an expired token does not stop us being connected. * * @return bool */ public function isRefreshTokenAvailable() { return is_array($this->tokenRefreshParameters); } /** * Authorization Request Error Response * * RFC6749: If the request fails due to a missing, invalid, or mismatching * redirection URI, or if the client identifier is missing or invalid, * the authorization server SHOULD inform the resource owner of the error. * * http://tools.ietf.org/html/rfc6749#section-4.1.2.1 * * @throws \Hybridauth\Exception\InvalidAuthorizationCodeException * @throws \Hybridauth\Exception\AuthorizationDeniedException */ protected function authenticateCheckError() { $error = filter_input(INPUT_GET, 'error', FILTER_SANITIZE_SPECIAL_CHARS); if (!empty($error)) { $error_description = filter_input(INPUT_GET, 'error_description', FILTER_SANITIZE_SPECIAL_CHARS); $error_uri = filter_input(INPUT_GET, 'error_uri', FILTER_SANITIZE_SPECIAL_CHARS); $collated_error = sprintf('Provider returned an error: %s %s %s', $error, $error_description, $error_uri); if ($error == 'access_denied') { throw new AuthorizationDeniedException($collated_error); } throw new InvalidAuthorizationCodeException($collated_error); } } /** * Initiate the authorization protocol * * Build Authorization URL for Authorization Request and redirect the user-agent to the * Authorization Server. */ protected function authenticateBegin() { $authUrl = $this->getAuthorizeUrl(); $this->logger->debug(sprintf('%s::authenticateBegin(), redirecting user to:', get_class($this)), [$authUrl]); HttpClient\Util::redirect($authUrl); } /** * Finalize the authorization process * * @throws \Hybridauth\Exception\HttpClientFailureException * @throws \Hybridauth\Exception\HttpRequestFailedException * @throws InvalidAccessTokenException * @throws InvalidAuthorizationStateException */ protected function authenticateFinish() { $this->logger->debug( sprintf('%s::authenticateFinish(), callback url:', get_class($this)), [HttpClient\Util::getCurrentUrl(true)] ); $state = filter_input($_SERVER['REQUEST_METHOD'] === 'POST' ? INPUT_POST : INPUT_GET, 'state'); $code = filter_input($_SERVER['REQUEST_METHOD'] === 'POST' ? INPUT_POST : INPUT_GET, 'code'); /** * Authorization Request State * * RFC6749: state : RECOMMENDED. An opaque value used by the client to maintain * state between the request and callback. The authorization server includes * this value when redirecting the user-agent back to the client. * * http://tools.ietf.org/html/rfc6749#section-4.1.1 */ if ($this->supportRequestState && (!$state || $this->getStoredData('authorization_state') != $state) ) { $this->deleteStoredData('authorization_state'); throw new InvalidAuthorizationStateException( 'The authorization state [state=' . substr(htmlentities($state), 0, 100) . '] ' . 'of this page is either invalid or has already been consumed.' ); } /** * Authorization Request Code * * RFC6749: If the resource owner grants the access request, the authorization * server issues an authorization code and delivers it to the client: * * http://tools.ietf.org/html/rfc6749#section-4.1.2 */ $response = $this->exchangeCodeForAccessToken($code); $this->validateAccessTokenExchange($response); $this->initialize(); } /** * Build Authorization URL for Authorization Request * * RFC6749: The client constructs the request URI by adding the following * $parameters to the query component of the authorization endpoint URI: * * - response_type REQUIRED. Value MUST be set to "code". * - client_id REQUIRED. * - redirect_uri OPTIONAL. * - scope OPTIONAL. * - state RECOMMENDED. * * http://tools.ietf.org/html/rfc6749#section-4.1.1 * * Sub classes may redefine this method when necessary. * * @param array $parameters * * @return string Authorization URL */ protected function getAuthorizeUrl($parameters = []) { $this->AuthorizeUrlParameters = !empty($parameters) ? $parameters : array_replace( (array)$this->AuthorizeUrlParameters, (array)$this->config->get('authorize_url_parameters') ); if ($this->supportRequestState) { if (!isset($this->AuthorizeUrlParameters['state'])) { $this->AuthorizeUrlParameters['state'] = 'HA-' . str_shuffle('ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'); } $this->storeData('authorization_state', $this->AuthorizeUrlParameters['state']); } $queryParams = http_build_query($this->AuthorizeUrlParameters, '', '&', $this->AuthorizeUrlParametersEncType); return $this->authorizeUrl . '?' . $queryParams; } /** * Access Token Request * * This method will exchange the received $code in loginFinish() with an Access Token. * * RFC6749: The client makes a request to the token endpoint by sending the * following parameters using the "application/x-www-form-urlencoded" * with a character encoding of UTF-8 in the HTTP request entity-body: * * - grant_type REQUIRED. Value MUST be set to "authorization_code". * - code REQUIRED. The authorization code received from the authorization server. * - redirect_uri REQUIRED. * - client_id REQUIRED. * * http://tools.ietf.org/html/rfc6749#section-4.1.3 * * @param string $code * * @return string Raw Provider API response * @throws \Hybridauth\Exception\HttpClientFailureException * @throws \Hybridauth\Exception\HttpRequestFailedException */ protected function exchangeCodeForAccessToken($code) { $this->tokenExchangeParameters['code'] = $code; $response = $this->httpClient->request( $this->accessTokenUrl, $this->tokenExchangeMethod, $this->tokenExchangeParameters, $this->tokenExchangeHeaders ); $this->validateApiResponse('Unable to exchange code for API access token'); return $response; } /** * Validate Access Token Response * * RFC6749: If the access token request is valid and authorized, the * authorization server issues an access token and optional refresh token. * If the request client authentication failed or is invalid, the authorization * server returns an error response as described in Section 5.2. * * Example of a successful response: * * HTTP/1.1 200 OK * Content-Type: application/json;charset=UTF-8 * Cache-Control: no-store * Pragma: no-cache * * { * "access_token":"2YotnFZFEjr1zCsicMWpAA", * "token_type":"example", * "expires_in":3600, * "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA", * "example_parameter":"example_value" * } * * http://tools.ietf.org/html/rfc6749#section-4.1.4 * * This method uses Data_Parser to attempt to decodes the raw $response (usually JSON) * into a data collection. * * @param string $response * * @return \Hybridauth\Data\Collection * @throws InvalidAccessTokenException */ protected function validateAccessTokenExchange($response) { $data = (new Data\Parser())->parse($response); $collection = new Data\Collection($data); if (!$collection->exists('access_token')) { throw new InvalidAccessTokenException( 'Provider returned no access_token: ' . htmlentities($response) ); } $this->storeData('access_token', $collection->get('access_token')); $this->storeData('token_type', $collection->get('token_type')); if ($collection->get('refresh_token')) { $this->storeData('refresh_token', $collection->get('refresh_token')); } // calculate when the access token expire if ($collection->exists('expires_in')) { $expires_at = time() + (int)$collection->get('expires_in'); $this->storeData('expires_in', $collection->get('expires_in')); $this->storeData('expires_at', $expires_at); } $this->deleteStoredData('authorization_state'); $this->initialize(); return $collection; } /** * Refreshing an Access Token * * RFC6749: If the authorization server issued a refresh token to the * client, the client makes a refresh request to the token endpoint by * adding the following parameters ... in the HTTP request entity-body: * * - grant_type REQUIRED. Value MUST be set to "refresh_token". * - refresh_token REQUIRED. The refresh token issued to the client. * - scope OPTIONAL. * * http://tools.ietf.org/html/rfc6749#section-6 * * This method is similar to exchangeCodeForAccessToken(). The only * difference is here we exchange refresh_token for a new access_token. * * @param array $parameters * * @return string|null Raw Provider API response, or null if we cannot refresh * @throws \Hybridauth\Exception\HttpClientFailureException * @throws \Hybridauth\Exception\HttpRequestFailedException * @throws InvalidAccessTokenException */ public function refreshAccessToken($parameters = []) { $this->tokenRefreshParameters = !empty($parameters) ? $parameters : $this->tokenRefreshParameters; if (!$this->isRefreshTokenAvailable()) { return null; } $response = $this->httpClient->request( $this->accessTokenUrl, $this->tokenRefreshMethod, $this->tokenRefreshParameters, $this->tokenRefreshHeaders ); $this->validateApiResponse('Unable to refresh the access token'); $this->validateRefreshAccessToken($response); return $response; } /** * Check whether access token has expired * * @param int|null $time * @return bool|null */ public function hasAccessTokenExpired($time = null) { if ($time === null) { $time = time(); } $expires_at = $this->getStoredData('expires_at'); if (!$expires_at) { return null; } return $expires_at <= $time; } /** * Validate Refresh Access Token Request * * RFC6749: If valid and authorized, the authorization server issues an * access token as described in Section 5.1. If the request failed * verification or is invalid, the authorization server returns an error * response as described in Section 5.2. * * http://tools.ietf.org/html/rfc6749#section-6 * http://tools.ietf.org/html/rfc6749#section-5.1 * http://tools.ietf.org/html/rfc6749#section-5.2 * * This method simply use validateAccessTokenExchange(), however sub * classes may redefine it when necessary. * * @param $response * * @return \Hybridauth\Data\Collection * @throws InvalidAccessTokenException */ protected function validateRefreshAccessToken($response) { return $this->validateAccessTokenExchange($response); } /** * Send a signed request to provider API * * RFC6749: Accessing Protected Resources: The client accesses protected * resources by presenting the access token to the resource server. The * resource server MUST validate the access token and ensure that it has * not expired and that its scope covers the requested resource. * * Note: Since the specifics of error responses is beyond the scope of * RFC6749 and OAuth specifications, Hybridauth will consider any HTTP * status code that is different than '200 OK' as an ERROR. * * http://tools.ietf.org/html/rfc6749#section-7 * * @param string $url * @param string $method * @param array $parameters * @param array $headers * @param bool $multipart * * @return mixed * @throws \Hybridauth\Exception\HttpClientFailureException * @throws \Hybridauth\Exception\HttpRequestFailedException * @throws InvalidAccessTokenException */ public function apiRequest($url, $method = 'GET', $parameters = [], $headers = [], $multipart = false) { // refresh tokens if needed $this->maintainToken(); if ($this->hasAccessTokenExpired() === true) { $this->refreshAccessToken(); } if (strrpos($url, 'http://') !== 0 && strrpos($url, 'https://') !== 0) { $url = rtrim($this->apiBaseUrl, '/') . '/' . ltrim($url, '/'); } $parameters = array_replace($this->apiRequestParameters, (array)$parameters); $headers = array_replace($this->apiRequestHeaders, (array)$headers); $response = $this->httpClient->request( $url, $method, // HTTP Request Method. Defaults to GET. $parameters, // Request Parameters $headers, // Request Headers $multipart // Is request multipart ); $this->validateApiResponse('Signed API request to ' . $url . ' has returned an error'); $response = (new Data\Parser())->parse($response); return $response; } } Adapter/OpenID.php 0000644 00000017362 15103446660 0007771 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Adapter; use Hybridauth\Exception\InvalidOpenidIdentifierException; use Hybridauth\Exception\AuthorizationDeniedException; use Hybridauth\Exception\UnexpectedApiResponseException; use Hybridauth\Data; use Hybridauth\HttpClient; use Hybridauth\User; use Hybridauth\Thirdparty\OpenID\LightOpenID; /** * This class can be used to simplify the authentication flow of OpenID based service providers. * * Subclasses (i.e., providers adapters) can either use the already provided methods or override * them when necessary. */ abstract class OpenID extends AbstractAdapter implements AdapterInterface { /** * LightOpenID instance * * @var object */ protected $openIdClient = null; /** * Openid provider identifier * * @var string */ protected $openidIdentifier = ''; /** * IPD API Documentation * * OPTIONAL. * * @var string */ protected $apiDocumentation = ''; /** * {@inheritdoc} */ protected function configure() { if ($this->config->exists('openid_identifier')) { $this->openidIdentifier = $this->config->get('openid_identifier'); } if (empty($this->openidIdentifier)) { throw new InvalidOpenidIdentifierException('OpenID adapter requires an openid_identifier.', 4); } $this->setCallback($this->config->get('callback')); $this->setApiEndpoints($this->config->get('endpoints')); } /** * {@inheritdoc} */ protected function initialize() { $hostPort = parse_url($this->callback, PHP_URL_PORT); $hostUrl = parse_url($this->callback, PHP_URL_HOST); if ($hostPort) { $hostUrl .= ':' . $hostPort; } // @fixme: add proxy $this->openIdClient = new LightOpenID($hostUrl, null); } /** * {@inheritdoc} */ public function authenticate() { $this->logger->info(sprintf('%s::authenticate()', get_class($this))); if ($this->isConnected()) { return true; } if (empty($_REQUEST['openid_mode'])) { $this->authenticateBegin(); } else { return $this->authenticateFinish(); } return null; } /** * {@inheritdoc} */ public function isConnected() { return (bool)$this->storage->get($this->providerId . '.user'); } /** * {@inheritdoc} */ public function disconnect() { $this->storage->delete($this->providerId . '.user'); return true; } /** * Initiate the authorization protocol * * Include and instantiate LightOpenID */ protected function authenticateBegin() { $this->openIdClient->identity = $this->openidIdentifier; $this->openIdClient->returnUrl = $this->callback; $this->openIdClient->required = [ 'namePerson/first', 'namePerson/last', 'namePerson/friendly', 'namePerson', 'contact/email', 'birthDate', 'birthDate/birthDay', 'birthDate/birthMonth', 'birthDate/birthYear', 'person/gender', 'pref/language', 'contact/postalCode/home', 'contact/city/home', 'contact/country/home', 'media/image/default', ]; $authUrl = $this->openIdClient->authUrl(); $this->logger->debug(sprintf('%s::authenticateBegin(), redirecting user to:', get_class($this)), [$authUrl]); HttpClient\Util::redirect($authUrl); } /** * Finalize the authorization process. * * @throws AuthorizationDeniedException * @throws UnexpectedApiResponseException */ protected function authenticateFinish() { $this->logger->debug( sprintf('%s::authenticateFinish(), callback url:', get_class($this)), [HttpClient\Util::getCurrentUrl(true)] ); if ($this->openIdClient->mode == 'cancel') { throw new AuthorizationDeniedException('User has cancelled the authentication.'); } if (!$this->openIdClient->validate()) { throw new UnexpectedApiResponseException('Invalid response received.'); } $openidAttributes = $this->openIdClient->getAttributes(); if (!$this->openIdClient->identity) { throw new UnexpectedApiResponseException('Provider returned an unexpected response.'); } $userProfile = $this->fetchUserProfile($openidAttributes); /* with openid providers we only get user profiles once, so we store it */ $this->storage->set($this->providerId . '.user', $userProfile); } /** * Fetch user profile from received openid attributes * * @param array $openidAttributes * * @return User\Profile */ protected function fetchUserProfile($openidAttributes) { $data = new Data\Collection($openidAttributes); $userProfile = new User\Profile(); $userProfile->identifier = $this->openIdClient->identity; $userProfile->firstName = $data->get('namePerson/first'); $userProfile->lastName = $data->get('namePerson/last'); $userProfile->email = $data->get('contact/email'); $userProfile->language = $data->get('pref/language'); $userProfile->country = $data->get('contact/country/home'); $userProfile->zip = $data->get('contact/postalCode/home'); $userProfile->gender = $data->get('person/gender'); $userProfile->photoURL = $data->get('media/image/default'); $userProfile->birthDay = $data->get('birthDate/birthDay'); $userProfile->birthMonth = $data->get('birthDate/birthMonth'); $userProfile->birthYear = $data->get('birthDate/birthDate'); $userProfile = $this->fetchUserGender($userProfile, $data->get('person/gender')); $userProfile = $this->fetchUserDisplayName($userProfile, $data); return $userProfile; } /** * Extract users display names * * @param User\Profile $userProfile * @param Data\Collection $data * * @return User\Profile */ protected function fetchUserDisplayName(User\Profile $userProfile, Data\Collection $data) { $userProfile->displayName = $data->get('namePerson'); $userProfile->displayName = $userProfile->displayName ? $userProfile->displayName : $data->get('namePerson/friendly'); $userProfile->displayName = $userProfile->displayName ? $userProfile->displayName : trim($userProfile->firstName . ' ' . $userProfile->lastName); return $userProfile; } /** * Extract users gender * * @param User\Profile $userProfile * @param string $gender * * @return User\Profile */ protected function fetchUserGender(User\Profile $userProfile, $gender) { $gender = strtolower((string)$gender); if ('f' == $gender) { $gender = 'female'; } if ('m' == $gender) { $gender = 'male'; } $userProfile->gender = $gender; return $userProfile; } /** * OpenID only provide the user profile one. This method will attempt to retrieve the profile from storage. */ public function getUserProfile() { $userProfile = $this->storage->get($this->providerId . '.user'); if (!is_object($userProfile)) { throw new UnexpectedApiResponseException('Provider returned an unexpected response.'); } return $userProfile; } } Adapter/DataStoreTrait.php 0000644 00000003640 15103446660 0011537 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Adapter; /** * Trait DataStoreTrait */ trait DataStoreTrait { /** * Returns storage instance * * @return \Hybridauth\Storage\StorageInterface */ abstract public function getStorage(); /** * Store a piece of data in storage. * * This method is mainly used for OAuth tokens (access, secret, refresh, and whatnot), but it * can be also used by providers to store any other useful data (i.g., user_id, auth_nonce, etc.) * * @param string $name * @param mixed $value */ protected function storeData($name, $value = null) { // if empty, we simply delete the thing as we'd want to only store necessary data if (empty($value)) { $this->deleteStoredData($name); } $this->getStorage()->set($this->providerId . '.' . $name, $value); } /** * Retrieve a piece of data from storage. * * This method is mainly used for OAuth tokens (access, secret, refresh, and whatnot), but it * can be also used by providers to retrieve from store any other useful data (i.g., user_id, * auth_nonce, etc.) * * @param string $name * * @return mixed */ protected function getStoredData($name) { return $this->getStorage()->get($this->providerId . '.' . $name); } /** * Delete a stored piece of data. * * @param string $name */ protected function deleteStoredData($name) { $this->getStorage()->delete($this->providerId . '.' . $name); } /** * Delete all stored data of the instantiated adapter */ protected function clearStoredData() { $this->getStorage()->deleteMatch($this->providerId . '.'); } } Adapter/AdapterInterface.php 0000644 00000006520 15103446660 0012046 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Adapter; use Hybridauth\HttpClient\HttpClientInterface; use Hybridauth\Storage\StorageInterface; use Hybridauth\Logger\LoggerInterface; /** * Interface AdapterInterface */ interface AdapterInterface { /** * Initiate the appropriate protocol and process/automate the authentication or authorization flow. * * @return bool|null */ public function authenticate(); /** * Returns TRUE if the user is connected * * @return bool */ public function isConnected(); /** * Clear all access token in storage */ public function disconnect(); /** * Retrieve the connected user profile * * @return \Hybridauth\User\Profile */ public function getUserProfile(); /** * Retrieve the connected user contacts list * * @return \Hybridauth\User\Contact[] */ public function getUserContacts(); /** * Retrieve the connected user pages|companies|groups list * * @return array */ public function getUserPages(); /** * Retrieve the user activity stream * * @param string $stream * * @return \Hybridauth\User\Activity[] */ public function getUserActivity($stream); /** * Post a status on user wall|timeline|blog|website|etc. * * @param string|array $status * * @return mixed API response */ public function setUserStatus($status); /** * Post a status on page|company|group wall. * * @param string|array $status * @param string $pageId * * @return mixed API response */ public function setPageStatus($status, $pageId); /** * Send a signed request to provider API * * @param string $url * @param string $method * @param array $parameters * @param array $headers * @param bool $multipart * * @return mixed */ public function apiRequest($url, $method = 'GET', $parameters = [], $headers = [], $multipart = false); /** * Do whatever may be necessary to make sure tokens do not expire. * Intended to be be called frequently, e.g. via Cron. */ public function maintainToken(); /** * Return oauth access tokens. * * @return array */ public function getAccessToken(); /** * Set oauth access tokens. * * @param array $tokens */ public function setAccessToken($tokens = []); /** * Set http client instance. * * @param HttpClientInterface $httpClient */ public function setHttpClient(HttpClientInterface $httpClient = null); /** * Return http client instance. */ public function getHttpClient(); /** * Set storage instance. * * @param StorageInterface $storage */ public function setStorage(StorageInterface $storage = null); /** * Return storage instance. */ public function getStorage(); /** * Set Logger instance. * * @param LoggerInterface $logger */ public function setLogger(LoggerInterface $logger = null); /** * Return logger instance. */ public function getLogger(); } Adapter/OAuth1.php 0000644 00000044121 15103446660 0007745 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Adapter; use Hybridauth\Exception\Exception; use Hybridauth\Exception\InvalidApplicationCredentialsException; use Hybridauth\Exception\AuthorizationDeniedException; use Hybridauth\Exception\InvalidOauthTokenException; use Hybridauth\Exception\InvalidAccessTokenException; use Hybridauth\Data; use Hybridauth\HttpClient; use Hybridauth\Thirdparty\OAuth\OAuthConsumer; use Hybridauth\Thirdparty\OAuth\OAuthRequest; use Hybridauth\Thirdparty\OAuth\OAuthSignatureMethodHMACSHA1; use Hybridauth\Thirdparty\OAuth\OAuthUtil; /** * This class can be used to simplify the authorization flow of OAuth 1 based service providers. * * Subclasses (i.e., providers adapters) can either use the already provided methods or override * them when necessary. */ abstract class OAuth1 extends AbstractAdapter implements AdapterInterface { /** * Base URL to provider API * * This var will be used to build urls when sending signed requests * * @var string */ protected $apiBaseUrl = ''; /** * @var string */ protected $authorizeUrl = ''; /** * @var string */ protected $requestTokenUrl = ''; /** * @var string */ protected $accessTokenUrl = ''; /** * IPD API Documentation * * OPTIONAL. * * @var string */ protected $apiDocumentation = ''; /** * OAuth Version * * '1.0' OAuth Core 1.0 * '1.0a' OAuth Core 1.0 Revision A * * @var string */ protected $oauth1Version = '1.0a'; /** * @var string */ protected $consumerKey = null; /** * @var string */ protected $consumerSecret = null; /** * @var object */ protected $OAuthConsumer = null; /** * @var object */ protected $sha1Method = null; /** * @var object */ protected $consumerToken = null; /** * Authorization Url Parameters * * @var bool */ protected $AuthorizeUrlParameters = []; /** * @var string */ protected $requestTokenMethod = 'POST'; /** * @var array */ protected $requestTokenParameters = []; /** * @var array */ protected $requestTokenHeaders = []; /** * @var string */ protected $tokenExchangeMethod = 'POST'; /** * @var array */ protected $tokenExchangeParameters = []; /** * @var array */ protected $tokenExchangeHeaders = []; /** * @var array */ protected $apiRequestParameters = []; /** * @var array */ protected $apiRequestHeaders = []; /** * {@inheritdoc} */ protected function configure() { $this->consumerKey = $this->config->filter('keys')->get('id') ?: $this->config->filter('keys')->get('key'); $this->consumerSecret = $this->config->filter('keys')->get('secret'); if (!$this->consumerKey || !$this->consumerSecret) { throw new InvalidApplicationCredentialsException( 'Your application id is required in order to connect to ' . $this->providerId ); } if ($this->config->exists('tokens')) { $this->setAccessToken($this->config->get('tokens')); } $this->setCallback($this->config->get('callback')); $this->setApiEndpoints($this->config->get('endpoints')); } /** * {@inheritdoc} */ protected function initialize() { /** * Set up OAuth Signature and Consumer * * OAuth Core: All Token requests and Protected Resources requests MUST be signed * by the Consumer and verified by the Service Provider. * * The protocol defines three signature methods: HMAC-SHA1, RSA-SHA1, and PLAINTEXT.. * * The Consumer declares a signature method in the oauth_signature_method parameter.. * * http://oauth.net/core/1.0a/#signing_process */ $this->sha1Method = new OAuthSignatureMethodHMACSHA1(); $this->OAuthConsumer = new OAuthConsumer( $this->consumerKey, $this->consumerSecret ); if ($this->getStoredData('request_token')) { $this->consumerToken = new OAuthConsumer( $this->getStoredData('request_token'), $this->getStoredData('request_token_secret') ); } if ($this->getStoredData('access_token')) { $this->consumerToken = new OAuthConsumer( $this->getStoredData('access_token'), $this->getStoredData('access_token_secret') ); } } /** * {@inheritdoc} */ public function authenticate() { $this->logger->info(sprintf('%s::authenticate()', get_class($this))); if ($this->isConnected()) { return true; } try { if (!$this->getStoredData('request_token')) { // Start a new flow. $this->authenticateBegin(); } elseif (empty($_GET['oauth_token']) && empty($_GET['denied'])) { // A previous authentication was not finished, and this request is not finishing it. $this->authenticateBegin(); } else { // Finish a flow. $this->authenticateFinish(); } } catch (Exception $exception) { $this->clearStoredData(); throw $exception; } return null; } /** * {@inheritdoc} */ public function isConnected() { return (bool)$this->getStoredData('access_token'); } /** * Initiate the authorization protocol * * 1. Obtaining an Unauthorized Request Token * 2. Build Authorization URL for Authorization Request and redirect the user-agent to the * Authorization Server. */ protected function authenticateBegin() { $response = $this->requestAuthToken(); $this->validateAuthTokenRequest($response); $authUrl = $this->getAuthorizeUrl(); $this->logger->debug(sprintf('%s::authenticateBegin(), redirecting user to:', get_class($this)), [$authUrl]); HttpClient\Util::redirect($authUrl); } /** * Finalize the authorization process * * @throws AuthorizationDeniedException * @throws \Hybridauth\Exception\HttpClientFailureException * @throws \Hybridauth\Exception\HttpRequestFailedException * @throws InvalidAccessTokenException * @throws InvalidOauthTokenException */ protected function authenticateFinish() { $this->logger->debug( sprintf('%s::authenticateFinish(), callback url:', get_class($this)), [HttpClient\Util::getCurrentUrl(true)] ); $denied = filter_input(INPUT_GET, 'denied'); $oauth_problem = filter_input(INPUT_GET, 'oauth_problem'); $oauth_token = filter_input(INPUT_GET, 'oauth_token'); $oauth_verifier = filter_input(INPUT_GET, 'oauth_verifier'); if ($denied) { throw new AuthorizationDeniedException( 'User denied access request. Provider returned a denied token: ' . htmlentities($denied) ); } if ($oauth_problem) { throw new InvalidOauthTokenException( 'Provider returned an error. oauth_problem: ' . htmlentities($oauth_problem) ); } if (!$oauth_token) { throw new InvalidOauthTokenException( 'Expecting a non-null oauth_token to continue the authorization flow.' ); } $response = $this->exchangeAuthTokenForAccessToken($oauth_token, $oauth_verifier); $this->validateAccessTokenExchange($response); $this->initialize(); } /** * Build Authorization URL for Authorization Request * * @param array $parameters * * @return string */ protected function getAuthorizeUrl($parameters = []) { $this->AuthorizeUrlParameters = !empty($parameters) ? $parameters : array_replace( (array)$this->AuthorizeUrlParameters, (array)$this->config->get('authorize_url_parameters') ); $this->AuthorizeUrlParameters['oauth_token'] = $this->getStoredData('request_token'); return $this->authorizeUrl . '?' . http_build_query($this->AuthorizeUrlParameters, '', '&'); } /** * Unauthorized Request Token * * OAuth Core: The Consumer obtains an unauthorized Request Token by asking the Service Provider * to issue a Token. The Request Token's sole purpose is to receive User approval and can only * be used to obtain an Access Token. * * http://oauth.net/core/1.0/#auth_step1 * 6.1.1. Consumer Obtains a Request Token * * @return string Raw Provider API response * @throws \Hybridauth\Exception\HttpClientFailureException * @throws \Hybridauth\Exception\HttpRequestFailedException */ protected function requestAuthToken() { /** * OAuth Core 1.0 Revision A: oauth_callback: An absolute URL to which the Service Provider will redirect * the User back when the Obtaining User Authorization step is completed. * * http://oauth.net/core/1.0a/#auth_step1 */ if ('1.0a' == $this->oauth1Version) { $this->requestTokenParameters['oauth_callback'] = $this->callback; } $response = $this->oauthRequest( $this->requestTokenUrl, $this->requestTokenMethod, $this->requestTokenParameters, $this->requestTokenHeaders ); return $response; } /** * Validate Unauthorized Request Token Response * * OAuth Core: The Service Provider verifies the signature and Consumer Key. If successful, * it generates a Request Token and Token Secret and returns them to the Consumer in the HTTP * response body. * * http://oauth.net/core/1.0/#auth_step1 * 6.1.2. Service Provider Issues an Unauthorized Request Token * * @param string $response * * @return \Hybridauth\Data\Collection * @throws InvalidOauthTokenException */ protected function validateAuthTokenRequest($response) { /** * The response contains the following parameters: * * - oauth_token The Request Token. * - oauth_token_secret The Token Secret. * - oauth_callback_confirmed MUST be present and set to true. * * http://oauth.net/core/1.0/#auth_step1 * 6.1.2. Service Provider Issues an Unauthorized Request Token * * Example of a successful response: * * HTTP/1.1 200 OK * Content-Type: text/html; charset=utf-8 * Cache-Control: no-store * Pragma: no-cache * * oauth_token=80359084-clg1DEtxQF3wstTcyUdHF3wsdHM&oauth_token_secret=OIF07hPmJB:P * 6qiHTi1znz6qiH3tTcyUdHnz6qiH3tTcyUdH3xW3wsDvV08e&example_parameter=example_value * * OAuthUtil::parse_parameters will attempt to decode the raw response into an array. */ $tokens = OAuthUtil::parse_parameters($response); $collection = new Data\Collection($tokens); if (!$collection->exists('oauth_token')) { throw new InvalidOauthTokenException( 'Provider returned no oauth_token: ' . htmlentities($response) ); } $this->consumerToken = new OAuthConsumer( $tokens['oauth_token'], $tokens['oauth_token_secret'] ); $this->storeData('request_token', $tokens['oauth_token']); $this->storeData('request_token_secret', $tokens['oauth_token_secret']); return $collection; } /** * Requests an Access Token * * OAuth Core: The Request Token and Token Secret MUST be exchanged for an Access Token and Token Secret. * * http://oauth.net/core/1.0a/#auth_step3 * 6.3.1. Consumer Requests an Access Token * * @param string $oauth_token * @param string $oauth_verifier * * @return string Raw Provider API response * @throws \Hybridauth\Exception\HttpClientFailureException * @throws \Hybridauth\Exception\HttpRequestFailedException */ protected function exchangeAuthTokenForAccessToken($oauth_token, $oauth_verifier = '') { $this->tokenExchangeParameters['oauth_token'] = $oauth_token; /** * OAuth Core 1.0 Revision A: oauth_verifier: The verification code received from the Service Provider * in the "Service Provider Directs the User Back to the Consumer" step. * * http://oauth.net/core/1.0a/#auth_step3 */ if ('1.0a' == $this->oauth1Version) { $this->tokenExchangeParameters['oauth_verifier'] = $oauth_verifier; } $response = $this->oauthRequest( $this->accessTokenUrl, $this->tokenExchangeMethod, $this->tokenExchangeParameters, $this->tokenExchangeHeaders ); return $response; } /** * Validate Access Token Response * * OAuth Core: If successful, the Service Provider generates an Access Token and Token Secret and returns * them in the HTTP response body. * * The Access Token and Token Secret are stored by the Consumer and used when signing Protected Resources requests. * * http://oauth.net/core/1.0a/#auth_step3 * 6.3.2. Service Provider Grants an Access Token * * @param string $response * * @return \Hybridauth\Data\Collection * @throws InvalidAccessTokenException */ protected function validateAccessTokenExchange($response) { /** * The response contains the following parameters: * * - oauth_token The Access Token. * - oauth_token_secret The Token Secret. * * http://oauth.net/core/1.0/#auth_step3 * 6.3.2. Service Provider Grants an Access Token * * Example of a successful response: * * HTTP/1.1 200 OK * Content-Type: text/html; charset=utf-8 * Cache-Control: no-store * Pragma: no-cache * * oauth_token=sHeLU7Far428zj8PzlWR75&oauth_token_secret=fXb30rzoG&oauth_callback_confirmed=true * * OAuthUtil::parse_parameters will attempt to decode the raw response into an array. */ $tokens = OAuthUtil::parse_parameters($response); $collection = new Data\Collection($tokens); if (!$collection->exists('oauth_token')) { throw new InvalidAccessTokenException( 'Provider returned no access_token: ' . htmlentities($response) ); } $this->consumerToken = new OAuthConsumer( $collection->get('oauth_token'), $collection->get('oauth_token_secret') ); $this->storeData('access_token', $collection->get('oauth_token')); $this->storeData('access_token_secret', $collection->get('oauth_token_secret')); $this->deleteStoredData('request_token'); $this->deleteStoredData('request_token_secret'); return $collection; } /** * Send a signed request to provider API * * Note: Since the specifics of error responses is beyond the scope of RFC6749 and OAuth specifications, * Hybridauth will consider any HTTP status code that is different than '200 OK' as an ERROR. * * @param string $url * @param string $method * @param array $parameters * @param array $headers * @param bool $multipart * * @return mixed * @throws \Hybridauth\Exception\HttpClientFailureException * @throws \Hybridauth\Exception\HttpRequestFailedException */ public function apiRequest($url, $method = 'GET', $parameters = [], $headers = [], $multipart = false) { // refresh tokens if needed $this->maintainToken(); if (strrpos($url, 'http://') !== 0 && strrpos($url, 'https://') !== 0) { $url = rtrim($this->apiBaseUrl, '/') . '/' . ltrim($url, '/'); } $parameters = array_replace($this->apiRequestParameters, (array)$parameters); $headers = array_replace($this->apiRequestHeaders, (array)$headers); $response = $this->oauthRequest($url, $method, $parameters, $headers, $multipart); $response = (new Data\Parser())->parse($response); return $response; } /** * Setup and Send a Signed Oauth Request * * This method uses OAuth Library. * * @param string $uri * @param string $method * @param array $parameters * @param array $headers * @param bool $multipart * * @return string Raw Provider API response * @throws \Hybridauth\Exception\HttpClientFailureException * @throws \Hybridauth\Exception\HttpRequestFailedException */ protected function oauthRequest($uri, $method = 'GET', $parameters = [], $headers = [], $multipart = false) { $signing_parameters = $parameters; if ($multipart) { $signing_parameters = []; } $request = OAuthRequest::from_consumer_and_token( $this->OAuthConsumer, $this->consumerToken, $method, $uri, $signing_parameters ); $request->sign_request( $this->sha1Method, $this->OAuthConsumer, $this->consumerToken ); $uri = $request->get_normalized_http_url(); $headers = array_replace($request->to_header(), (array)$headers); $response = $this->httpClient->request( $uri, $method, $parameters, $headers, $multipart ); $this->validateApiResponse('Signed API request to ' . $uri . ' has returned an error'); return $response; } } User/Activity.php 0000644 00000002765 15103446660 0010006 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\User; use Hybridauth\Exception\UnexpectedValueException; /** * Hybridauth\User\Activity */ final class Activity { /** * activity id on the provider side, usually given as integer * * @var string */ public $id = null; /** * activity date of creation * * @var string */ public $date = null; /** * activity content as a string * * @var string */ public $text = null; /** * user who created the activity * * @var object */ public $user = null; /** * */ public function __construct() { $this->user = new \stdClass(); // typically, we should have a few information about the user who created the event from social apis $this->user->identifier = null; $this->user->displayName = null; $this->user->profileURL = null; $this->user->photoURL = null; } /** * Prevent the providers adapters from adding new fields. * * @throws UnexpectedValueException * @var string $name * * @var mixed $value * */ public function __set($name, $value) { // phpcs:ignore throw new UnexpectedValueException(sprintf('Adding new property "%s\' to %s is not allowed.', $name, __CLASS__)); } } User/Contact.php 0000644 00000002765 15103446660 0007605 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\User; use Hybridauth\Exception\UnexpectedValueException; /** * Hybridauth\User\Contact */ final class Contact { /** * The Unique contact user ID * * @var string */ public $identifier = null; /** * User website, blog, web page * * @var string */ public $webSiteURL = null; /** * URL link to profile page on the IDp web site * * @var string */ public $profileURL = null; /** * URL link to user photo or avatar * * @var string */ public $photoURL = null; /** * User displayName provided by the IDp or a concatenation of first and last name * * @var string */ public $displayName = null; /** * A short about_me * * @var string */ public $description = null; /** * User email. Not all of IDp grant access to the user email * * @var string */ public $email = null; /** * Prevent the providers adapters from adding new fields. * * @param string $name * @param mixed $value * * @throws UnexpectedValueException */ public function __set($name, $value) { throw new UnexpectedValueException(sprintf('Adding new property "%s" to %s is not allowed.', $name, __CLASS__)); } } User/Profile.php 0000644 00000006352 15103446660 0007606 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\User; use Hybridauth\Exception\UnexpectedValueException; /** * Hybridauth\Userobject represents the current logged in user profile. */ final class Profile { /** * The Unique user's ID on the connected provider * * @var int|null */ public $identifier = null; /** * User website, blog, web page * * @var string|null */ public $webSiteURL = null; /** * URL link to profile page on the IDp web site * * @var string|null */ public $profileURL = null; /** * URL link to user photo or avatar * * @var string|null */ public $photoURL = null; /** * User displayName provided by the IDp or a concatenation of first and last name. * * @var string|null */ public $displayName = null; /** * A short about_me * * @var string|null */ public $description = null; /** * User's first name * * @var string|null */ public $firstName = null; /** * User's last name * * @var string|null */ public $lastName = null; /** * male or female * * @var string|null */ public $gender = null; /** * Language * * @var string|null */ public $language = null; /** * User age, we don't calculate it. we return it as is if the IDp provide it. * * @var int|null */ public $age = null; /** * User birth Day * * @var int|null */ public $birthDay = null; /** * User birth Month * * @var int|null */ public $birthMonth = null; /** * User birth Year * * @var int|null */ public $birthYear = null; /** * User email. Note: not all of IDp grant access to the user email * * @var string|null */ public $email = null; /** * Verified user email. Note: not all of IDp grant access to verified user email * * @var string|null */ public $emailVerified = null; /** * Phone number * * @var string|null */ public $phone = null; /** * Complete user address * * @var string|null */ public $address = null; /** * User country * * @var string|null */ public $country = null; /** * Region * * @var string|null */ public $region = null; /** * City * * @var string|null */ public $city = null; /** * Postal code * * @var string|null */ public $zip = null; /** * An extra data which is related to the user * * @var array */ public $data = []; /** * Prevent the providers adapters from adding new fields. * * @throws UnexpectedValueException * @var mixed $value * * @var string $name */ public function __set($name, $value) { throw new UnexpectedValueException(sprintf('Adding new property "%s" to %s is not allowed.', $name, __CLASS__)); } } Thirdparty/readme.md 0000644 00000001355 15103446660 0010466 0 ustar 00 ##### Third party libraries Here we include a number of third party libraries. Those libraries are used by the various providers supported by Hybridauth. Library | Description -------- | ------------- [LightOpenID](https://gitorious.org/lightopenid) | Contain LightOpenID. Solid OpenID library licensed under the MIT License. [OAuth Library](https://code.google.com/p/oauth/) | Contain OAuth Library licensed under the MIT License. Notes: We no longer use the old OAuth clients. Please don't add new libs to this folder, unless strictly necessary. Both LightOpenID and OAuth are (to be) partially/indirectly tested within the Hybridauth library. Both LightOpenID and OAuth libraries are excluded from Codeclimate.com Analysis/GPA. Thirdparty/OpenID/README.md 0000644 00000000323 15103446660 0011276 0 ustar 00 This file is part of the LightOpenID PHP Library LightOpenID is an open source software available under the MIT License. https://github.com/iignatov/LightOpenID http://opensource.org/licenses/mit-license.php Thirdparty/OpenID/LightOpenID.php 0000644 00000127653 15103446660 0012656 0 ustar 00 <?php /*! * This file is part of the LightOpenID PHP Library (https://github.com/iignatov/LightOpenID) * * LightOpenID is an open source software available under the MIT License. * * Updated: 52f9910 on 4 Mar 2016. */ namespace Hybridauth\Thirdparty\OpenID; use Hybridauth\Exception\Exception; use Hybridauth\Exception\ExceptionInterface; /** * Class ErrorException * * @package Hybridauth\Thirdparty\OpenID */ class ErrorException extends Exception implements ExceptionInterface { } /** * This class provides a simple interface for OpenID 1.1/2.0 authentication. * * It requires PHP >= 5.1.2 with cURL or HTTP/HTTPS stream wrappers enabled. * * @version v1.3.1 (2016-03-04) * @link https://code.google.com/p/lightopenid/ Project URL * @link https://github.com/iignatov/LightOpenID GitHub Repo * @author Mewp <mewp151 at gmail dot com> * @copyright Copyright (c) 2013 Mewp * @license http://opensource.org/licenses/mit-license.php MIT License */ class LightOpenID { public $returnUrl ; public $required = array() ; public $optional = array() ; public $verify_peer = null ; public $capath = null ; public $cainfo = null ; public $cnmatch = null ; public $data ; public $oauth = array() ; public $curl_time_out = 30 // in seconds ; public $curl_connect_time_out = 30; // in seconds private $identity; private $claimed_id; protected $server; protected $version; protected $trustRoot; protected $aliases; protected $identifier_select = false ; protected $ax = false; protected $sreg = false; protected $setup_url = null; protected $headers = array() ; protected $proxy = null; protected $user_agent = 'LightOpenID' ; protected $xrds_override_pattern = null; protected $xrds_override_replacement = null; protected static $ax_to_sreg = array( 'namePerson/friendly' => 'nickname', 'contact/email' => 'email', 'namePerson' => 'fullname', 'birthDate' => 'dob', 'person/gender' => 'gender', 'contact/postalCode/home' => 'postcode', 'contact/country/home' => 'country', 'pref/language' => 'language', 'pref/timezone' => 'timezone', ); /** * LightOpenID constructor. * * @param $host * @param null $proxy * * @throws ErrorException */ public function __construct($host, $proxy = null) { $this->set_realm($host); $this->set_proxy($proxy); $uri = rtrim(preg_replace('#((?<=\?)|&)openid\.[^&]+#', '', $_SERVER['REQUEST_URI']), '?'); $this->returnUrl = $this->trustRoot . $uri; $this->data = ($_SERVER['REQUEST_METHOD'] === 'POST') ? $_POST : $_GET; if (!function_exists('curl_init') && !in_array('https', stream_get_wrappers())) { throw new ErrorException('You must have either https wrappers or curl enabled.'); } } /** * @param $name * * @return bool */ public function __isset($name) { return in_array($name, array('identity', 'trustRoot', 'realm', 'xrdsOverride', 'mode')); } /** * @param $name * @param $value */ public function __set($name, $value) { switch ($name) { case 'identity': if (strlen($value = trim((String) $value))) { if (preg_match('#^xri:/*#i', $value, $m)) { $value = substr($value, strlen($m[0])); } elseif (!preg_match('/^(?:[=@+\$!\(]|https?:)/i', $value)) { $value = "http://$value"; } if (preg_match('#^https?://[^/]+$#i', $value, $m)) { $value .= '/'; } } $this->$name = $this->claimed_id = $value; break; case 'trustRoot': case 'realm': $this->trustRoot = trim($value); break; case 'xrdsOverride': if (is_array($value)) { list($pattern, $replacement) = $value; $this->xrds_override_pattern = $pattern; $this->xrds_override_replacement = $replacement; } else { trigger_error('Invalid value specified for "xrdsOverride".', E_USER_ERROR); } break; } } /** * @param $name * * @return |null */ public function __get($name) { switch ($name) { case 'identity': # We return claimed_id instead of identity, # because the developer should see the claimed identifier, # i.e. what he set as identity, not the op-local identifier (which is what we verify) return $this->claimed_id; case 'trustRoot': case 'realm': return $this->trustRoot; case 'mode': return empty($this->data['openid_mode']) ? null : $this->data['openid_mode']; } } /** * @param $proxy * * @throws ErrorException */ public function set_proxy($proxy) { if (!empty($proxy)) { // When the proxy is a string - try to parse it. if (!is_array($proxy)) { $proxy = parse_url($proxy); } // Check if $proxy is valid after the parsing. if ($proxy && !empty($proxy['host'])) { // Make sure that a valid port number is specified. if (array_key_exists('port', $proxy)) { if (!is_int($proxy['port'])) { $proxy['port'] = is_numeric($proxy['port']) ? intval($proxy['port']) : 0; } if ($proxy['port'] <= 0) { throw new ErrorException('The specified proxy port number is invalid.'); } } $this->proxy = $proxy; } } } /** * Checks if the server specified in the url exists. * * @param $url string url to check * @return true, if the server exists; false otherwise */ public function hostExists($url) { if (strpos($url, '/') === false) { $server = $url; } else { $server = @parse_url($url, PHP_URL_HOST); } if (!$server) { return false; } return !!gethostbynamel($server); } /** * @param $uri */ protected function set_realm($uri) { $realm = ''; # Set a protocol, if not specified. $realm .= (($offset = strpos($uri, '://')) === false) ? $this->get_realm_protocol() : ''; # Set the offset properly. $offset = (($offset !== false) ? $offset + 3 : 0); # Get only the root, without the path. $realm .= (($end = strpos($uri, '/', $offset)) === false) ? $uri : substr($uri, 0, $end); $this->trustRoot = $realm; } /** * @return string */ protected function get_realm_protocol() { if (!empty($_SERVER['HTTPS'])) { $use_secure_protocol = ($_SERVER['HTTPS'] !== 'off'); } elseif (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) { $use_secure_protocol = ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https'); } elseif (isset($_SERVER['HTTP__WSSC'])) { $use_secure_protocol = ($_SERVER['HTTP__WSSC'] == 'https'); } else { $use_secure_protocol = false; } return $use_secure_protocol ? 'https://' : 'http://'; } /** * @param $url * @param string $method * @param array $params * @param $update_claimed_id * * @return array|bool|string * @throws ErrorException */ protected function request_curl($url, $method='GET', $params=array(), $update_claimed_id=false) { $params = http_build_query($params, '', '&'); $curl = curl_init($url . ($method == 'GET' && $params ? '?' . $params : '')); curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); curl_setopt($curl, CURLOPT_HEADER, false); curl_setopt($curl, CURLOPT_USERAGENT, $this->user_agent); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); if ($method == 'POST') { curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded')); } else { curl_setopt($curl, CURLOPT_HTTPHEADER, array('Accept: application/xrds+xml, */*')); } curl_setopt($curl, CURLOPT_TIMEOUT, $this->curl_time_out); // defaults to infinite curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $this->curl_connect_time_out); // defaults to 300s if (!empty($this->proxy)) { curl_setopt($curl, CURLOPT_PROXY, $this->proxy['host']); if (!empty($this->proxy['port'])) { curl_setopt($curl, CURLOPT_PROXYPORT, $this->proxy['port']); } if (!empty($this->proxy['user'])) { curl_setopt($curl, CURLOPT_PROXYUSERPWD, $this->proxy['user'] . ':' . $this->proxy['pass']); } } if ($this->verify_peer !== null) { curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verify_peer); if ($this->capath) { curl_setopt($curl, CURLOPT_CAPATH, $this->capath); } if ($this->cainfo) { curl_setopt($curl, CURLOPT_CAINFO, $this->cainfo); } } if ($method == 'POST') { curl_setopt($curl, CURLOPT_POST, true); curl_setopt($curl, CURLOPT_POSTFIELDS, $params); } elseif ($method == 'HEAD') { curl_setopt($curl, CURLOPT_HEADER, true); curl_setopt($curl, CURLOPT_NOBODY, true); } else { curl_setopt($curl, CURLOPT_HEADER, true); curl_setopt($curl, CURLOPT_HTTPGET, true); } $response = curl_exec($curl); if ($method == 'HEAD' && curl_getinfo($curl, CURLINFO_HTTP_CODE) == 405) { curl_setopt($curl, CURLOPT_HTTPGET, true); $response = curl_exec($curl); $response = substr($response, 0, strpos($response, "\r\n\r\n")); } if ($method == 'HEAD' || $method == 'GET') { $header_response = $response; # If it's a GET request, we want to only parse the header part. if ($method == 'GET') { $header_response = substr($response, 0, strpos($response, "\r\n\r\n")); } $headers = array(); foreach (explode("\n", $header_response) as $header) { $pos = strpos($header, ':'); if ($pos !== false) { $name = strtolower(trim(substr($header, 0, $pos))); $headers[$name] = trim(substr($header, $pos+1)); } } if ($update_claimed_id) { # Update the claimed_id value in case of redirections. $effective_url = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL); # Ignore the fragment (some cURL versions don't handle it well). if (strtok($effective_url, '#') != strtok($url, '#')) { $this->identity = $this->claimed_id = $effective_url; } } if ($method == 'HEAD') { return $headers; } else { $this->headers = $headers; } } if (curl_errno($curl)) { throw new ErrorException(curl_error($curl), curl_errno($curl)); } return $response; } /** * @param $array * @param $update_claimed_id * * @return array */ protected function parse_header_array($array, $update_claimed_id) { $headers = array(); foreach ($array as $header) { $pos = strpos($header, ':'); if ($pos !== false) { $name = strtolower(trim(substr($header, 0, $pos))); $headers[$name] = trim(substr($header, $pos+1)); # Following possible redirections. The point is just to have # claimed_id change with them, because the redirections # are followed automatically. # We ignore redirections with relative paths. # If any known provider uses them, file a bug report. if ($name == 'location' && $update_claimed_id) { if (strpos($headers[$name], 'http') === 0) { $this->identity = $this->claimed_id = $headers[$name]; } elseif ($headers[$name][0] == '/') { $parsed_url = parse_url($this->claimed_id); $this->identity = $this->claimed_id = $parsed_url['scheme'] . '://' . $parsed_url['host'] . $headers[$name]; } } } } return $headers; } /** * @param $url * @param string $method * @param array $params * @param $update_claimed_id * * @return array|false|string * @throws ErrorException */ protected function request_streams($url, $method='GET', $params=array(), $update_claimed_id=false) { if (!$this->hostExists($url)) { throw new ErrorException("Could not connect to $url.", 404); } if (empty($this->cnmatch)) { $this->cnmatch = parse_url($url, PHP_URL_HOST); } $params = http_build_query($params, '', '&'); switch ($method) { case 'GET': $opts = array( 'http' => array( 'method' => 'GET', 'header' => 'Accept: application/xrds+xml, */*', 'user_agent' => $this->user_agent, 'ignore_errors' => true, ), 'ssl' => array( 'CN_match' => $this->cnmatch ) ); $url = $url . ($params ? '?' . $params : ''); if (!empty($this->proxy)) { $opts['http']['proxy'] = $this->proxy_url(); } break; case 'POST': $opts = array( 'http' => array( 'method' => 'POST', 'header' => 'Content-type: application/x-www-form-urlencoded', 'user_agent' => $this->user_agent, 'content' => $params, 'ignore_errors' => true, ), 'ssl' => array( 'CN_match' => $this->cnmatch ) ); if (!empty($this->proxy)) { $opts['http']['proxy'] = $this->proxy_url(); } break; case 'HEAD': // We want to send a HEAD request, but since get_headers() doesn't // accept $context parameter, we have to change the defaults. $default = stream_context_get_options(stream_context_get_default()); // PHP does not reset all options. Instead, it just sets the options // available in the passed array, therefore set the defaults manually. $default += array( 'http' => array(), 'ssl' => array() ); $default['http'] += array( 'method' => 'GET', 'header' => '', 'user_agent' => '', 'ignore_errors' => false ); $default['ssl'] += array( 'CN_match' => '' ); $opts = array( 'http' => array( 'method' => 'HEAD', 'header' => 'Accept: application/xrds+xml, */*', 'user_agent' => $this->user_agent, 'ignore_errors' => true, ), 'ssl' => array( 'CN_match' => $this->cnmatch ) ); // Enable validation of the SSL certificates. if ($this->verify_peer) { $default['ssl'] += array( 'verify_peer' => false, 'capath' => '', 'cafile' => '' ); $opts['ssl'] += array( 'verify_peer' => true, 'capath' => $this->capath, 'cafile' => $this->cainfo ); } // Change the stream context options. stream_context_get_default($opts); $headers = get_headers($url . ($params ? '?' . $params : '')); // Restore the stream context options. stream_context_get_default($default); if (!empty($headers)) { if (intval(substr($headers[0], strlen('HTTP/1.1 '))) == 405) { // The server doesn't support HEAD - emulate it with a GET. $args = func_get_args(); $args[1] = 'GET'; call_user_func_array(array($this, 'request_streams'), $args); $headers = $this->headers; } else { $headers = $this->parse_header_array($headers, $update_claimed_id); } } else { $headers = array(); } return $headers; } if ($this->verify_peer) { $opts['ssl'] += array( 'verify_peer' => true, 'capath' => $this->capath, 'cafile' => $this->cainfo ); } $context = stream_context_create($opts); $data = file_get_contents($url, false, $context); # This is a hack for providers who don't support HEAD requests. # It just creates the headers array for the last request in $this->headers. if (isset($http_response_header)) { $this->headers = $this->parse_header_array($http_response_header, $update_claimed_id); } return $data; } /** * @param $url * @param string $method * @param array $params * @param bool $update_claimed_id * * @return array|bool|false|string * @throws ErrorException */ protected function request($url, $method='GET', $params=array(), $update_claimed_id=false) { $use_curl = false; if (function_exists('curl_init')) { if (!$use_curl) { # When allow_url_fopen is disabled, PHP streams will not work. $use_curl = !ini_get('allow_url_fopen'); } if (!$use_curl) { # When there is no HTTPS wrapper, PHP streams cannott be used. $use_curl = !in_array('https', stream_get_wrappers()); } if (!$use_curl) { # With open_basedir or safe_mode set, cURL can't follow redirects. $use_curl = !(ini_get('safe_mode') || ini_get('open_basedir')); } } return $use_curl ? $this->request_curl($url, $method, $params, $update_claimed_id) : $this->request_streams($url, $method, $params, $update_claimed_id); } /** * @return string */ protected function proxy_url() { $result = ''; if (!empty($this->proxy)) { $result = $this->proxy['host']; if (!empty($this->proxy['port'])) { $result = $result . ':' . $this->proxy['port']; } if (!empty($this->proxy['user'])) { $result = $this->proxy['user'] . ':' . $this->proxy['pass'] . '@' . $result; } $result = 'http://' . $result; } return $result; } /** * @param $url * @param $parts * * @return string */ protected function build_url($url, $parts) { if (isset($url['query'], $parts['query'])) { $parts['query'] = $url['query'] . '&' . $parts['query']; } $url = $parts + $url; $url = $url['scheme'] . '://' . (empty($url['username'])?'' :(empty($url['password'])? "{$url['username']}@" :"{$url['username']}:{$url['password']}@")) . $url['host'] . (empty($url['port'])?'':":{$url['port']}") . (empty($url['path'])?'':$url['path']) . (empty($url['query'])?'':"?{$url['query']}") . (empty($url['fragment'])?'':"#{$url['fragment']}"); return $url; } /** * Helper function used to scan for <meta>/<link> tags and extract information * from them * * @param $content * @param $tag * @param $attrName * @param $attrValue * @param $valueName * * @return bool */ protected function htmlTag($content, $tag, $attrName, $attrValue, $valueName) { preg_match_all("#<{$tag}[^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*$valueName=['\"](.+?)['\"][^>]*/?>#i", $content, $matches1); preg_match_all("#<{$tag}[^>]*$valueName=['\"](.+?)['\"][^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*/?>#i", $content, $matches2); $result = array_merge($matches1[1], $matches2[1]); return empty($result)?false:$result[0]; } /** * Performs Yadis and HTML discovery. Normally not used. * @param $url Identity URL. * @return String OP Endpoint (i.e. OpenID provider address). * @throws ErrorException */ public function discover($url) { if (!$url) { throw new ErrorException('No identity supplied.'); } # Use xri.net proxy to resolve i-name identities if (!preg_match('#^https?:#', $url)) { $url = "https://xri.net/$url"; } # We save the original url in case of Yadis discovery failure. # It can happen when we'll be lead to an XRDS document # which does not have any OpenID2 services. $originalUrl = $url; # A flag to disable yadis discovery in case of failure in headers. $yadis = true; # Allows optional regex replacement of the URL, e.g. to use Google Apps # as an OpenID provider without setting up XRDS on the domain hosting. if (!is_null($this->xrds_override_pattern) && !is_null($this->xrds_override_replacement)) { $url = preg_replace($this->xrds_override_pattern, $this->xrds_override_replacement, $url); } # We'll jump a maximum of 5 times, to avoid endless redirections. for ($i = 0; $i < 5; $i ++) { if ($yadis) { $headers = $this->request($url, 'HEAD', array(), true); $next = false; if (isset($headers['x-xrds-location'])) { $url = $this->build_url(parse_url($url), parse_url(trim($headers['x-xrds-location']))); $next = true; } if (isset($headers['content-type']) && $this->is_allowed_type($headers['content-type'])) { # Found an XRDS document, now let's find the server, and optionally delegate. $content = $this->request($url, 'GET'); preg_match_all('#<Service.*?>(.*?)</Service>#s', $content, $m); foreach ($m[1] as $content) { $content = ' ' . $content; # The space is added, so that strpos doesn't return 0. # OpenID 2 $ns = preg_quote('http://specs.openid.net/auth/2.0/', '#'); if (preg_match('#<Type>\s*'.$ns.'(server|signon)\s*</Type>#s', $content, $type)) { if ($type[1] == 'server') { $this->identifier_select = true; } preg_match('#<URI.*?>(.*)</URI>#', $content, $server); preg_match('#<(Local|Canonical)ID>(.*)</\1ID>#', $content, $delegate); if (empty($server)) { return false; } # Does the server advertise support for either AX or SREG? $this->ax = (bool) strpos($content, '<Type>http://openid.net/srv/ax/1.0</Type>'); $this->sreg = strpos($content, '<Type>http://openid.net/sreg/1.0</Type>') || strpos($content, '<Type>http://openid.net/extensions/sreg/1.1</Type>'); $server = $server[1]; if (isset($delegate[2])) { $this->identity = trim($delegate[2]); } $this->version = 2; $this->server = $server; return $server; } # OpenID 1.1 $ns = preg_quote('http://openid.net/signon/1.1', '#'); if (preg_match('#<Type>\s*'.$ns.'\s*</Type>#s', $content)) { preg_match('#<URI.*?>(.*)</URI>#', $content, $server); preg_match('#<.*?Delegate>(.*)</.*?Delegate>#', $content, $delegate); if (empty($server)) { return false; } # AX can be used only with OpenID 2.0, so checking only SREG $this->sreg = strpos($content, '<Type>http://openid.net/sreg/1.0</Type>') || strpos($content, '<Type>http://openid.net/extensions/sreg/1.1</Type>'); $server = $server[1]; if (isset($delegate[1])) { $this->identity = $delegate[1]; } $this->version = 1; $this->server = $server; return $server; } } $next = true; $yadis = false; $url = $originalUrl; $content = null; break; } if ($next) { continue; } # There are no relevant information in headers, so we search the body. $content = $this->request($url, 'GET', array(), true); if (isset($this->headers['x-xrds-location'])) { $url = $this->build_url(parse_url($url), parse_url(trim($this->headers['x-xrds-location']))); continue; } $location = $this->htmlTag($content, 'meta', 'http-equiv', 'X-XRDS-Location', 'content'); if ($location) { $url = $this->build_url(parse_url($url), parse_url($location)); continue; } } if (!$content) { $content = $this->request($url, 'GET'); } # At this point, the YADIS Discovery has failed, so we'll switch # to openid2 HTML discovery, then fallback to openid 1.1 discovery. $server = $this->htmlTag($content, 'link', 'rel', 'openid2.provider', 'href'); $delegate = $this->htmlTag($content, 'link', 'rel', 'openid2.local_id', 'href'); $this->version = 2; if (!$server) { # The same with openid 1.1 $server = $this->htmlTag($content, 'link', 'rel', 'openid.server', 'href'); $delegate = $this->htmlTag($content, 'link', 'rel', 'openid.delegate', 'href'); $this->version = 1; } if ($server) { # We found an OpenID2 OP Endpoint if ($delegate) { # We have also found an OP-Local ID. $this->identity = $delegate; } $this->server = $server; return $server; } throw new ErrorException("No OpenID Server found at $url", 404); } throw new ErrorException('Endless redirection!', 500); } /** * @param $content_type * * @return bool */ protected function is_allowed_type($content_type) { # Apparently, some providers return XRDS documents as text/html. # While it is against the spec, allowing this here shouldn't break # compatibility with anything. $allowed_types = array('application/xrds+xml', 'text/xml'); # Only allow text/html content type for the Yahoo logins, since # it might cause an endless redirection for the other providers. if ($this->get_provider_name($this->claimed_id) == 'yahoo') { $allowed_types[] = 'text/html'; } foreach ($allowed_types as $type) { if (strpos($content_type, $type) !== false) { return true; } } return false; } /** * @param $provider_url * * @return string */ protected function get_provider_name($provider_url) { $result = ''; if (!empty($provider_url)) { $tokens = array_reverse( explode('.', parse_url($provider_url, PHP_URL_HOST)) ); $result = strtolower( (count($tokens) > 1 && strlen($tokens[1]) > 3) ? $tokens[1] : (count($tokens) > 2 ? $tokens[2] : '') ); } return $result; } /** * @return array */ protected function sregParams() { $params = array(); # We always use SREG 1.1, even if the server is advertising only support for 1.0. # That's because it's fully backwards compatible with 1.0, and some providers # advertise 1.0 even if they accept only 1.1. One such provider is myopenid.com $params['openid.ns.sreg'] = 'http://openid.net/extensions/sreg/1.1'; if ($this->required) { $params['openid.sreg.required'] = array(); foreach ($this->required as $required) { if (!isset(self::$ax_to_sreg[$required])) { continue; } $params['openid.sreg.required'][] = self::$ax_to_sreg[$required]; } $params['openid.sreg.required'] = implode(',', $params['openid.sreg.required']); } if ($this->optional) { $params['openid.sreg.optional'] = array(); foreach ($this->optional as $optional) { if (!isset(self::$ax_to_sreg[$optional])) { continue; } $params['openid.sreg.optional'][] = self::$ax_to_sreg[$optional]; } $params['openid.sreg.optional'] = implode(',', $params['openid.sreg.optional']); } return $params; } /** * @return array */ protected function axParams() { $params = array(); if ($this->required || $this->optional) { $params['openid.ns.ax'] = 'http://openid.net/srv/ax/1.0'; $params['openid.ax.mode'] = 'fetch_request'; $this->aliases = array(); $counts = array(); $required = array(); $optional = array(); foreach (array('required','optional') as $type) { foreach ($this->$type as $alias => $field) { if (is_int($alias)) { $alias = strtr($field, '/', '_'); } $this->aliases[$alias] = 'http://axschema.org/' . $field; if (empty($counts[$alias])) { $counts[$alias] = 0; } $counts[$alias] += 1; ${$type}[] = $alias; } } foreach ($this->aliases as $alias => $ns) { $params['openid.ax.type.' . $alias] = $ns; } foreach ($counts as $alias => $count) { if ($count == 1) { continue; } $params['openid.ax.count.' . $alias] = $count; } # Don't send empty ax.required and ax.if_available. # Google and possibly other providers refuse to support ax when one of these is empty. if ($required) { $params['openid.ax.required'] = implode(',', $required); } if ($optional) { $params['openid.ax.if_available'] = implode(',', $optional); } } return $params; } /** * @param $immediate * * @return string */ protected function authUrl_v1($immediate) { $returnUrl = $this->returnUrl; # If we have an openid.delegate that is different from our claimed id, # we need to somehow preserve the claimed id between requests. # The simplest way is to just send it along with the return_to url. if ($this->identity != $this->claimed_id) { $returnUrl .= (strpos($returnUrl, '?') ? '&' : '?') . 'openid.claimed_id=' . $this->claimed_id; } $params = array( 'openid.return_to' => $returnUrl, 'openid.mode' => $immediate ? 'checkid_immediate' : 'checkid_setup', 'openid.identity' => $this->identity, 'openid.trust_root' => $this->trustRoot, ) + $this->sregParams(); return $this->build_url(parse_url($this->server), array('query' => http_build_query($params, '', '&'))); } /** * @param $immediate * * @return string */ protected function authUrl_v2($immediate) { $params = array( 'openid.ns' => 'http://specs.openid.net/auth/2.0', 'openid.mode' => $immediate ? 'checkid_immediate' : 'checkid_setup', 'openid.return_to' => $this->returnUrl, 'openid.realm' => $this->trustRoot, ); if ($this->ax) { $params += $this->axParams(); } if ($this->sreg) { $params += $this->sregParams(); } if (!$this->ax && !$this->sreg) { # If OP doesn't advertise either SREG, nor AX, let's send them both # in worst case we don't get anything in return. $params += $this->axParams() + $this->sregParams(); } if (!empty($this->oauth) && is_array($this->oauth)) { $params['openid.ns.oauth'] = 'http://specs.openid.net/extensions/oauth/1.0'; $params['openid.oauth.consumer'] = str_replace(array('http://', 'https://'), '', $this->trustRoot); $params['openid.oauth.scope'] = implode(' ', $this->oauth); } if ($this->identifier_select) { $params['openid.identity'] = $params['openid.claimed_id'] = 'http://specs.openid.net/auth/2.0/identifier_select'; } else { $params['openid.identity'] = $this->identity; $params['openid.claimed_id'] = $this->claimed_id; } return $this->build_url(parse_url($this->server), array('query' => http_build_query($params, '', '&'))); } /** * Returns authentication url. Usually, you want to redirect your user to it. * @param bool $immediate * @return String The authentication url. * @throws ErrorException */ public function authUrl($immediate = false) { if ($this->setup_url && !$immediate) { return $this->setup_url; } if (!$this->server) { $this->discover($this->identity); } if ($this->version == 2) { return $this->authUrl_v2($immediate); } return $this->authUrl_v1($immediate); } /** * Performs OpenID verification with the OP. * @return Bool Whether the verification was successful. * @throws ErrorException */ public function validate() { # If the request was using immediate mode, a failure may be reported # by presenting user_setup_url (for 1.1) or reporting # mode 'setup_needed' (for 2.0). Also catching all modes other than # id_res, in order to avoid throwing errors. if (isset($this->data['openid_user_setup_url'])) { $this->setup_url = $this->data['openid_user_setup_url']; return false; } if ($this->mode != 'id_res') { return false; } $this->claimed_id = isset($this->data['openid_claimed_id'])?$this->data['openid_claimed_id']:$this->data['openid_identity']; $params = array( 'openid.assoc_handle' => $this->data['openid_assoc_handle'], 'openid.signed' => $this->data['openid_signed'], 'openid.sig' => $this->data['openid_sig'], ); if (isset($this->data['openid_ns'])) { # We're dealing with an OpenID 2.0 server, so let's set an ns # Even though we should know location of the endpoint, # we still need to verify it by discovery, so $server is not set here $params['openid.ns'] = 'http://specs.openid.net/auth/2.0'; } elseif (isset($this->data['openid_claimed_id']) && $this->data['openid_claimed_id'] != $this->data['openid_identity'] ) { # If it's an OpenID 1 provider, and we've got claimed_id, # we have to append it to the returnUrl, like authUrl_v1 does. $this->returnUrl .= (strpos($this->returnUrl, '?') ? '&' : '?') . 'openid.claimed_id=' . $this->claimed_id; } if ($this->data['openid_return_to'] != $this->returnUrl) { # The return_to url must match the url of current request. # I'm assuming that no one will set the returnUrl to something that doesn't make sense. return false; } $server = $this->discover($this->claimed_id); foreach (explode(',', $this->data['openid_signed']) as $item) { $value = $this->data['openid_' . str_replace('.', '_', $item)]; $params['openid.' . $item] = $value; } $params['openid.mode'] = 'check_authentication'; $response = $this->request($server, 'POST', $params); return preg_match('/is_valid\s*:\s*true/i', $response); } /** * @return array */ protected function getAxAttributes() { $result = array(); if ($alias = $this->getNamespaceAlias('http://openid.net/srv/ax/1.0', 'ax')) { $prefix = 'openid_' . $alias; $length = strlen('http://axschema.org/'); foreach (explode(',', $this->data['openid_signed']) as $key) { $keyMatch = $alias . '.type.'; if (strncmp($key, $keyMatch, strlen($keyMatch)) !== 0) { continue; } $key = substr($key, strlen($keyMatch)); $idv = $prefix . '_value_' . $key; $idc = $prefix . '_count_' . $key; $key = substr($this->getItem($prefix . '_type_' . $key), $length); if (!empty($key)) { if (($count = intval($this->getItem($idc))) > 0) { $value = array(); for ($i = 1; $i <= $count; $i++) { $value[] = $this->getItem($idv . '_' . $i); } $value = ($count == 1) ? reset($value) : $value; } else { $value = $this->getItem($idv); } if (!is_null($value)) { $result[$key] = $value; } } } } else { // No alias for the AX schema has been found, // so there is no AX data in the OP's response. } return $result; } /** * @return array */ protected function getSregAttributes() { $attributes = array(); $sreg_to_ax = array_flip(self::$ax_to_sreg); if ($alias = $this->getNamespaceAlias('http://openid.net/extensions/sreg/1.1', 'sreg')) { foreach (explode(',', $this->data['openid_signed']) as $key) { $keyMatch = $alias . '.'; if (strncmp($key, $keyMatch, strlen($keyMatch)) !== 0) { continue; } $key = substr($key, strlen($keyMatch)); if (!isset($sreg_to_ax[$key])) { # The field name isn't part of the SREG spec, so we ignore it. continue; } $attributes[$sreg_to_ax[$key]] = $this->data['openid_' . $alias . '_' . $key]; } } return $attributes; } /** * Gets AX/SREG attributes provided by OP. should be used only after successful validation. * Note that it does not guarantee that any of the required/optional parameters will be present, * or that there will be no other attributes besides those specified. * In other words. OP may provide whatever information it wants to. * * SREG names will be mapped to AX names. * * * @return array Array of attributes with keys being the AX schema names, e.g. 'contact/email' @see http://www.axschema.org/types/ */ public function getAttributes() { if (isset($this->data['openid_ns']) && $this->data['openid_ns'] == 'http://specs.openid.net/auth/2.0' ) { # OpenID 2.0 # We search for both AX and SREG attributes, with AX taking precedence. return $this->getAxAttributes() + $this->getSregAttributes(); } return $this->getSregAttributes(); } /** * Gets an OAuth request token if the OpenID+OAuth hybrid protocol has been used. * * In order to use the OpenID+OAuth hybrid protocol, you need to add at least one * scope to the $openid->oauth array before you get the call to getAuthUrl(), e.g.: * $openid->oauth[] = 'https://www.googleapis.com/auth/plus.me'; * * Furthermore the registered consumer name must fit the OpenID realm. * To register an OpenID consumer at Google use: https://www.google.com/accounts/ManageDomains * * @return string|bool OAuth request token on success, FALSE if no token was provided. */ public function getOAuthRequestToken() { $alias = $this->getNamespaceAlias('http://specs.openid.net/extensions/oauth/1.0'); return !empty($alias) ? $this->data['openid_' . $alias . '_request_token'] : false; } /** * Gets the alias for the specified namespace, if it's present. * * @param string $namespace The namespace for which an alias is needed. * @param string $hint Common alias of this namespace, used for optimization. * @return string|null The namespace alias if found, otherwise - NULL. */ private function getNamespaceAlias($namespace, $hint = null) { $result = null; if (empty($hint) || $this->getItem('openid_ns_' . $hint) != $namespace) { // The common alias is either undefined or points to // some other extension - search for another alias.. $prefix = 'openid_ns_'; $length = strlen($prefix); foreach ($this->data as $key => $val) { if (strncmp($key, $prefix, $length) === 0 && $val === $namespace) { $result = trim(substr($key, $length)); break; } } } else { $result = $hint; } return $result; } /** * Gets an item from the $data array by the specified id. * * @param string $id The id of the desired item. * @return string|null The item if found, otherwise - NULL. */ private function getItem($id) { return isset($this->data[$id]) ? $this->data[$id] : null; } } Thirdparty/OAuth/OAuthUtil.php 0000644 00000015636 15103446660 0012325 0 ustar 00 <?php /*! * This file is part of the OAuth PHP Library (https://code.google.com/p/oauth/) * * OAuth `PHP' Library is an open source software available under the MIT License. */ namespace Hybridauth\Thirdparty\OAuth; /** * Class OAuthUtil * * @package Hybridauth\Thirdparty\OAuth */ class OAuthUtil { /** * @param $input * * @return array|mixed|string */ public static function urlencode_rfc3986($input) { if (is_array($input)) { return array_map(array( '\Hybridauth\Thirdparty\OAuth\OAuthUtil', 'urlencode_rfc3986' ), $input); } elseif (is_scalar($input)) { return str_replace('+', ' ', str_replace('%7E', '~', rawurlencode($input))); } else { return ''; } } // This decode function isn't taking into consideration the above // modifications to the encoding process. However, this method doesn't // seem to be used anywhere so leaving it as is. /** * @param $string * * @return string */ public static function urldecode_rfc3986($string) { return urldecode($string); } // Utility function for turning the Authorization: header into // parameters, has to do some unescaping // Can filter out any non-oauth parameters if needed (default behaviour) // May 28th, 2010 - method updated to tjerk.meesters for a speed improvement. // see http://code.google.com/p/oauth/issues/detail?id=163 /** * @param $header * @param bool $only_allow_oauth_parameters * * @return array */ public static function split_header($header, $only_allow_oauth_parameters = true) { $params = array(); if (preg_match_all('/(' . ($only_allow_oauth_parameters ? 'oauth_' : '') . '[a-z_-]*)=(:?"([^"]*)"|([^,]*))/', $header, $matches)) { foreach ($matches[1] as $i => $h) { $params[$h] = OAuthUtil::urldecode_rfc3986(empty($matches[3][$i]) ? $matches[4][$i] : $matches[3][$i]); } if (isset($params['realm'])) { unset($params['realm']); } } return $params; } // helper to try to sort out headers for people who aren't running apache /** * @return array */ public static function get_headers() { if (function_exists('apache_request_headers')) { // we need this to get the actual Authorization: header // because apache tends to tell us it doesn't exist $headers = apache_request_headers(); // sanitize the output of apache_request_headers because // we always want the keys to be Cased-Like-This and arh() // returns the headers in the same case as they are in the // request $out = array(); foreach ($headers as $key => $value) { $key = str_replace(" ", "-", ucwords(strtolower(str_replace("-", " ", $key)))); $out[$key] = $value; } } else { // otherwise we don't have apache and are just going to have to hope // that $_SERVER actually contains what we need $out = array(); if (isset($_SERVER['CONTENT_TYPE'])) { $out['Content-Type'] = $_SERVER['CONTENT_TYPE']; } if (isset($_ENV['CONTENT_TYPE'])) { $out['Content-Type'] = $_ENV['CONTENT_TYPE']; } foreach ($_SERVER as $key => $value) { if (substr($key, 0, 5) == "HTTP_") { // this is chaos, basically it is just there to capitalize the first // letter of every word that is not an initial HTTP and strip HTTP // code from przemek $key = str_replace(" ", "-", ucwords(strtolower(str_replace("_", " ", substr($key, 5))))); $out[$key] = $value; } } } return $out; } // This function takes a input like a=b&a=c&d=e and returns the parsed // parameters like this // array('a' => array('b','c'), 'd' => 'e') /** * @param $input * * @return array */ public static function parse_parameters($input) { if (!isset($input) || !$input) { return array(); } $pairs = explode('&', $input); $parsed_parameters = array(); foreach ($pairs as $pair) { $split = explode('=', $pair, 2); $parameter = OAuthUtil::urldecode_rfc3986($split[0]); $value = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : ''; if (isset($parsed_parameters[$parameter])) { // We have already recieved parameter(s) with this name, so add to the list // of parameters with this name if (is_scalar($parsed_parameters[$parameter])) { // This is the first duplicate, so transform scalar (string) into an array // so we can add the duplicates $parsed_parameters[$parameter] = array( $parsed_parameters[$parameter] ); } $parsed_parameters[$parameter][] = $value; } else { $parsed_parameters[$parameter] = $value; } } return $parsed_parameters; } /** * @param $params * * @return string */ public static function build_http_query($params) { if (!$params) { return ''; } // Urlencode both keys and values $keys = OAuthUtil::urlencode_rfc3986(array_keys($params)); $values = OAuthUtil::urlencode_rfc3986(array_values($params)); $params = array_combine($keys, $values); // Parameters are sorted by name, using lexicographical byte value ordering. // Ref: Spec: 9.1.1 (1) uksort($params, 'strcmp'); $pairs = array(); foreach ($params as $parameter => $value) { if (is_array($value)) { // If two or more parameters share the same name, they are sorted by their value // Ref: Spec: 9.1.1 (1) // June 12th, 2010 - changed to sort because of issue 164 by hidetaka sort($value, SORT_STRING); foreach ($value as $duplicate_value) { $pairs[] = $parameter . '=' . $duplicate_value; } } else { $pairs[] = $parameter . '=' . $value; } } // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61) // Each name-value pair is separated by an '&' character (ASCII code 38) return implode('&', $pairs); } } Thirdparty/OAuth/OAuthSignatureMethodHMACSHA1.php 0000644 00000002034 15103446660 0015544 0 ustar 00 <?php /*! * This file is part of the OAuth PHP Library (https://code.google.com/p/oauth/) * * OAuth `PHP' Library is an open source software available under the MIT License. */ namespace Hybridauth\Thirdparty\OAuth; /** * Class OAuthSignatureMethodHMACSHA1 * * @package Hybridauth\Thirdparty\OAuth */ class OAuthSignatureMethodHMACSHA1 extends OAuthSignatureMethod { /** * @return string */ public function get_name() { return "HMAC-SHA1"; } /** * @param $request * @param $consumer * @param $token * * @return string */ public function build_signature($request, $consumer, $token) { $base_string = $request->get_signature_base_string(); $request->base_string = $base_string; $key_parts = array( $consumer->secret, $token ? $token->secret : '' ); $key_parts = OAuthUtil::urlencode_rfc3986($key_parts); $key = implode('&', $key_parts); return base64_encode(hash_hmac('sha1', $base_string, $key, true)); } } Thirdparty/OAuth/OAuthSignatureMethod.php 0000644 00000003361 15103446660 0014502 0 ustar 00 <?php /*! * This file is part of the OAuth PHP Library (https://code.google.com/p/oauth/) * * OAuth `PHP' Library is an open source software available under the MIT License. */ namespace Hybridauth\Thirdparty\OAuth; /** * Class OAuthSignatureMethod * * @package Hybridauth\Thirdparty\OAuth */ abstract class OAuthSignatureMethod { /** * Needs to return the name of the Signature Method (ie HMAC-SHA1) * * @return string */ abstract public function get_name(); /** * Build up the signature * NOTE: The output of this function MUST NOT be urlencoded. * the encoding is handled in OAuthRequest when the final * request is serialized * * @param OAuthRequest $request * @param OAuthConsumer $consumer * @param OAuthToken $token * @return string */ abstract public function build_signature($request, $consumer, $token); /** * Verifies that a given signature is correct * * @param OAuthRequest $request * @param OAuthConsumer $consumer * @param OAuthToken $token * @param string $signature * @return bool */ public function check_signature($request, $consumer, $token, $signature) { $built = $this->build_signature($request, $consumer, $token); // Check for zero length, although unlikely here if (strlen($built) == 0 || strlen($signature) == 0) { return false; } if (strlen($built) != strlen($signature)) { return false; } // Avoid a timing leak with a (hopefully) time insensitive compare $result = 0; for ($i = 0; $i < strlen($signature); $i ++) { $result |= ord($built[$i]) ^ ord($signature[$i]); } return $result == 0; } } Thirdparty/OAuth/OAuthRequest.php 0000644 00000024106 15103446660 0013030 0 ustar 00 <?php /*! * This file is part of the OAuth PHP Library (https://code.google.com/p/oauth/) * * OAuth `PHP' Library is an open source software available under the MIT License. */ namespace Hybridauth\Thirdparty\OAuth; /** * Class OAuthRequest * * @package Hybridauth\Thirdparty\OAuth */ class OAuthRequest { public $parameters; public $http_method; public $http_url; // for debug purposes public $base_string; public static $version = '1.0'; public static $POST_INPUT = 'php://input'; /** * OAuthRequest constructor. * * @param $http_method * @param $http_url * @param null $parameters */ public function __construct($http_method, $http_url, $parameters = null) { $parameters = ($parameters) ? $parameters : array(); $parameters = array_merge(OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters); $this->parameters = $parameters; $this->http_method = $http_method; $this->http_url = $http_url; } /** * attempt to build up a request from what was passed to the server * * @param null $http_method * @param null $http_url * @param null $parameters * * @return OAuthRequest */ public static function from_request($http_method = null, $http_url = null, $parameters = null) { $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on") ? 'http' : 'https'; $http_url = ($http_url) ? $http_url : $scheme . '://' . $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'] . $_SERVER['REQUEST_URI']; $http_method = ($http_method) ? $http_method : $_SERVER['REQUEST_METHOD']; // We weren't handed any parameters, so let's find the ones relevant to // this request. // If you run XML-RPC or similar you should use this to provide your own // parsed parameter-list if (!$parameters) { // Find request headers $request_headers = OAuthUtil::get_headers(); // Parse the query-string to find GET parameters $parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']); // It's a POST request of the proper content-type, so parse POST // parameters and add those overriding any duplicates from GET if ($http_method == "POST" && isset($request_headers['Content-Type']) && strstr($request_headers['Content-Type'], 'application/x-www-form-urlencoded')) { $post_data = OAuthUtil::parse_parameters(file_get_contents(self::$POST_INPUT)); $parameters = array_merge($parameters, $post_data); } // We have a Authorization-header with OAuth data. Parse the header // and add those overriding any duplicates from GET or POST if (isset($request_headers['Authorization']) && substr($request_headers['Authorization'], 0, 6) == 'OAuth ') { $header_parameters = OAuthUtil::split_header($request_headers['Authorization']); $parameters = array_merge($parameters, $header_parameters); } } return new OAuthRequest($http_method, $http_url, $parameters); } /** * pretty much a helper function to set up the request * @param $consumer * @param $token * @param $http_method * @param $http_url * @param null $parameters * @return OAuthRequest */ public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters = null) { $parameters = ($parameters) ? $parameters : array(); $defaults = array( "oauth_version" => OAuthRequest::$version, "oauth_nonce" => OAuthRequest::generate_nonce(), "oauth_timestamp" => OAuthRequest::generate_timestamp(), "oauth_consumer_key" => $consumer->key ); if ($token) { $defaults['oauth_token'] = $token->key; } $parameters = array_merge($defaults, $parameters); return new OAuthRequest($http_method, $http_url, $parameters); } /** * @param $name * @param $value * @param bool $allow_duplicates */ public function set_parameter($name, $value, $allow_duplicates = true) { if ($allow_duplicates && isset($this->parameters[$name])) { // We have already added parameter(s) with this name, so add to the list if (is_scalar($this->parameters[$name])) { // This is the first duplicate, so transform scalar (string) // into an array so we can add the duplicates $this->parameters[$name] = array( $this->parameters[$name] ); } $this->parameters[$name][] = $value; } else { $this->parameters[$name] = $value; } } /** * @param $name * * @return |null */ public function get_parameter($name) { return isset($this->parameters[$name]) ? $this->parameters[$name] : null; } /** * @return array */ public function get_parameters() { return $this->parameters; } /** * @param $name */ public function unset_parameter($name) { unset($this->parameters[$name]); } /** * The request parameters, sorted and concatenated into a normalized string. * * @return string */ public function get_signable_parameters() { $params = []; // Grab all parameters. foreach ($this->parameters as $key_param => $value_param) { // Process only scalar values. if (is_scalar($value_param)) { $params[$key_param] = $value_param; } } // Remove oauth_signature if present // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.") if (isset($params['oauth_signature'])) { unset($params['oauth_signature']); } return OAuthUtil::build_http_query($params); } /** * Returns the base string of this request * * The base string defined as the method, the url * and the parameters (normalized), each urlencoded * and the concated with &. */ public function get_signature_base_string() { $parts = array( $this->get_normalized_http_method(), $this->get_normalized_http_url(), $this->get_signable_parameters() ); $parts = OAuthUtil::urlencode_rfc3986($parts); return implode('&', $parts); } /** * just uppercases the http method */ public function get_normalized_http_method() { return strtoupper($this->http_method); } /** * parses the url and rebuilds it to be * scheme://host/path */ public function get_normalized_http_url() { $parts = parse_url($this->http_url); $scheme = (isset($parts['scheme'])) ? $parts['scheme'] : 'http'; $port = (isset($parts['port'])) ? $parts['port'] : (($scheme == 'https') ? '443' : '80'); $host = (isset($parts['host'])) ? strtolower($parts['host']) : ''; $path = (isset($parts['path'])) ? $parts['path'] : ''; if (($scheme == 'https' && $port != '443') || ($scheme == 'http' && $port != '80')) { $host = "$host:$port"; } return "$scheme://$host$path"; } /** * builds a url usable for a GET request */ public function to_url() { $post_data = $this->to_postdata(); $out = $this->get_normalized_http_url(); if ($post_data) { $out .= '?' . $post_data; } return $out; } /** * builds the data one would send in a POST request */ public function to_postdata() { return OAuthUtil::build_http_query($this->parameters); } /** * builds the Authorization: header * @param null $realm * @return array */ public function to_header($realm = null) { $first = true; if ($realm) { $out = 'OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"'; $first = false; } else { $out = 'OAuth'; } foreach ($this->parameters as $k => $v) { if (substr($k, 0, 5) != "oauth") { continue; } if (is_array($v)) { continue; } $out .= ($first) ? ' ' : ','; $out .= OAuthUtil::urlencode_rfc3986($k) . '="' . OAuthUtil::urlencode_rfc3986($v) . '"'; $first = false; } return array( 'Authorization' => $out ); //- hacked into this to make it return an array. 15/11/2014. } /** * @return string */ public function __toString() { return $this->to_url(); } /** * @param $signature_method * @param $consumer * @param $token */ public function sign_request($signature_method, $consumer, $token) { $this->set_parameter("oauth_signature_method", $signature_method->get_name(), false); $signature = $this->build_signature($signature_method, $consumer, $token); $this->set_parameter("oauth_signature", $signature, false); } /** * @param $signature_method * @param $consumer * @param $token * * @return mixed */ public function build_signature($signature_method, $consumer, $token) { $signature = $signature_method->build_signature($this, $consumer, $token); return $signature; } /** * util function: current timestamp */ private static function generate_timestamp() { return time(); } /** * util function: current nonce */ private static function generate_nonce() { $mt = microtime(); $rand = mt_rand(); return md5($mt . $rand); // md5s look nicer than numbers } } Thirdparty/OAuth/OAuthConsumer.php 0000644 00000001540 15103446660 0013170 0 ustar 00 <?php /*! * This file is part of the OAuth PHP Library (https://code.google.com/p/oauth/) * * OAuth `PHP' Library is an open source software available under the MIT License. */ namespace Hybridauth\Thirdparty\OAuth; /** * Class OAuthConsumer * * @package Hybridauth\Thirdparty\OAuth */ class OAuthConsumer { public $key; public $secret; public $callback_url; /** * OAuthConsumer constructor. * * @param $key * @param $secret * @param null $callback_url */ public function __construct($key, $secret, $callback_url = null) { $this->key = $key; $this->secret = $secret; $this->callback_url = $callback_url; } /** * @return string */ public function __toString() { return "OAuthConsumer[key=$this->key,secret=$this->secret]"; } } Thirdparty/OAuth/README.md 0000644 00000000320 15103446660 0011175 0 ustar 00 This package contains OAuth PHP Library. OAuth PHP Library is an open source software available under the MIT License. https://code.google.com/p/oauth/ http://oauth.googlecode.com/svn/code/php/LICENSE.txt Storage/Transient.php 0000644 00000006072 15103446660 0010642 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Storage; use Hybridauth\Exception\RuntimeException; /** * Hybridauth storage manager */ class Transient implements StorageInterface { /** * Namespace * * @var string */ protected $storeNamespace = 'HYBRIDAUTH::STORAGE'; /** * Key prefix * * @var string */ protected $keyPrefix = ''; /** * We need short term data storage, so we will be using WordPress transient * * @var array */ protected $transient = []; /** * Key used to identify the Transient * * @var string */ protected $key = ''; /** * Initiate a new session * * @throws RuntimeException */ public function __construct() { if(!empty($_COOKIE['lz_social_login'])){ $this->key = sanitize_text_field(wp_unslash($_COOKIE['lz_social_login'])); $transient = get_transient($this->key); if(!empty($transient)){ $this->transient = $transient; } return; } if(headers_sent()){ // phpcs:ignore throw new RuntimeException('HTTP headers already sent to browser and Hybridauth won\'t be able to start/resume LZ session. To resolve this, cookie must be set before outputing any data.'); } $this->key = 'lz_' . bin2hex(random_bytes(12)); if(!setcookie('lz_social_login', $this->key, time() + 90, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true)){ throw new RuntimeException('LZ session failed to start.'); } } /** * {@inheritdoc} */ public function get($key) { if(empty($this->transient)){ $this->transient = get_transient($this->key); if(empty($this->transient) || !is_array($this->transient)){ return null; } } if(isset($this->transient[$key])){ $value = $this->transient[$key]; if(is_array($value) && array_key_exists('lateObject', $value)){ $value = unserialize($value['lateObject']); } return $value; } return null; } /** * {@inheritdoc} */ public function set($key, $value) { if(is_object($value)){ // We encapsulate as our classes may be defined after session is initialized. $value = ['lateObject' => serialize($value)]; } $this->transient[$key] = $value; set_transient($this->key, $this->transient, 60); } /** * {@inheritdoc} */ public function clear() { delete_transient($this->key); setcookie('lz_social_login', '', time() - 3600, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true); } /** * {@inheritdoc} */ public function delete($key) { if(isset($this->transient, $this->transient[$key])){ unset($this->transient[$key]); set_transient($this->key, $this->transient, 60); } } /** * {@inheritdoc} */ public function deleteMatch($key) { if(isset($this->transient) && count($this->transient)){ foreach($this->transient as $k => $v) { if(strstr($k, $key)){ unset($this->transient[$k]); } } set_transient($this->key, $this->transient, 60); } } } Storage/Session.php 0000644 00000005731 15103446660 0010317 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Storage; use Hybridauth\Exception\RuntimeException; /** * Hybridauth storage manager */ class Session implements StorageInterface { /** * Namespace * * @var string */ protected $storeNamespace = 'HYBRIDAUTH::STORAGE'; /** * Key prefix * * @var string */ protected $keyPrefix = ''; /** * Initiate a new session * * @throws RuntimeException */ public function __construct() { if (session_id()) { return; } if (headers_sent()) { // phpcs:ignore throw new RuntimeException('HTTP headers already sent to browser and Hybridauth won\'t be able to start/resume PHP session. To resolve this, session_start() must be called before outputing any data.'); } if (!session_start()) { throw new RuntimeException('PHP session failed to start.'); } } /** * {@inheritdoc} */ public function get($key) { $key = $this->keyPrefix . strtolower($key); if (isset($_SESSION[$this->storeNamespace], $_SESSION[$this->storeNamespace][$key])) { $value = $_SESSION[$this->storeNamespace][$key]; if (is_array($value) && array_key_exists('lateObject', $value)) { $value = unserialize($value['lateObject']); } return $value; } return null; } /** * {@inheritdoc} */ public function set($key, $value) { $key = $this->keyPrefix . strtolower($key); if (is_object($value)) { // We encapsulate as our classes may be defined after session is initialized. $value = ['lateObject' => serialize($value)]; } $_SESSION[$this->storeNamespace][$key] = $value; } /** * {@inheritdoc} */ public function clear() { $_SESSION[$this->storeNamespace] = []; } /** * {@inheritdoc} */ public function delete($key) { $key = $this->keyPrefix . strtolower($key); if (isset($_SESSION[$this->storeNamespace], $_SESSION[$this->storeNamespace][$key])) { $tmp = $_SESSION[$this->storeNamespace]; unset($tmp[$key]); $_SESSION[$this->storeNamespace] = $tmp; } } /** * {@inheritdoc} */ public function deleteMatch($key) { $key = $this->keyPrefix . strtolower($key); if (isset($_SESSION[$this->storeNamespace]) && count($_SESSION[$this->storeNamespace])) { $tmp = $_SESSION[$this->storeNamespace]; foreach ($tmp as $k => $v) { if (strstr($k, $key)) { unset($tmp[$k]); } } $_SESSION[$this->storeNamespace] = $tmp; } } } Storage/StorageInterface.php 0000644 00000001615 15103446660 0012116 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Storage; /** * Hybridauth storage manager interface */ interface StorageInterface { /** * Retrieve a item from storage * * @param string $key * * @return mixed */ public function get($key); /** * Add or Update an item to storage * * @param string $key * @param string $value */ public function set($key, $value); /** * Delete an item from storage * * @param string $key */ public function delete($key); /** * Delete a item from storage * * @param string $key */ public function deleteMatch($key); /** * Clear all items in storage */ public function clear(); } autoload.php 0000644 00000003676 15103446660 0007106 0 ustar 00 <?php /** * Basic autoloader for Hybridauth library which you may use as it is or as a template * to suit your application's environment. * * Note that you'd ONLY need this file if you are not using composer. */ if (version_compare(PHP_VERSION, '5.4.0', '<')) { throw new Exception('Hybridauth 3 requires PHP version 5.4 or higher.'); } /** * Register the autoloader for Hybridauth classes. * * Based off the official PSR-4 autoloader example found at * http://www.php-fig.org/psr/psr-4/examples/ * * @param string $class The fully-qualified class name. * * @return void */ spl_autoload_register( function ($class) { // project-specific namespace prefix. Will only kicks in for Hybridauth's namespace. $prefix = 'Hybridauth\\'; // base directory for the namespace prefix. $base_dir = __DIR__; // By default, it points to this same folder. // You may change this path if having trouble detecting the path to // the source files. // does the class use the namespace prefix? $len = strlen($prefix); if (strncmp($prefix, $class, $len) !== 0) { // no, move to the next registered autoloader. return; } // get the relative class name. $relative_class = substr($class, $len); // replace the namespace prefix with the base directory, replace namespace // separators with directory separators in the relative class name, append // with .php $file = $base_dir . DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, $relative_class) . '.php'; // if the file exists, require it if (file_exists($file)) { require $file; return; } if(!defined('LOGINIZER_PRO_DIR')) { return; } $file = LOGINIZER_PRO_DIR . 'lib/hybridauth/'. str_replace('\\', DIRECTORY_SEPARATOR, $relative_class) . '.php'; if(file_exists($file)){ require $file; } } ); Data/Parser.php 0000644 00000004545 15103446660 0007377 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Data; /** * Parser * * This class is used to parse plain text into objects. It's used by hybriauth adapters to converts * providers api responses to a more 'manageable' format. */ final class Parser { /** * Decodes a string into an object. * * This method will first attempt to parse data as a JSON string (since most providers use this format) * then XML and parse_str. * * @param string $raw * * @return mixed */ public function parse($raw = null) { $data = $this->parseJson($raw); if (!$data) { $data = $this->parseXml($raw); if (!$data) { $data = $this->parseQueryString($raw); } } return $data; } /** * Decodes a JSON string * * @param $result * * @return mixed */ public function parseJson($result) { return json_decode($result); } /** * Decodes a XML string * * @param $result * * @return mixed */ public function parseXml($result) { libxml_use_internal_errors(true); $result = preg_replace('/([<\/])([a-z0-9-]+):/i', '$1', $result); $xml = simplexml_load_string($result); libxml_use_internal_errors(false); if (!$xml) { return []; } $arr = json_decode(json_encode((array)$xml), true); $arr = array($xml->getName() => $arr); return $arr; } /** * Parses a string into variables * * @param $result * * @return \StdClass */ public function parseQueryString($result) { parse_str($result, $output); if (!is_array($output)) { return $result; } $result = new \StdClass(); foreach ($output as $k => $v) { $result->$k = $v; } return $result; } /** * needs to be improved * * @param $birthday * * @return array */ public function parseBirthday($birthday) { $birthday = date_parse((string) $birthday); return [$birthday['year'], $birthday['month'], $birthday['day']]; } } Data/Collection.php 0000644 00000005305 15103446660 0010231 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Data; /** * A very basic Data collection. */ final class Collection { /** * Data collection * * @var mixed */ protected $collection = null; /** * @param mixed $data */ public function __construct($data = null) { $this->collection = (object)$data; } /** * Retrieves the whole collection as array * * @return mixed */ public function toArray() { return (array)$this->collection; } /** * Retrieves an item * * @param $property * * @return mixed */ public function get($property) { if ($this->exists($property)) { return $this->collection->$property; } return null; } /** * Add or update an item * * @param $property * @param mixed $value */ public function set($property, $value) { if ($property) { $this->collection->$property = $value; } } /** * .. until I come with a better name.. * * @param $property * * @return Collection */ public function filter($property) { if ($this->exists($property)) { $data = $this->get($property); if (!is_a($data, 'Collection')) { $data = new Collection($data); } return $data; } return new Collection([]); } /** * Checks whether an item within the collection * * @param $property * * @return bool */ public function exists($property) { return property_exists($this->collection, $property); } /** * Finds whether the collection is empty * * @return bool */ public function isEmpty() { return !(bool)$this->count(); } /** * Count all items in collection * * @return int */ public function count() { return count($this->properties()); } /** * Returns all items properties names * * @return array */ public function properties() { $properties = []; foreach ($this->collection as $key => $value) { $properties[] = $key; } return $properties; } /** * Returns all items values * * @return array */ public function values() { $values = []; foreach ($this->collection as $value) { $values[] = $value; } return $values; } } Provider/OpenID.php 0000644 00000002067 15103446660 0010177 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Provider; use Hybridauth\Adapter; /** * Generic OpenID providers adapter. * * Example: * * $config = [ * 'callback' => Hybridauth\HttpClient\Util::getCurrentUrl(), * * // authenticate with Yahoo openid * 'openid_identifier' => 'https://open.login.yahooapis.com/openid20/www.yahoo.com/xrds' * * // authenticate with stackexchange network openid * // 'openid_identifier' => 'https://openid.stackexchange.com/', * * // authenticate with Steam openid * // 'openid_identifier' => 'http://steamcommunity.com/openid', * * // etc. * ]; * * $adapter = new Hybridauth\Provider\OpenID($config); * * try { * $adapter->authenticate(); * * $userProfile = $adapter->getUserProfile(); * } catch (\Exception $e) { * echo $e->getMessage() ; * } */ class OpenID extends Adapter\OpenID { } Provider/LinkedInOpenID.php 0000644 00000004175 15103446660 0011617 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\Provider; use Hybridauth\Adapter\OAuth2; use Hybridauth\Data; use Hybridauth\Exception\UnexpectedApiResponseException; use Hybridauth\User; /** * LinkedIn OAuth2 provider adapter. */ class LinkedInOpenID extends OAuth2 { /** * {@inheritdoc} */ protected $scope = 'openid profile email'; /** * {@inheritdoc} */ protected $apiBaseUrl = 'https://api.linkedin.com/v2/'; /** * {@inheritdoc} */ protected $authorizeUrl = 'https://www.linkedin.com/oauth/v2/authorization'; /** * {@inheritdoc} */ protected $accessTokenUrl = 'https://www.linkedin.com/oauth/v2/accessToken'; /** * {@inheritdoc} */ protected $apiDocumentation = 'https://docs.microsoft.com/en-us/linkedin/shared/authentication/authentication'; /** * {@inheritdoc} */ protected function initialize() { parent::initialize(); if ($this->isRefreshTokenAvailable()) { $this->tokenRefreshParameters += [ 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret ]; } } /** * {@inheritdoc} */ public function getUserProfile() { $response = $this->apiRequest('/userinfo', 'GET', []); $data = new Data\Collection($response); if (!$data->exists('sub')) { throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); } $userProfile = new User\Profile(); $userProfile->firstName = $data->get('given_name'); $userProfile->lastName = $data->get('family_name'); $userProfile->identifier = $data->get('sub'); $userProfile->email = $data->get('email'); $userProfile->emailVerified = $data->get('email_verified'); $userProfile->displayName = $data->get('name'); $userProfile->photoURL = $data->get('picture'); return $userProfile; } } HttpClient/HttpClientInterface.php 0000644 00000002506 15103446660 0013242 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\HttpClient; /** * Hybridauth Http clients interface */ interface HttpClientInterface { /** * Send request to the remote server * * Returns the result (Raw response from the server) on success, FALSE on failure * * @param string $uri * @param string $method * @param array $parameters * @param array $headers * @param bool $multipart * * @return mixed */ public function request($uri, $method = 'GET', $parameters = [], $headers = [], $multipart = false); /** * Returns raw response from the server on success, FALSE on failure * * @return mixed */ public function getResponseBody(); /** * Retriever the headers returned in the response * * @return array */ public function getResponseHeader(); /** * Returns latest request HTTP status code * * @return int */ public function getResponseHttpCode(); /** * Returns latest error encountered by the client * This can be either a code or error message * * @return mixed */ public function getResponseClientError(); } HttpClient/Curl.php 0000644 00000017120 15103446660 0010246 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\HttpClient; /** * Hybridauth default Http client */ class Curl implements HttpClientInterface { /** * Default curl options * * These defaults options can be overwritten when sending requests. * * See setCurlOptions() * * @var array */ protected $curlOptions = [ CURLOPT_TIMEOUT => 30, CURLOPT_CONNECTTIMEOUT => 30, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 5, CURLINFO_HEADER_OUT => true, CURLOPT_ENCODING => 'identity', // phpcs:ignore CURLOPT_USERAGENT => 'Hybridauth, PHP Social Authentication Library (https://github.com/hybridauth/hybridauth)', ]; /** * Method request() arguments * * This is used for debugging. * * @var array */ protected $requestArguments = []; /** * Default request headers * * @var array */ protected $requestHeader = [ 'Accept' => '*/*', 'Cache-Control' => 'max-age=0', 'Connection' => 'keep-alive', 'Expect' => '', 'Pragma' => '', ]; /** * Raw response returned by server * * @var string */ protected $responseBody = ''; /** * Headers returned in the response * * @var array */ protected $responseHeader = []; /** * Response HTTP status code * * @var int */ protected $responseHttpCode = 0; /** * Last curl error number * * @var mixed */ protected $responseClientError = null; /** * Information about the last transfer * * @var mixed */ protected $responseClientInfo = []; /** * Hybridauth logger instance * * @var object */ protected $logger = null; /** * {@inheritdoc} */ public function request($uri, $method = 'GET', $parameters = [], $headers = [], $multipart = false) { $this->requestHeader = array_replace($this->requestHeader, (array)$headers); $this->requestArguments = [ 'uri' => $uri, 'method' => $method, 'parameters' => $parameters, 'headers' => $this->requestHeader, ]; $curl = curl_init(); switch ($method) { case 'GET': case 'DELETE': unset($this->curlOptions[CURLOPT_POST]); unset($this->curlOptions[CURLOPT_POSTFIELDS]); $uri = $uri . (strpos($uri, '?') ? '&' : '?') . http_build_query($parameters); if ($method === 'DELETE') { $this->curlOptions[CURLOPT_CUSTOMREQUEST] = 'DELETE'; } break; case 'PUT': case 'POST': case 'PATCH': $body_content = $multipart ? $parameters : http_build_query($parameters); if (isset($this->requestHeader['Content-Type']) && $this->requestHeader['Content-Type'] == 'application/json' ) { $body_content = json_encode($parameters); } if ($method === 'POST') { $this->curlOptions[CURLOPT_POST] = true; } else { $this->curlOptions[CURLOPT_CUSTOMREQUEST] = $method; } $this->curlOptions[CURLOPT_POSTFIELDS] = $body_content; break; } $this->curlOptions[CURLOPT_URL] = $uri; $this->curlOptions[CURLOPT_HTTPHEADER] = $this->prepareRequestHeaders(); $this->curlOptions[CURLOPT_HEADERFUNCTION] = [$this, 'fetchResponseHeader']; foreach ($this->curlOptions as $opt => $value) { curl_setopt($curl, $opt, $value); } $response = curl_exec($curl); $this->responseBody = $response; $this->responseHttpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); $this->responseClientError = curl_error($curl); $this->responseClientInfo = curl_getinfo($curl); if ($this->logger) { // phpcs:ignore $this->logger->debug(sprintf('%s::request( %s, %s ), response:', get_class($this), $uri, $method), $this->getResponse()); if (false === $response) { // phpcs:ignore $this->logger->error(sprintf('%s::request( %s, %s ), error:', get_class($this), $uri, $method), [$this->responseClientError]); } } curl_close($curl); return $this->responseBody; } /** * Get response details * * @return array Map structure of details */ public function getResponse() { $curlOptions = $this->curlOptions; $curlOptions[CURLOPT_HEADERFUNCTION] = '*omitted'; return [ 'request' => $this->getRequestArguments(), 'response' => [ 'code' => $this->getResponseHttpCode(), 'headers' => $this->getResponseHeader(), 'body' => $this->getResponseBody(), ], 'client' => [ 'error' => $this->getResponseClientError(), 'info' => $this->getResponseClientInfo(), 'opts' => $curlOptions, ], ]; } /** * Reset curl options * * @param array $curlOptions */ public function setCurlOptions($curlOptions) { foreach ($curlOptions as $opt => $value) { $this->curlOptions[$opt] = $value; } } /** * Set logger instance * * @param object $logger */ public function setLogger($logger) { $this->logger = $logger; } /** * {@inheritdoc} */ public function getResponseBody() { return $this->responseBody; } /** * {@inheritdoc} */ public function getResponseHeader() { return $this->responseHeader; } /** * {@inheritdoc} */ public function getResponseHttpCode() { return $this->responseHttpCode; } /** * {@inheritdoc} */ public function getResponseClientError() { return $this->responseClientError; } /** * @return array */ protected function getResponseClientInfo() { return $this->responseClientInfo; } /** * Returns method request() arguments * * This is used for debugging. * * @return array */ protected function getRequestArguments() { return $this->requestArguments; } /** * Fetch server response headers * * @param mixed $curl * @param string $header * * @return int */ protected function fetchResponseHeader($curl, $header) { $pos = strpos($header, ':'); if (!empty($pos)) { $key = str_replace('-', '_', strtolower(substr($header, 0, $pos))); $value = trim(substr($header, $pos + 2)); $this->responseHeader[$key] = $value; } return strlen($header); } /** * Convert request headers to the expect curl format * * @return array */ protected function prepareRequestHeaders() { $headers = []; foreach ($this->requestHeader as $header => $value) { $headers[] = trim($header) . ': ' . trim($value); } return $headers; } } HttpClient/Guzzle.php 0000644 00000015113 15103446660 0010621 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\HttpClient; use GuzzleHttp\Client; use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Exception\ServerException; use GuzzleHttp\Exception\TransferException; /** * Hybridauth Guzzle Http client * * Note: This is just a proof of concept. Feel free to improve it. * * Example: * * <code> * $guzzle = new Hybridauth\HttpClient\Guzzle(new GuzzleHttp\Client(), [ * 'verify' => '/path/to/your/certificate.crt', * 'headers' => ['User-Agent' => '..'] * // 'proxy' => ... * ]); * * $adapter = new Hybridauth\Provider\Github($config, $guzzle); * * $adapter->authenticate(); * </code> */ class Guzzle implements HttpClientInterface { /** * Method request() arguments * * This is used for debugging. * * @var array */ protected $requestArguments = []; /** * Default request headers * * @var array */ protected $requestHeader = []; /** * Raw response returned by server * * @var string */ protected $responseBody = ''; /** * Headers returned in the response * * @var array */ protected $responseHeader = []; /** * Response HTTP status code * * @var int */ protected $responseHttpCode = 0; /** * Last curl error number * * @var mixed */ protected $responseClientError = null; /** * Information about the last transfer * * @var mixed */ protected $responseClientInfo = []; /** * Hybridauth logger instance * * @var object */ protected $logger = null; /** * GuzzleHttp client * * @var \GuzzleHttp\Client */ protected $client = null; /** * .. * @param null $client * @param array $config */ public function __construct($client = null, $config = []) { $this->client = $client ? $client : new Client($config); } /** * {@inheritdoc} */ public function request($uri, $method = 'GET', $parameters = [], $headers = [], $multipart = false) { $this->requestHeader = array_replace($this->requestHeader, (array)$headers); $this->requestArguments = [ 'uri' => $uri, 'method' => $method, 'parameters' => $parameters, 'headers' => $this->requestHeader, ]; $response = null; try { switch ($method) { case 'GET': case 'DELETE': $response = $this->client->request($method, $uri, [ 'query' => $parameters, 'headers' => $this->requestHeader, ]); break; case 'PUT': case 'PATCH': case 'POST': $body_type = $multipart ? 'multipart' : 'form_params'; if (isset($this->requestHeader['Content-Type']) && $this->requestHeader['Content-Type'] === 'application/json' ) { $body_type = 'json'; } $body_content = $parameters; if ($multipart) { $body_content = []; foreach ($parameters as $key => $val) { if ($val instanceof \CURLFile) { $val = fopen($val->getFilename(), 'r'); } $body_content[] = [ 'name' => $key, 'contents' => $val, ]; } } $response = $this->client->request($method, $uri, [ $body_type => $body_content, 'headers' => $this->requestHeader, ]); break; } } catch (\Exception $e) { $response = $e->getResponse(); $this->responseClientError = $e->getMessage(); } if (!$this->responseClientError) { $this->responseBody = $response->getBody(); $this->responseHttpCode = $response->getStatusCode(); $this->responseHeader = $response->getHeaders(); } if ($this->logger) { // phpcs:ignore $this->logger->debug(sprintf('%s::request( %s, %s ), response:', get_class($this), $uri, $method), $this->getResponse()); if ($this->responseClientError) { // phpcs:ignore $this->logger->error(sprintf('%s::request( %s, %s ), error:', get_class($this), $uri, $method), [$this->responseClientError]); } } return $this->responseBody; } /** * Get response details * * @return array Map structure of details */ public function getResponse() { return [ 'request' => $this->getRequestArguments(), 'response' => [ 'code' => $this->getResponseHttpCode(), 'headers' => $this->getResponseHeader(), 'body' => $this->getResponseBody(), ], 'client' => [ 'error' => $this->getResponseClientError(), 'info' => $this->getResponseClientInfo(), 'opts' => null, ], ]; } /** * Set logger instance * * @param object $logger */ public function setLogger($logger) { $this->logger = $logger; } /** * {@inheritdoc} */ public function getResponseBody() { return $this->responseBody; } /** * {@inheritdoc} */ public function getResponseHeader() { return $this->responseHeader; } /** * {@inheritdoc} */ public function getResponseHttpCode() { return $this->responseHttpCode; } /** * {@inheritdoc} */ public function getResponseClientError() { return $this->responseClientError; } /** * @return array */ protected function getResponseClientInfo() { return $this->responseClientInfo; } /** * Returns method request() arguments * * This is used for debugging. * * @return array */ protected function getRequestArguments() { return $this->requestArguments; } } HttpClient/Util.php 0000644 00000004773 15103446660 0010270 0 ustar 00 <?php /*! * Hybridauth * https://hybridauth.github.io | https://github.com/hybridauth/hybridauth * (c) 2017 Hybridauth authors | https://hybridauth.github.io/license.html */ namespace Hybridauth\HttpClient; use Hybridauth\Data; /** * HttpClient\Util home to a number of utility functions. */ class Util { /** * Redirect handler. * * @var callable|null */ protected static $redirectHandler; /** * Exit handler. * * @var callable|null */ protected static $exitHandler; /** * Redirect to a given URL. * * In case your application need to perform certain required actions before Hybridauth redirect users * to IDPs websites, the default behaviour can be altered in one of two ways: * If callable $redirectHandler is defined, it will be called instead. * If callable $exitHandler is defined, it will be called instead of exit(). * * @param string $url * * @return mixed */ public static function redirect($url) { if (static::$redirectHandler) { return call_user_func(static::$redirectHandler, $url); } header(sprintf('Location: %s', $url)); if (static::$exitHandler) { return call_user_func(static::$exitHandler); } exit(1); } /** * Redirect handler to which the regular redirect() will yield the action of redirecting users. * * @param callable $callback */ public static function setRedirectHandler($callback) { self::$redirectHandler = $callback; } /** * Exit handler will be called instead of regular exit() when calling Util::redirect() method. * * @param callable $callback */ public static function setExitHandler($callback) { self::$exitHandler = $callback; } /** * Returns the Current URL. * * @param bool $requestUri TRUE to use $_SERVER['REQUEST_URI'], FALSE for $_SERVER['PHP_SELF'] * * @return string */ public static function getCurrentUrl($requestUri = false) { $collection = new Data\Collection($_SERVER); $protocol = 'http://'; if (($collection->get('HTTPS') && $collection->get('HTTPS') !== 'off') || $collection->get('HTTP_X_FORWARDED_PROTO') === 'https') { $protocol = 'https://'; } return $protocol . $collection->get('HTTP_HOST') . $collection->get($requestUri ? 'REQUEST_URI' : 'PHP_SELF'); } }
| ver. 1.4 |
Github
|
.
| PHP 8.1.33 | Generation time: 0.02 |
proxy
|
phpinfo
|
Settings