diff --git a/e107_handlers/vendor/hybridauth/hybridauth/src/Provider/Apple.php b/e107_handlers/vendor/hybridauth/hybridauth/src/Provider/Apple.php new file mode 100644 index 000000000..04029914d --- /dev/null +++ b/e107_handlers/vendor/hybridauth/hybridauth/src/Provider/Apple.php @@ -0,0 +1,289 @@ + Hybridauth\HttpClient\Util::getCurrentUrl(), + * 'keys' => ['id' => '', 'team_id' => '', 'key_id' => '', 'key_file' => '', 'key_content' => ''], + * 'scope' => 'name email', + * + * // Apple's custom auth url params + * 'authorize_url_parameters' => [ + * 'response_mode' => 'form_post' + * ] + * ]; + * + * $adapter = new Hybridauth\Provider\Apple($config); + * + * try { + * $adapter->authenticate(); + * + * $userProfile = $adapter->getUserProfile(); + * $tokens = $adapter->getAccessToken(); + * $response = $adapter->setUserStatus("Hybridauth test message.."); + * } catch (\Exception $e) { + * echo $e->getMessage() ; + * } + * + * Requires: + * + * composer require codercat/jwk-to-pem + * composer require firebase/php-jwt + * + * @see https://github.com/sputnik73/hybridauth-sign-in-with-apple + * @see https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api + */ +class Apple extends OAuth2 +{ + /** + * {@inheritdoc} + */ + protected $scope = 'name email'; + + /** + * {@inheritdoc} + */ + protected $apiBaseUrl = 'https://appleid.apple.com/auth/'; + + /** + * {@inheritdoc} + */ + protected $authorizeUrl = 'https://appleid.apple.com/auth/authorize'; + + /** + * {@inheritdoc} + */ + protected $accessTokenUrl = 'https://appleid.apple.com/auth/token'; + + /** + * {@inheritdoc} + */ + protected $apiDocumentation = 'https://developer.apple.com/documentation/sign_in_with_apple'; + + /** + * {@inheritdoc} + * The Sign in with Apple servers require percent encoding (or URL encoding) + * for its query parameters. If you are using the Sign in with Apple REST API, + * you must provide values with encoded spaces (`%20`) instead of plus (`+`) signs. + */ + protected $AuthorizeUrlParametersEncType = PHP_QUERY_RFC3986; + + /** + * {@inheritdoc} + */ + protected function initialize() + { + parent::initialize(); + $this->AuthorizeUrlParameters['response_mode'] = 'form_post'; + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $keys = $this->config->get('keys'); + $keys['secret'] = $this->getSecret(); + $this->config->set('keys', $keys); + return parent::configure(); + } + + /** + * {@inheritdoc} + * + * include id_token $tokenNames + */ + public function getAccessToken() + { + $tokenNames = [ + 'access_token', + 'id_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} + */ + protected function validateAccessTokenExchange($response) + { + $collection = parent::validateAccessTokenExchange($response); + + $this->storeData('id_token', $collection->get('id_token')); + + return $collection; + } + + public function getUserProfile() + { + $id_token = $this->getStoredData('id_token'); + + $verifyTokenSignature = + $this->config->exists('verifyTokenSignature') ? $this->config->get('verifyTokenSignature') : true; + + if (!$verifyTokenSignature) { + // payload extraction by https://github.com/omidborjian + // https://github.com/hybridauth/hybridauth/issues/1095#issuecomment-626479263 + // JWT splits the string to 3 components 1) first is header 2) is payload 3) is signature + $payload = explode('.', $id_token)[1]; + $payload = json_decode(base64_decode($payload)); + } else { + // validate the token signature and get the payload + $publicKeys = $this->apiRequest('keys'); + + \Firebase\JWT\JWT::$leeway = 120; + + $error = false; + $payload = null; + + foreach ($publicKeys->keys as $publicKey) { + try { + $rsa = new RSA(); + $jwk = (array)$publicKey; + + $rsa->loadKey( + [ + 'e' => new BigInteger(base64_decode($jwk['e']), 256), + 'n' => new BigInteger(base64_decode(strtr($jwk['n'], '-_', '+/'), true), 256) + ] + ); + $pem = $rsa->getPublicKey(); + + $payload = JWT::decode($id_token, $pem, ['RS256']); + break; + } catch (\Exception $e) { + $error = $e->getMessage(); + if ($e instanceof \Firebase\JWT\ExpiredException) { + break; + } + } + } + + if ($error && !$payload) { + throw new \Exception($error); + } + } + + $data = new Data\Collection($payload); + + if (!$data->exists('sub')) { + throw new UnexpectedValueException('Missing token payload.'); + } + + $userProfile = new User\Profile(); + $userProfile->identifier = $data->get('sub'); + $userProfile->email = $data->get('email'); + $this->storeData('expires_at', $data->get('exp')); + + if (!empty($_REQUEST['user'])) { + $objUser = json_decode($_REQUEST['user']); + $user = new Data\Collection($objUser); + if (!$user->isEmpty()) { + $name = $user->get('name'); + $userProfile->firstName = $name->firstName; + $userProfile->lastName = $name->lastName; + $userProfile->displayName = join(' ', [$userProfile->firstName, $userProfile->lastName]); + } + } + + return $userProfile; + } + + /** + * @return string secret token + */ + private function getSecret() + { + // Your 10-character Team ID + if (!$team_id = $this->config->filter('keys')->get('team_id')) { + throw new InvalidApplicationCredentialsException( + 'Missing parameter team_id: your team id is required to generate the JWS token.' + ); + } + + // Your Services ID, e.g. com.aaronparecki.services + if (!$client_id = $this->config->filter('keys')->get('id') ?: $this->config->filter('keys')->get('key')) { + throw new InvalidApplicationCredentialsException( + 'Missing parameter id: your client id is required to generate the JWS token.' + ); + } + + // Find the 10-char Key ID value from the portal + if (!$key_id = $this->config->filter('keys')->get('key_id')) { + throw new InvalidApplicationCredentialsException( + 'Missing parameter key_id: your key id is required to generate the JWS token.' + ); + } + + // Find the 10-char Key ID value from the portal + $key_content = $this->config->filter('keys')->get('key_content'); + + // Save your private key from Apple in a file called `key.txt` + if (!$key_content) { + if (!$key_file = $this->config->filter('keys')->get('key_file')) { + throw new InvalidApplicationCredentialsException( + 'Missing parameter key_content or key_file: your key is required to generate the JWS token.' + ); + } + + if (!file_exists($key_file)) { + throw new InvalidApplicationCredentialsException( + "Your key file $key_file does not exist." + ); + } + + $key_content = file_get_contents($key_file); + } + + $data = [ + 'iat' => time(), + 'exp' => time() + 86400 * 180, + 'iss' => $team_id, + 'aud' => 'https://appleid.apple.com', + 'sub' => $client_id + ]; + + $secret = JWT::encode($data, $key_content, 'ES256', $key_id); + + return $secret; + } +} diff --git a/e107_handlers/vendor/hybridauth/hybridauth/src/Provider/AutoDesk.php b/e107_handlers/vendor/hybridauth/hybridauth/src/Provider/AutoDesk.php new file mode 100644 index 000000000..949a56b89 --- /dev/null +++ b/e107_handlers/vendor/hybridauth/hybridauth/src/Provider/AutoDesk.php @@ -0,0 +1,96 @@ +isRefreshTokenAvailable()) { + $this->tokenRefreshParameters += [ + 'client_id' => $this->clientId, + 'client_secret' => $this->clientSecret, + 'grant_type' => 'refresh_token', + ]; + } + } + + /** + * {@inheritdoc} + * + * See: https://forge.autodesk.com/en/docs/oauth/v2/reference/http/users-@me-GET/ + */ + public function getUserProfile() + { + $response = $this->apiRequest('userprofile/v1/users/@me'); + + $collection = new Data\Collection($response); + + $userProfile = new User\Profile(); + + $userProfile->identifier = $collection->get('userId'); + $userProfile->displayName + = $collection->get('firstName') .' '. $collection->get('lastName'); + $userProfile->firstName = $collection->get('firstName'); + $userProfile->lastName = $collection->get('lastName'); + $userProfile->email = $collection->get('emailId'); + $userProfile->language = $collection->get('language'); + $userProfile->webSiteURL = $collection->get('websiteUrl'); + $userProfile->photoURL + = $collection->filter('profileImages')->get('sizeX360'); + + return $userProfile; + } +} diff --git a/e107_handlers/vendor/hybridauth/hybridauth/src/Provider/DeviantArt.php b/e107_handlers/vendor/hybridauth/hybridauth/src/Provider/DeviantArt.php new file mode 100644 index 000000000..407e938da --- /dev/null +++ b/e107_handlers/vendor/hybridauth/hybridauth/src/Provider/DeviantArt.php @@ -0,0 +1,91 @@ +isRefreshTokenAvailable()) { + $this->tokenRefreshParameters += [ + 'client_id' => $this->clientId, + 'client_secret' => $this->clientSecret, + ]; + } + } + + /** + * {@inheritdoc} + * + * See: https://www.deviantart.com/developers/http/v1/20200519/user_whoami/2413749853e66c5812c9beccc0ab3495 + */ + public function getUserProfile() + { + $response = $this->apiRequest('user/whoami'); + + $data = new Data\Collection($response); + + $userProfile = new User\Profile(); + + $full_name = explode(' ', $data->filter('profile')->get('real_name')); + if (count($full_name) < 2) { + $full_name[1] = ''; + } + + $userProfile->identifier = $data->get('userid'); + $userProfile->displayName = $data->get('username'); + $userProfile->profileURL = $data->get('usericon'); + $userProfile->webSiteURL = $data->filter('profile')->get('website'); + $userProfile->firstName = $full_name[0]; + $userProfile->lastName = $full_name[1]; + $userProfile->profileURL = $data->filter('profile')->filter('profile_pic')->get('url'); + $userProfile->gender = $data->filter('details')->get('sex'); + $userProfile->age = $data->filter('details')->get('age'); + $userProfile->country = $data->filter('geo')->get('country'); + + return $userProfile; + } +} diff --git a/e107_handlers/vendor/hybridauth/hybridauth/src/Provider/Dropbox.php b/e107_handlers/vendor/hybridauth/hybridauth/src/Provider/Dropbox.php new file mode 100644 index 000000000..a38336702 --- /dev/null +++ b/e107_handlers/vendor/hybridauth/hybridauth/src/Provider/Dropbox.php @@ -0,0 +1,74 @@ +apiRequest('users/get_current_account', 'POST', [], [], true); + + $data = new Data\Collection($response); + + if (!$data->exists('account_id') || !$data->get('account_id')) { + throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); + } + + $userProfile = new User\Profile(); + + $userProfile->identifier = $data->get('account_id'); + $userProfile->displayName = $data->filter('name')->get('display_name'); + $userProfile->firstName = $data->filter('name')->get('given_name'); + $userProfile->lastName = $data->filter('name')->get('surname'); + $userProfile->email = $data->get('email'); + $userProfile->photoURL = $data->get('profile_photo_url'); + $userProfile->language = $data->get('locale'); + $userProfile->country = $data->get('country'); + if ($data->get('email_verified')) { + $userProfile->emailVerified = $data->get('email'); + } + + return $userProfile; + } +} diff --git a/e107_handlers/vendor/hybridauth/hybridauth/src/Provider/Medium.php b/e107_handlers/vendor/hybridauth/hybridauth/src/Provider/Medium.php new file mode 100644 index 000000000..464709f7d --- /dev/null +++ b/e107_handlers/vendor/hybridauth/hybridauth/src/Provider/Medium.php @@ -0,0 +1,88 @@ +isRefreshTokenAvailable()) { + $this->tokenRefreshParameters += [ + 'client_id' => $this->clientId, + 'client_secret' => $this->clientSecret, + ]; + } + } + + /** + * {@inheritdoc} + * + * See: https://github.com/Medium/medium-api-docs#getting-the-authenticated-users-details + */ + public function getUserProfile() + { + $response = $this->apiRequest('me'); + + $data = new Data\Collection($response); + + $userProfile = new User\Profile(); + $data = $data->filter('data'); + + $full_name = explode(' ', $data->get('name')); + if (count($full_name) < 2) { + $full_name[1] = ''; + } + + $userProfile->identifier = $data->get('id'); + $userProfile->displayName = $data->get('username'); + $userProfile->profileURL = $data->get('imageUrl'); + $userProfile->firstName = $full_name[0]; + $userProfile->lastName = $full_name[1]; + $userProfile->profileURL = $data->get('url'); + + return $userProfile; + } +} diff --git a/e107_handlers/vendor/hybridauth/hybridauth/src/Provider/Pinterest.php b/e107_handlers/vendor/hybridauth/hybridauth/src/Provider/Pinterest.php new file mode 100644 index 000000000..e6e7ee604 --- /dev/null +++ b/e107_handlers/vendor/hybridauth/hybridauth/src/Provider/Pinterest.php @@ -0,0 +1,74 @@ +apiRequest('me'); + + $data = new Data\Collection($response); + + $data = $data->filter('data'); + + if (!$data->exists('id')) { + throw new UnexpectedApiResponseException('Provider API returned an unexpected response.'); + } + + $userProfile = new User\Profile(); + + $userProfile->identifier = $data->get('id'); + $userProfile->description = $data->get('bio'); + $userProfile->photoURL = $data->get('image'); + $userProfile->displayName = $data->get('username'); + $userProfile->firstName = $data->get('first_name'); + $userProfile->lastName = $data->get('last_name'); + $userProfile->profileURL = "https://pinterest.com/{$data->get('username')}"; + + $userProfile->data = (array)$data->get('counts'); + + return $userProfile; + } +} diff --git a/e107_tests/tests/unit/e_user_providerTest.php b/e107_tests/tests/unit/e_user_providerTest.php index c85c10136..c09ad0868 100644 --- a/e107_tests/tests/unit/e_user_providerTest.php +++ b/e107_tests/tests/unit/e_user_providerTest.php @@ -35,7 +35,7 @@ class e_user_providerTest extends \Codeception\Test\Unit $this->assertIsArray($result); $this->assertContains("Facebook", $result); $this->assertContains("Twitter", $result); - $this->assertCount(45, $result, + $this->assertCount(51, $result, "The number of Hybridauth providers has changed! If this is intentional, note the change " . "in Hybridauth providers in the release changelog and update the count in this test." );