PK ‚¹%UV$H! ! ISSUE_TEMPLATE.mdnu €žÙ˜ ## Bug, feature or question?
Is this a bug, a feature request or a question?
Give us a short description.
### Version and provider
What version of Hybrid Auth does this relates to?
Is this an issue with a provider? If yes, which one?
### Reproduction
How can we replicate this issue?
PK ‚¹%Uq‹á» » .travis.ymlnu €žÙ˜ # TravisCI configuration for hybridauth/hybridauth
language: "php"
os:
- "linux"
dist: "bionic"
php:
- "7.4"
- "7.3"
- "7.2"
- "8.0"
jobs:
include:
- name: "PHP 5.6"
dist: "xenial"
php: "5.6"
# "php": ">=5.4.0"
- name: "PHP 5.4"
dist: "trusty"
php: "5.4"
cache:
directories:
- "${HOME}/.composer/cache"
before_script:
- "composer validate --strict"
install:
- "composer install --prefer-dist"
- "composer global require squizlabs/php_codesniffer"
script:
- "vendor/bin/phpunit --verbose"
- "composer global exec -- phpcs -p -s --extensions=php --standard=PSR2 --ignore='./src/Thirdparty/*' ./src"
notifications:
email: false
PK ‚¹%U¾Çšû
COPYING.mdnu €žÙ˜ Except where otherwise noted in the source code (i.e., LightOpenID and OAuth
Library, which are covered by similar licences but with different Copyright
notices) all the files are:
Copyright (C) 2009-2019, Hybridauth authors. All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------
Hybridauth includes a copy of LightOpenID, licensed as follows:
Copyright (c) 2013-2016 Mewp (mewp151 at gmail dot com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------
Hybridauth includes a modified copy of OAuth PHP Library, licensed as follows:
Copyright (c) 2007-2011 Andy Smith
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
PK ‚¹%U–š›þ‘ ‘ CODE_OF_CONDUCT.mdnu €žÙ˜ # Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hybridauth@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
PK ‚¹%UkMŒ%œ œ README.mdnu €žÙ˜ [![GitHub stars](https://img.shields.io/github/stars/hybridauth/hybridauth.svg)](https://github.com/hybridauth/hybridauth/stargazers)
[![GitHub issues](https://img.shields.io/github/issues/hybridauth/hybridauth.svg)](https://github.com/hybridauth/hybridauth/issues)
[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://supportukrainenow.org/)
## [Hybridauth](https://hybridauth.github.io/) 3.8
[![Build Status](https://travis-ci.org/hybridauth/hybridauth.svg?branch=master)](https://travis-ci.org/hybridauth/hybridauth) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/hybridauth/hybridauth/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/hybridauth/hybridauth/?branch=master) [![Latest Stable Version](https://poser.pugx.org/hybridauth/hybridauth/v/stable.png)](https://packagist.org/packages/hybridauth/hybridauth) [![Join the chat at https://gitter.im/hybridauth/hybridauth](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/hybridauth/hybridauth?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Hybridauth enables developers to easily build social applications and tools to engage websites visitors and customers on a social level that starts off with social sign-in and extends to social sharing, users profiles, friends lists, activities streams, status updates and more.
The main goal of Hybridauth is to act as an abstract API between your application and the various social networks APIs and identities providers such as Facebook, Twitter and Google.
#### Usage
Hybridauth provides a number of basic [examples](https://github.com/hybridauth/hybridauth/tree/master/examples). You can also find complete Hybridauth documentation at https://hybridauth.github.io
```php
$config = [
'callback' => 'https://example.com/path/to/script.php',
'keys' => [
'key' => 'your-twitter-consumer-key',
'secret' => 'your-twitter-consumer-secret',
],
];
try {
$twitter = new Hybridauth\Provider\Twitter($config);
$twitter->authenticate();
$accessToken = $twitter->getAccessToken();
$userProfile = $twitter->getUserProfile();
$apiResponse = $twitter->apiRequest('statuses/home_timeline.json');
}
catch (\Exception $e) {
echo 'Oops, we ran into an issue! ' . $e->getMessage();
}
```
#### Requirements
* PHP 5.4+
* PHP Session
* PHP cURL
#### Installation
To install Hybridauth we recommend [Composer](https://getcomposer.org/), the now defacto dependency manager for PHP. Alternatively, you can download and use the latest release available at [Github](https://github.com/hybridauth/hybridauth/releases).
#### Versions Status
| Version | Status | Repository | Documentation | PHP Version |
|---------|-------------|-------------------------|-------------------------|-------------|
| 2.x | Maintenance | [v2][hybridauth-2-repo] | [v2][hybridauth-2-docs] | >= 5.3 |
| 3.x | Development | [v3][hybridauth-3-repo] | [v3][hybridauth-3-docs] | >= 5.4 |
| 4.x | Future | -- | -- | >= 7.3 |
[hybridauth-2-repo]: https://github.com/hybridauth/hybridauth/tree/v2
[hybridauth-3-repo]: https://github.com/hybridauth/hybridauth/
[hybridauth-2-docs]: https://hybridauth.github.io/hybridauth/
[hybridauth-3-docs]: https://hybridauth.github.io/
#### Questions, Help and Support?
For general questions (i.e, "how-to" questions), please consider using [StackOverflow](https://stackoverflow.com/questions/tagged/hybridauth) instead of the Github issues tracker. For convenience, we also have a [low-activity] [Gitter channel](https://gitter.im/hybridauth/hybridauth) if you want to get help directly from the community.
#### License
Hybridauth PHP Library is released under the terms of MIT License.
For the full Copyright Notice and Disclaimer, see [COPYING.md](https://github.com/hybridauth/hybridauth/blob/master/COPYING.md).
PK ‚¹%U?Å&_y y CONTRIBUTING.mdnu €žÙ˜ Contributing
============
Hybridauth is a community driven project, and it needs your help to keep the project going.
### Report Problems
A great way to help is to find and submit [bug reports](https://github.com/hybridauth/hybridauth/issues) or to fix the
existing ones.
When reporting new issues, please provide as much detail and context as possible, otherwise we will be left in the dark
about the problem you having.
### Documentation
You can help improve Hybridauth user documentation (this website) by making it more consistent and readable, and by adding
missing information, correcting errors and typos.
Hybridauth documentation is maintained as plain text markdown files at https://github.com/hybridauth/hybridauth.github.io
### Develop
If you have fixed a bug or implemented a new feature that you would like to share with the community, send a pull request
against the [dev branch](https://github.com/hybridauth/hybridauth/).
Before contributing code, please consider these guide lines:
**Coding Style**
Hybridauth follows [PSR-1](http://www.php-fig.org/psr/psr-1/) and [PSR-2](http://www.php-fig.org/psr/psr-2/).
Please prevent your IDE for reformatting huge chunks of code/files as it make it nearly impossible to see what changes were
actually made to a file on your Pull Request.
**Compatibility**
Additional providers, minor enhancements, bugs and typos fixes are most welcome. Large and "breaking" changes should be
discussed ahead of time. **Please ask first**.
Hybridauth 3 is compatible with **PHP 5.4** and therefore all code supplied must stick to this requirement.
**License**
Hybridauth PHP Library is released under the terms of MIT License: By contributing your code to the project, you agree to
license your contribution under the MIT License. (which means, once you donate your code to the community, it become freely
available to everyone to use, or mis-use).
PK ‚¹%U$'o÷% %
.gitignorenu €žÙ˜ .*
*.xml
*.log
*.json
*.lock
/vendor
PK ‚¹%Uº€‚=ú ú examples/example_04.phpnu €žÙ˜ Hybridauth\HttpClient\Util::getCurrentUrl(),
'openid_identifier' => 'https://open.login.yahooapis.com/openid20/www.yahoo.com/xrds',
// 'openid_identifier' => 'https://openid.stackexchange.com/',
// 'openid_identifier' => 'http://steamcommunity.com/openid',
// etc.
];
try {
$adapter = new Hybridauth\Provider\OpenID($config);
$adapter->authenticate();
$tokens = $adapter->getAccessToken();
$userProfile = $adapter->getUserProfile();
// print_r($tokens);
// print_r($userProfile);
$adapter->disconnect();
} catch (Exception $e) {
echo $e->getMessage();
}
PK ‚¹%UÏ.?' ' examples/README.mdnu €žÙ˜ Hybridauth 3 Examples
======================
File | Description
-------------- | ------------------------------------------------------------------------------
example_01.php | This simple example illustrate how to authenticate users with GitHub. If you're new to Hybridauth, this file is the one you'll likely want to check.
example_02.php | Details how to use users in a similar fashion to Hybridauth 2. Note that while Hybridauth 3 provides a similar interface to Hybridauth 2, both versions are not fully compatible with each other.
example_03.php | An example on how use Access Tokens to access providers APIs, and how to setup custom API endpoints.
example_04.php | A simple example that shows how to connect users to providers using OpenID.
example_05.php | A simple example that shows how to use Guzzle as a Http Client for Hybridauth instead of PHP Curl extension.
example_06/ | A simple example that shows how to organize multiple providers.
example_07/ | A simple example that shows how to organize multiple providers, using a pop-up.
PK ‚¹%UǶ}ø©
©
examples/example_07/callback.phpnu €žÙ˜ getProviders())) {
// Store the provider for the callback event
$storage->set('provider', $_GET['provider']);
} else {
$error = $_GET['provider'];
}
}
//
// Event 2: User clicked LOGOUT link
//
if (isset($_GET['logout'])) {
if (in_array($_GET['logout'], $hybridauth->getProviders())) {
// Disconnect the adapter
$adapter = $hybridauth->getAdapter($_GET['logout']);
$adapter->disconnect();
} else {
$error = $_GET['logout'];
}
}
//
// Handle invalid provider errors
//
if ($error) {
error_log('Hybridauth Error: Provider ' . json_encode($error) . ' not found or not enabled in $config');
// Close the pop-up window
echo "
";
exit;
}
//
// Event 3: Provider returns via CALLBACK
//
if ($provider = $storage->get('provider')) {
$hybridauth->authenticate($provider);
$storage->set('provider', null);
// Retrieve the provider record
$adapter = $hybridauth->getAdapter($provider);
$userProfile = $adapter->getUserProfile();
$accessToken = $adapter->getAccessToken();
// add your custom AUTH functions (if any) here
// ...
$data = [
'token' => $accessToken,
'identifier' => $userProfile->identifier,
'email' => $userProfile->email,
'first_name' => $userProfile->firstName,
'last_name' => $userProfile->lastName,
'photoURL' => strtok($userProfile->photoURL, '?'),
];
// ...
// Close pop-up window
echo "
";
}
} catch (Exception $e) {
error_log($e->getMessage());
echo $e->getMessage();
}
PK ‚¹%U"ì· · examples/example_07/index.phpnu €žÙ˜ getConnectedAdapters();
?>
Example 07
Sign in
You are logged in:
$adapter) : ?>
getUserProfile()->displayName; ?> from
(">Log Out )
PK ‚¹%Uä‘€ÔÎ Î examples/example_07/config.phpnu €žÙ˜ 'https://path/to/hybridauth/examples/example_07/callback.php',
'providers' => [
'Google' => [
'enabled' => true,
'keys' => [
'id' => '...',
'secret' => '...',
],
'scope' => 'email',
],
// 'Yahoo' => ['enabled' => true, 'keys' => ['key' => '...', 'secret' => '...']],
// 'Facebook' => ['enabled' => true, 'keys' => ['id' => '...', 'secret' => '...']],
// 'Twitter' => ['enabled' => true, 'keys' => ['key' => '...', 'secret' => '...']],
// 'Instagram' => ['enabled' => true, 'keys' => ['id' => '...', 'secret' => '...']],
],
];
PK ‚¹%Uñ:”hk k examples/example_06/callback.phpnu €žÙ˜ set('provider', $_GET['provider']);
}
/**
* When provider exists in the storage, try to authenticate user and clear storage.
*
* When invoked, `authenticate()` will redirect users to provider login page where they
* will be asked to grant access to your application. If they do, provider will redirect
* the users back to Authorization callback URL (i.e., this script).
*/
if ($provider = $storage->get('provider')) {
$hybridauth->authenticate($provider);
$storage->set('provider', null);
}
/**
* This will erase the current user authentication data from session, and any further
* attempt to communicate with provider.
*/
if (isset($_GET['logout'])) {
$adapter = $hybridauth->getAdapter($_GET['logout']);
$adapter->disconnect();
}
/**
* Redirects user to home page (i.e., index.php in our case)
*/
HttpClient\Util::redirect('https://path/to/hybridauth/examples/example_06');
} catch (Exception $e) {
echo $e->getMessage();
}
PK ‚¹%UÄeD£Ö Ö examples/example_06/index.phpnu €žÙ˜ getConnectedAdapters();
?>
Example 06
Sign in
You are logged in:
$adapter) : ?>
getUserProfile()->displayName; ?> from
(">Log Out )
PK ‚¹%U9Sà« « examples/example_06/config.phpnu €žÙ˜ 'https://path/to/hybridauth/examples/example_06/callback.php',
'providers' => [
'Twitter' => [
'enabled' => true,
'keys' => [
'key' => '...',
'secret' => '...',
],
],
'LinkedIn' => [
'enabled' => true,
'keys' => [
'id' => '...',
'secret' => '...',
],
],
'Facebook' => [
'enabled' => true,
'keys' => [
'id' => '...',
'secret' => '...',
],
],
],
];
PK ‚¹%U…=ÛR examples/index.htmlnu €žÙ˜ 403.PK ‚¹%Uß3Û² examples/example_02.phpnu €žÙ˜ HttpClient\Util::getCurrentUrl(),
'providers' => [
'GitHub' => [
'enabled' => true,
'keys' => ['id' => '', 'secret' => ''],
],
'Google' => [
'enabled' => true,
'keys' => ['id' => '', 'secret' => ''],
],
'Facebook' => [
'enabled' => true,
'keys' => ['id' => '', 'secret' => ''],
],
'Twitter' => [
'enabled' => true,
'keys' => ['key' => '', 'secret' => ''],
]
],
/* optional : set debug mode
'debug_mode' => true,
// Path to file writeable by the web server. Required if 'debug_mode' is not false
'debug_file' => __FILE__ . '.log', */
/* optional : customize Curl settings
// for more information on curl, refer to: http://www.php.net/manual/fr/function.curl-setopt.php
'curl_options' => [
// setting custom certificates
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_CAINFO => '/path/to/your/certificate.crt',
// set a valid proxy ip address
CURLOPT_PROXY => '*.*.*.*:*',
// set a custom user agent
CURLOPT_USERAGENT => ''
] */
];
try {
$hybridauth = new Hybridauth($config);
$adapter = $hybridauth->authenticate('GitHub');
// $adapter = $hybridauth->authenticate('Google');
// $adapter = $hybridauth->authenticate('Facebook');
// $adapter = $hybridauth->authenticate('Twitter');
$tokens = $adapter->getAccessToken();
$userProfile = $adapter->getUserProfile();
// print_r($tokens);
// print_r($userProfile);
$adapter->disconnect();
} catch (\Exception $e) {
echo $e->getMessage();
}
PK ‚¹%UŠ¨7pi i examples/example_01.phpnu €žÙ˜ Hybridauth\HttpClient\Util::getCurrentUrl()
*
* After configuring your GitHub application, simple replace 'your-app-id' and 'your-app-secret'
* with your application credentials (Client ID and Client Secret).
*
* Providers who uses OAuth 2.0 protocol (i.g., GitHub, Facebook, Google, etc.) may need
* an Authorization scope as additional parameter. Authorization scopes are strings that
* enable access to particular resources, such as user data.
*
* https://developer.github.com/v3/oauth/
* https://developer.github.com/v3/oauth/#scopes
*/
$config = [
'callback' => 'https://path/to/hybridauth/examples/example_01.php', // or Hybridauth\HttpClient\Util::getCurrentUrl()
'keys' => ['id' => 'your-app-id', 'secret' => 'your-app-secret'], // Your Github application credentials
/* optional : set scope
'scope' => 'user:email', */
/* optional : set debug mode
'debug_mode' => true,
// Path to file writeable by the web server. Required if 'debug_mode' is not false
'debug_file' => __FILE__ . '.log', */
/* optional : customize Curl settings
// for more information on curl, refer to: http://www.php.net/manual/fr/function.curl-setopt.php
'curl_options' => [
// setting custom certificates
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_CAINFO => '/path/to/your/certificate.crt',
// set a valid proxy ip address
CURLOPT_PROXY => '*.*.*.*:*',
// set a custom user agent
CURLOPT_USERAGENT => ''
] */
];
/**
* Step 3: Instantiate Github Adapter
*
* This example instantiates a GitHub adapter using the array $config we just built.
*/
$github = new Hybridauth\Provider\GitHub($config);
/**
* Step 4: Authenticating Users
*
* When invoked, `authenticate()` will redirect users to GitHub login page where they
* will be asked to grant access to your application. If they do, GitHub will redirect
* the users back to Authorization callback URL (i.e., this script).
*
* Note that GitHub and few other providers will ask their users for authorisation
* only once.
*/
$github->authenticate();
/**
* Step 5: Retrieve Users Profiles
*
* Calling getUserProfile returns an instance of class Hybridauth\User\Profile which contain the
* connected user's profile in simple and standardized structure across all the social APIs supported
* by Hybridauth.
*/
$userProfile = $github->getUserProfile();
echo 'Hi ' . $userProfile->displayName;
/**
* Bonus: Access GitHub API
*
* Now that the user is authenticated with Gihub, and depending on the authorization given to your
* application, you should be able to query the said API on behalf of the user.
*
* As an example we list the authenticated user's public gists.
*/
$apiResponse = $github->apiRequest('gists');
/**
* Step 6: Disconnect the adapter
*
* This will erase the current user authentication data from session, and any further
* attempt to communicate with Github API will result on an authorisation exception.
*/
$github->disconnect();
/**
* Final note: Catching Exceptions
*
* Hybridauth use exceptions extensively and it's important that these exceptions
* be properly caught/handled in your code.
*
* Below is a basic example of how to catch exceptions.
*
* Note that on the previous step we disconnected from the API; meaning Hybridauth
* has erased the oauth access token used to sign http requests from the current
* session, thus, any new request we now make will now throw an exception.
*
* It's important that you don't show Hybridauth exception's messages to the end user as
* they may include sensitive data, and that you use your own error messages instead.
*/
try {
$github->getUserProfile();
}
/**
* Catch Curl Errors
*
* This kind of error may happen in case of:
* - Internet or Network issues.
* - Your server configuration is not setup correctly.
*
* The full list of curl errors that may happen can be found at http://curl.haxx.se/libcurl/c/libcurl-errors.html
*/
catch (Hybridauth\Exception\HttpClientFailureException $e) {
echo 'Curl text error message : ' . $github->getHttpClient()->getResponseClientError();
}
/**
* Catch API Requests Errors
*
* This usually happens when requesting a:
* - Wrong URI or a mal-formatted http request.
* - Protected resource without providing a valid access token.
*/
catch (Hybridauth\Exception\HttpRequestFailedException $e) {
echo 'Raw API Response: ' . $github->getHttpClient()->getResponseBody();
}
/**
* Base PHP's exception that catches everything [else]
*/
catch (\Exception $e) {
echo 'Oops! We ran into an unknown issue: ' . $e->getMessage();
}
PK ‚¹%U3ïÔ¼ì ì examples/example_05.phpnu €žÙ˜ Hybridauth\HttpClient\Util::getCurrentUrl(),
'keys' => ['id' => '', 'secret' => ''],
];
$guzzle = new Hybridauth\HttpClient\Guzzle(null, [
// 'verify' => true, # Set to false to disable SSL certificate verification
]);
try {
$adapter = new Hybridauth\Provider\Github($config, $guzzle);
$adapter->authenticate();
$tokens = $adapter->getAccessToken();
$userProfile = $adapter->getUserProfile();
// print_r($tokens);
// print_r($userProfile);
$adapter->disconnect();
} catch (Exception $e) {
echo $e->getMessage();
}
PK ‚¹%U ®5] ] examples/example_03.phpnu €žÙ˜ Hybridauth\HttpClient\Util::getCurrentUrl(),
'keys' => ['id' => 'your-facebook-app-id', 'secret' => 'your-facebook-app-secret'],
'endpoints' => [
'api_base_url' => 'https://graph.facebook.com/v2.8/',
'authorize_url' => 'https://www.facebook.com/dialog/oauth',
'access_token_url' => 'https://graph.facebook.com/oauth/access_token',
]
];
try {
$adapter = new Hybridauth\Provider\Facebook($config);
$adapter->setAccessToken(['access_token' => 'user-facebook-access-token']);
$userProfile = $adapter->getUserProfile();
// print_r($userProfile);
$adapter->disconnect();
} catch (Exception $e) {
echo $e->getMessage();
}
PK ‚¹%U¡Jr^ ^ phpunit.xmlnu €žÙ˜
./tests/
PK ‚¹%URÊ$² ² PULL_REQUEST_TEMPLATE.mdnu €žÙ˜ | Q            | A
| ------------------------ | ---
| Fixed Issues? | `Fixes #1, Fixes #2`
| Patch: Bug Fix? |
| Major: Breaking Change? |
| Minor: New Feature? |
PK ‚¹%UÊlðB B . src/Exception/AuthorizationDeniedException.phpnu €žÙ˜ getCode();
$message = $this->getMessage();
$file = $this->getFile();
$line = $this->getLine();
$trace = $this->getTraceAsString();
$html = sprintf('%s ', $title);
$html .= 'Hybridauth has encountered the following error:
';
$html .= 'Details ';
$html .= sprintf('Exception: %s
', get_class($this));
$html .= sprintf('Message: %s
', $message);
$html .= sprintf('File: %s
', $file);
$html .= sprintf('Line: %s
', $line);
$html .= sprintf('Code: %s
', $code);
$html .= 'Trace ';
$html .= sprintf('%s ', $trace);
if ($object) {
$html .= 'Debug ';
$obj_dump = print_r($object, true);
// phpcs:ignore
$html .= sprintf('' . get_class($object) . ' extends ' . get_parent_class($object) . ' %s ', $obj_dump);
}
$html .= 'Session ';
$session_dump = print_r($_SESSION, true);
$html .= sprintf('%s ', $session_dump);
// phpcs:ignore
echo sprintf("%s %s", $title, $html);
}
}
PK ‚¹%UØÐΘL L 8 src/Exception/InvalidApplicationCredentialsException.phpnu €žÙ˜ 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'));
}
$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
&& $this->getStoredData('authorization_state') != $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;
}
}
PK ‚¹%U×q¦- src/Adapter/DataStoreTrait.phpnu €žÙ˜ 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 . '.');
}
}
PK ‚¹%U‚L®ëò ò src/Adapter/OpenID.phpnu €žÙ˜ 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;
}
}
PK ‚¹%U Ç'¡QH QH src/Adapter/OAuth1.phpnu €žÙ˜ 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;
}
}
PK ‚¹%U¼n0/P
P
src/Adapter/AdapterInterface.phpnu €žÙ˜ 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() . '.'
);
}
}
}
PK ‚¹%U4¾Z½L L src/Provider/Pinterest.phpnu €žÙ˜ 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;
}
}
PK ‚¹%Uñåi¢ ¢ src/Provider/Disqus.phpnu €žÙ˜ apiRequestParameters = [
'api_key' => $this->clientId, 'api_secret' => $this->clientSecret
];
}
/**
* {@inheritdoc}
*/
public function getUserProfile()
{
$response = $this->apiRequest('users/details');
$data = new Data\Collection($response);
if (!$data->filter('response')->exists('id')) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
$userProfile = new User\Profile();
$data = $data->filter('response');
$userProfile->identifier = $data->get('id');
$userProfile->displayName = $data->get('name');
$userProfile->description = $data->get('bio');
$userProfile->profileURL = $data->get('profileUrl');
$userProfile->email = $data->get('email');
$userProfile->region = $data->get('location');
$userProfile->description = $data->get('about');
$userProfile->photoURL = $data->filter('avatar')->get('permalink');
$userProfile->displayName = $userProfile->displayName ?: $data->get('username');
return $userProfile;
}
}
PK ‚¹%Ußbj'| | src/Provider/Spotify.phpnu €žÙ˜ 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->email = $data->get('email');
$userProfile->emailVerified = $data->get('email');
$userProfile->profileURL = $data->filter('external_urls')->get('spotify');
$userProfile->photoURL = $data->filter('images')->get('url');
$userProfile->country = $data->get('country');
if ($data->exists('birthdate')) {
$this->fetchBirthday($userProfile, $data->get('birthdate'));
}
return $userProfile;
}
/**
* Fetch use birthday
*
* @param User\Profile $userProfile
* @param $birthday
*
* @return User\Profile
*/
protected function fetchBirthday(User\Profile $userProfile, $birthday)
{
$result = (new Data\Parser())->parseBirthday($birthday);
$userProfile->birthDay = (int)$result[0];
$userProfile->birthMonth = (int)$result[1];
$userProfile->birthYear = (int)$result[2];
return $userProfile;
}
}
PK ‚¹%Uˆ7‹ÀX X src/Provider/LinkedIn.phpnu €žÙ˜ apiRequest('me', 'GET', ['projection' => '(' . implode(',', $fields) . ')']);
$data = new Data\Collection($response);
if (!$data->exists('id')) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
$userProfile = new User\Profile();
// Handle localized names.
$userProfile->firstName = $data
->filter('firstName')
->filter('localized')
->get($this->getPreferredLocale($data, 'firstName'));
$userProfile->lastName = $data
->filter('lastName')
->filter('localized')
->get($this->getPreferredLocale($data, 'lastName'));
$userProfile->identifier = $data->get('id');
$userProfile->email = $this->getUserEmail();
$userProfile->emailVerified = $userProfile->email;
$userProfile->displayName = trim($userProfile->firstName . ' ' . $userProfile->lastName);
$photo_elements = $data
->filter('profilePicture')
->filter('displayImage~')
->get('elements');
$userProfile->photoURL = $this->getUserPhotoUrl($photo_elements);
return $userProfile;
}
/**
* Returns a user photo.
*
* @param array $elements
* List of file identifiers related to this artifact.
*
* @return string
* The user photo URL.
*
* @see https://docs.microsoft.com/en-us/linkedin/shared/references/v2/profile/profile-picture
*/
public function getUserPhotoUrl($elements)
{
if (is_array($elements)) {
// Get the largest picture from the list which is the last one.
$element = end($elements);
if (!empty($element->identifiers)) {
return reset($element->identifiers)->identifier;
}
}
return null;
}
/**
* Returns an email address of user.
*
* @return string
* The user email address.
*
* @throws \Exception
*/
public function getUserEmail()
{
$response = $this->apiRequest('emailAddress', 'GET', [
'q' => 'members',
'projection' => '(elements*(handle~))',
]);
$data = new Data\Collection($response);
foreach ($data->filter('elements')->toArray() as $element) {
$item = new Data\Collection($element);
if ($email = $item->filter('handle~')->get('emailAddress')) {
return $email;
}
}
return null;
}
/**
* {@inheritdoc}
*
* @see https://docs.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/share-on-linkedin
* @throws \Exception
*/
public function setUserStatus($status, $userID = null)
{
if (strpos($this->scope, 'w_member_social') === false) {
throw new \Exception('Set user status requires w_member_social permission!');
}
if (is_string($status)) {
$status = [
'author' => 'urn:li:person:' . $userID,
'lifecycleState' => 'PUBLISHED',
'specificContent' => [
'com.linkedin.ugc.ShareContent' => [
'shareCommentary' => [
'text' => $status,
],
'shareMediaCategory' => 'NONE',
],
],
'visibility' => [
'com.linkedin.ugc.MemberNetworkVisibility' => 'PUBLIC',
],
];
}
$headers = [
'Content-Type' => 'application/json',
'x-li-format' => 'json',
'X-Restli-Protocol-Version' => '2.0.0',
];
$response = $this->apiRequest("ugcPosts", 'POST', $status, $headers);
return $response;
}
/**
* Returns a preferred locale for given field.
*
* @param \Hybridauth\Data\Collection $data
* A data to check.
* @param string $field_name
* A field name to perform.
*
* @return string
* A field locale.
*/
protected function getPreferredLocale($data, $field_name)
{
$locale = $data->filter($field_name)->filter('preferredLocale');
if ($locale) {
return $locale->get('language') . '_' . $locale->get('country');
}
return 'en_US';
}
}
PK ‚¹%U(ø‡§ § src/Provider/Google.phpnu €žÙ˜ 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;
}
}
PK ‚¹%UÆ0Èà src/Provider/Patreon.phpnu €žÙ˜ isRefreshTokenAvailable()) {
$this->tokenRefreshParameters += [
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret,
];
}
}
/**
* {@inheritdoc}
*/
public function getUserProfile()
{
$response = $this->apiRequest('oauth2/v2/identity', 'GET', [
'fields[user]' => 'created,first_name,last_name,email,full_name,is_email_verified,thumb_url,url',
]);
$collection = new Collection($response);
if (!$collection->exists('data')) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
$userProfile = new Profile();
$data = $collection->filter('data');
$attributes = $data->filter('attributes');
$userProfile->identifier = $data->get('id');
$userProfile->email = $attributes->get('email');
$userProfile->firstName = $attributes->get('first_name');
$userProfile->lastName = $attributes->get('last_name');
$userProfile->displayName = $attributes->get('full_name') ?: $data->get('id');
$userProfile->photoURL = $attributes->get('thumb_url');
$userProfile->profileURL = $attributes->get('url');
$userProfile->emailVerified = $attributes->get('is_email_verified') ? $userProfile->email : '';
return $userProfile;
}
/**
* Contacts are defined as Patrons here
*/
public function getUserContacts()
{
$campaignId = $this->config->get('campaign_id') ?: null;
$tierFilter = $this->config->get('tier_filter') ?: null;
$campaigns = [];
if ($campaignId === null) {
$campaignsUrl = 'oauth2/v2/campaigns';
do {
$response = $this->apiRequest($campaignsUrl);
$data = new Collection($response);
if (!$data->exists('data')) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
foreach ($data->filter('data')->toArray() as $item) {
$campaign = new Collection($item);
$campaigns[] = $campaign->get('id');
}
if ($data->filter('links')->exists('next')) {
$campaignsUrl = $data->filter('links')->get('next');
$pagedList = true;
} else {
$pagedList = false;
}
} while ($pagedList);
} else {
$campaigns[] = $campaignId;
}
$contacts = [];
foreach ($campaigns as $campaignId) {
$params = [
'include' => 'currently_entitled_tiers',
'fields[member]' => 'full_name,patron_status,email',
'fields[tier]' => 'title',
];
$membersUrl = 'oauth2/v2/campaigns/' . $campaignId . '/members?' . http_build_query($params);
do {
$response = $this->apiRequest($membersUrl);
$data = new Collection($response);
if (!$data->exists('data')) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
$tierTitles = [];
foreach ($data->filter('included')->toArray() as $item) {
$includedItem = new Collection($item);
if ($includedItem->get('type') == 'tier') {
$tierTitles[$includedItem->get('id')] = $includedItem->filter('attributes')->get('title');
}
}
foreach ($data->filter('data')->toArray() as $item) {
$member = new Collection($item);
if ($member->filter('attributes')->get('patron_status') == 'active_patron') {
$tiers = [];
$tierObs = $member->filter('relationships')->filter('currently_entitled_tiers')->get('data');
foreach ($tierObs as $item) {
$tier = new Collection($item);
$tierId = $tier->get('id');
$tiers[] = $tierTitles[$tierId];
}
if (($tierFilter === null) || (in_array($tierFilter, $tiers))) {
$userContact = new User\Contact();
$userContact->identifier = $member->get('id');
$userContact->email = $member->filter('attributes')->get('email');
$userContact->displayName = $member->filter('attributes')->get('full_name');
$userContact->description = json_encode($tiers);
$contacts[] = $userContact;
}
}
}
if ($data->filter('links')->exists('next')) {
$membersUrl = $data->filter('links')->get('next');
$pagedList = true;
} else {
$pagedList = false;
}
} while ($pagedList);
}
return $contacts;
}
}
PK ‚¹%U¡¡µó ó src/Provider/BlizzardAPAC.phpnu €žÙ˜ storeData('orcid', $data->get('orcid'));
return $data;
}
/**
* {@inheritdoc}
*/
public function getUserProfile()
{
$response = $this->apiRequest($this->getStoredData('orcid') . '/record');
$data = new Data\Collection($response['record']);
if (!$data->exists('orcid-identifier')) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
$profile = new User\Profile();
$profile = $this->getDetails($profile, $data);
$profile = $this->getBiography($profile, $data);
$profile = $this->getWebsite($profile, $data);
$profile = $this->getName($profile, $data);
$profile = $this->getEmail($profile, $data);
$profile = $this->getLanguage($profile, $data);
$profile = $this->getAddress($profile, $data);
return $profile;
}
/**
* Get profile details.
*
* @param User\Profile $profile
* @param Data\Collection $data
*
* @return User\Profile
*/
protected function getDetails(User\Profile $profile, Data\Collection $data)
{
$data = new Data\Collection($data->get('orcid-identifier'));
$profile->identifier = $data->get('path');
$profile->profileURL = $data->get('uri');
return $profile;
}
/**
* Get profile biography.
*
* @param User\Profile $profile
* @param Data\Collection $data
*
* @return User\Profile
*/
protected function getBiography(User\Profile $profile, Data\Collection $data)
{
$data = new Data\Collection($data->get('person'));
$data = new Data\Collection($data->get('biography'));
$profile->description = $data->get('content');
return $profile;
}
/**
* Get profile website.
*
* @param User\Profile $profile
* @param Data\Collection $data
*
* @return User\Profile
*/
protected function getWebsite(User\Profile $profile, Data\Collection $data)
{
$data = new Data\Collection($data->get('person'));
$data = new Data\Collection($data->get('researcher-urls'));
$data = new Data\Collection($data->get('researcher-url'));
if ($data->exists(0)) {
$data = new Data\Collection($data->get(0));
}
$profile->webSiteURL = $data->get('url');
return $profile;
}
/**
* Get profile name.
*
* @param User\Profile $profile
* @param Data\Collection $data
*
* @return User\Profile
*/
protected function getName(User\Profile $profile, Data\Collection $data)
{
$data = new Data\Collection($data->get('person'));
$data = new Data\Collection($data->get('name'));
if ($data->exists('credit-name')) {
$profile->displayName = $data->get('credit-name');
} else {
$profile->displayName = $data->get('given-names') . ' ' . $data->get('family-name');
}
$profile->firstName = $data->get('given-names');
$profile->lastName = $data->get('family-name');
return $profile;
}
/**
* Get profile email.
*
* @param User\Profile $profile
* @param Data\Collection $data
*
* @return User\Profile
*/
protected function getEmail(User\Profile $profile, Data\Collection $data)
{
$data = new Data\Collection($data->get('person'));
$data = new Data\Collection($data->get('emails'));
$data = new Data\Collection($data->get('email'));
if (!$data->exists(0)) {
$email = $data;
} else {
$email = new Data\Collection($data->get(0));
$i = 1;
while ($email->get('@attributes')['primary'] == 'false') {
$email = new Data\Collection($data->get($i));
$i++;
}
}
if ($email->get('@attributes')['primary'] == 'false') {
return $profile;
}
$profile->email = $email->get('email');
if ($email->get('@attributes')['verified'] == 'true') {
$profile->emailVerified = $email->get('email');
}
return $profile;
}
/**
* Get profile language.
*
* @param User\Profile $profile
* @param Data\Collection $data
*
* @return User\Profile
*/
protected function getLanguage(User\Profile $profile, Data\Collection $data)
{
$data = new Data\Collection($data->get('preferences'));
$profile->language = $data->get('locale');
return $profile;
}
/**
* Get profile address.
*
* @param User\Profile $profile
* @param Data\Collection $data
*
* @return User\Profile
*/
protected function getAddress(User\Profile $profile, Data\Collection $data)
{
$data = new Data\Collection($data->get('person'));
$data = new Data\Collection($data->get('addresses'));
$data = new Data\Collection($data->get('address'));
if ($data->exists(0)) {
$data = new Data\Collection($data->get(0));
}
$profile->country = $data->get('country');
return $profile;
}
}
PK ‚¹%UÍ[;”ù
ù
src/Provider/WeChat.phpnu €žÙ˜ AuthorizeUrlParameters += [
'appid' => $this->clientId
];
unset($this->AuthorizeUrlParameters['client_id']);
$this->tokenExchangeParameters += [
'appid' => $this->clientId,
'secret' => $this->clientSecret
];
unset($this->tokenExchangeParameters['client_id']);
unset($this->tokenExchangeParameters['client_secret']);
if ($this->isRefreshTokenAvailable()) {
$this->tokenRefreshParameters += [
'appid' => $this->clientId,
];
}
$this->apiRequestParameters = [
'appid' => $this->clientId,
'secret' => $this->clientSecret
];
}
/**
* {@inheritdoc}
*/
protected function validateAccessTokenExchange($response)
{
$collection = parent::validateAccessTokenExchange($response);
$this->storeData('openid', $collection->get('openid'));
$this->storeData('access_token', $collection->get('access_token'));
}
/**
* {@inheritdoc}
*/
public function getUserProfile()
{
$openid = $this->getStoredData('openid');
$access_token = $this->getStoredData('access_token');
$response = $this->apiRequest('userinfo', 'GET', ['openid' => $openid, 'access_token' => $access_token]);
$data = new Data\Collection($response);
if (!$data->exists('openid')) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
$userProfile = new User\Profile();
$userProfile->identifier = $data->get('openid');
$userProfile->displayName = $data->get('nickname');
$userProfile->photoURL = $data->get('headimgurl');
$userProfile->city = $data->get('city');
$userProfile->region = $data->get('province');
$userProfile->country = $data->get('country');
$genders = ['', 'male', 'female'];
$userProfile->gender = $genders[(int)$data->get('sex')];
return $userProfile;
}
}
PK ‚¹%U³Émåõ õ src/Provider/Authentiq.phpnu €žÙ˜ AuthorizeUrlParameters += [
'prompt' => 'consent'
];
$this->tokenExchangeHeaders = [
'Authorization' => 'Basic ' . base64_encode($this->clientId . ':' . $this->clientSecret)
];
$this->tokenRefreshHeaders = [
'Authorization' => 'Basic ' . base64_encode($this->clientId . ':' . $this->clientSecret)
];
}
/**
* {@inheritdoc}
*/
public function getUserProfile()
{
$response = $this->apiRequest('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->displayName = $data->get('name');
$userProfile->firstName = $data->get('given_name');
// $userProfile->middleName = $data->get('middle_name'); // not supported
$userProfile->lastName = $data->get('family_name');
if (!empty($userProfile->displayName)) {
$userProfile->displayName = join(' ', array($userProfile->firstName,
// $userProfile->middleName,
$userProfile->lastName));
}
$userProfile->email = $data->get('email');
$userProfile->emailVerified = $data->get('email_verified') ? $userProfile->email : '';
$userProfile->phone = $data->get('phone');
// $userProfile->phoneVerified = $data->get('phone_verified') ? $userProfile->phone : ''; // not supported
$userProfile->profileURL = $data->get('profile');
$userProfile->webSiteURL = $data->get('website');
$userProfile->photoURL = $data->get('picture');
$userProfile->gender = $data->get('gender');
$userProfile->address = $data->filter('address')->get('street_address');
$userProfile->city = $data->filter('address')->get('locality');
$userProfile->country = $data->filter('address')->get('country');
$userProfile->region = $data->filter('address')->get('region');
$userProfile->zip = $data->filter('address')->get('postal_code');
return $userProfile;
}
}
PK ‚¹%UY¹>q $ src/Provider/StackExchangeOpenID.phpnu €žÙ˜ storage->get($this->providerId . '.user');
$userProfile->identifier = !empty($userProfile->identifier) ? $userProfile->identifier : $userProfile->email;
$userProfile->emailVerified = $userProfile->email;
// re store the user profile
$this->storage->set($this->providerId . '.user', $userProfile);
}
}
PK ‚¹%Uý^7 7 src/Provider/OpenID.phpnu €žÙ˜ 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
{
}
PK ‚¹%U@êñ³Y Y src/Provider/WindowsLive.phpnu €žÙ˜ 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('name');
$userProfile->firstName = $data->get('first_name');
$userProfile->lastName = $data->get('last_name');
$userProfile->gender = $data->get('gender');
$userProfile->profileURL = $data->get('link');
$userProfile->email = $data->filter('emails')->get('preferred');
$userProfile->emailVerified = $data->filter('emails')->get('account');
$userProfile->birthDay = $data->get('birth_day');
$userProfile->birthMonth = $data->get('birth_month');
$userProfile->birthYear = $data->get('birth_year');
$userProfile->language = $data->get('locale');
return $userProfile;
}
/**
* {@inheritdoc}
*/
public function getUserContacts()
{
$response = $this->apiRequest('me/contacts');
$data = new Data\Collection($response);
if (!$data->exists('data')) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
$contacts = [];
foreach ($data->filter('data')->toArray() as $idx => $entry) {
$userContact = new User\Contact();
$userContact->identifier = $entry->get('id');
$userContact->displayName = $entry->get('name');
$userContact->email = $entry->filter('emails')->get('preferred');
$contacts[] = $userContact;
}
return $contacts;
}
}
PK ‚¹%UŒŒ src/Provider/Telegram.phpnu €žÙ˜ Hybridauth\HttpClient\Util::getCurrentUrl(),
* 'keys' => ['id' => 'your_bot_name', 'secret' => 'your_bot_token'],
* ];
*
* $adapter = new Hybridauth\Provider\Telegram($config);
*
* try {
* $adapter->authenticate();
*
* $userProfile = $adapter->getUserProfile();
* } catch (\Exception $e) {
* print $e->getMessage();
* }
*/
class Telegram extends AbstractAdapter implements AdapterInterface
{
protected $botId = '';
protected $botSecret = '';
protected $callbackUrl = '';
/**
* IPD API Documentation
*
* OPTIONAL.
*
* @var string
*/
protected $apiDocumentation = 'https://core.telegram.org/bots';
/**
* {@inheritdoc}
*/
protected function configure()
{
$this->botId = $this->config->filter('keys')->get('id');
$this->botSecret = $this->config->filter('keys')->get('secret');
$this->callbackUrl = $this->config->get('callback');
if (!$this->botId || !$this->botSecret) {
throw new InvalidApplicationCredentialsException(
'Your application id is required in order to connect to ' . $this->providerId
);
}
}
/**
* {@inheritdoc}
*/
protected function initialize()
{
}
/**
* {@inheritdoc}
*/
public function authenticate()
{
$this->logger->info(sprintf('%s::authenticate()', get_class($this)));
if (!filter_input(INPUT_GET, 'hash')) {
$this->authenticateBegin();
} else {
$this->authenticateCheckError();
$this->authenticateFinish();
}
return null;
}
/**
* {@inheritdoc}
*/
public function isConnected()
{
$authData = $this->getStoredData('auth_data');
return !empty($authData);
}
/**
* {@inheritdoc}
*/
public function getUserProfile()
{
$data = new Collection($this->getStoredData('auth_data'));
if (!$data->exists('id')) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
$userProfile = new Profile();
$userProfile->identifier = $data->get('id');
$userProfile->firstName = $data->get('first_name');
$userProfile->lastName = $data->get('last_name');
$userProfile->displayName = $data->get('username');
$userProfile->photoURL = $data->get('photo_url');
$username = $data->get('username');
if (!empty($username)) {
// Only some accounts have usernames.
$userProfile->profileURL = "https://t.me/{$username}";
}
return $userProfile;
}
/**
* See: https://telegram.im/widget-login.php
* See: https://gist.github.com/anonymous/6516521b1fb3b464534fbc30ea3573c2
*/
protected function authenticateCheckError()
{
$auth_data = $this->parseAuthData();
$check_hash = $auth_data['hash'];
unset($auth_data['hash']);
$data_check_arr = [];
foreach ($auth_data as $key => $value) {
if (!empty($value)) {
$data_check_arr[] = $key . '=' . $value;
}
}
sort($data_check_arr);
$data_check_string = implode("\n", $data_check_arr);
$secret_key = hash('sha256', $this->botSecret, true);
$hash = hash_hmac('sha256', $data_check_string, $secret_key);
if (strcmp($hash, $check_hash) !== 0) {
throw new InvalidAuthorizationCodeException(
sprintf('Provider returned an error: %s', 'Data is NOT from Telegram')
);
}
if ((time() - $auth_data['auth_date']) > 86400) {
throw new InvalidAuthorizationCodeException(
sprintf('Provider returned an error: %s', 'Data is outdated')
);
}
}
/**
* See: https://telegram.im/widget-login.php
*/
protected function authenticateBegin()
{
$this->logger->debug(sprintf('%s::authenticateBegin(), redirecting user to:', get_class($this)));
$nonce = $this->config->get('nonce');
$nonce_code = empty($nonce) ? '' : "nonce=\"{$nonce}\"";
exit(
<<
HTML
);
}
protected function authenticateFinish()
{
$this->logger->debug(
sprintf('%s::authenticateFinish(), callback url:', get_class($this)),
[Util::getCurrentUrl(true)]
);
$this->storeData('auth_data', $this->parseAuthData());
$this->initialize();
}
protected function parseAuthData()
{
return [
'id' => filter_input(INPUT_GET, 'id'),
'first_name' => filter_input(INPUT_GET, 'first_name'),
'last_name' => filter_input(INPUT_GET, 'last_name'),
'username' => filter_input(INPUT_GET, 'username'),
'photo_url' => filter_input(INPUT_GET, 'photo_url'),
'auth_date' => filter_input(INPUT_GET, 'auth_date'),
'hash' => filter_input(INPUT_GET, 'hash'),
];
}
}
PK ‚¹%UÙw³©ñ ñ src/Provider/QQ.phpnu €žÙ˜ isRefreshTokenAvailable()) {
$this->tokenRefreshParameters += [
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret,
];
}
$this->apiRequestParameters = [
'access_token' => $this->getStoredData('access_token')
];
$this->apiRequestHeaders = [];
}
/**
* {@inheritdoc}
*/
protected function validateAccessTokenExchange($response)
{
$collection = parent::validateAccessTokenExchange($response);
$resp = $this->apiRequest($this->accessTokenInfoUrl);
$resp = key($resp);
$len = strlen($resp);
$res = substr($resp, 10, $len - 14);
$response = (new Data\Parser())->parse($res);
if (!isset($response->openid)) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
$this->storeData('openid', $response->openid);
return $collection;
}
/**
* {@inheritdoc}
*/
public function getUserProfile()
{
$openid = $this->getStoredData('openid');
$userRequestParameters = [
'oauth_consumer_key' => $this->clientId,
'openid' => $openid,
'format' => 'json'
];
$response = $this->apiRequest($this->accessUserInfo, 'GET', $userRequestParameters);
$data = new Data\Collection($response);
if ($data->get('ret') < 0) {
throw new UnexpectedApiResponseException('Provider API returned an error: ' . $data->get('msg'));
}
$userProfile = new Profile();
$userProfile->identifier = $openid;
$userProfile->displayName = $data->get('nickname');
$userProfile->photoURL = $data->get('figureurl_2');
$userProfile->gender = $data->get('gender');
$userProfile->region = $data->get('province');
$userProfile->city = $data->get('city');
return $userProfile;
}
}
PK ‚¹%UŽ‹|2# # src/Provider/Dropbox.phpnu €žÙ˜ 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;
}
}
PK ‚¹%U¥VwŒ<
<
src/Provider/Keycloak.phpnu €žÙ˜ [
* 'enabled' => true,
* 'url' => 'https://your-keycloak', // depending on your setup you might need to add '/auth'
* 'realm' => 'your-realm',
* 'keys' => [
* 'id' => 'client-id',
* 'secret' => 'client-secret'
* ]
* ]
*
*/
class Keycloak extends OAuth2
{
/**
* {@inheritdoc}
*/
public $scope = 'openid profile email';
/**
* {@inheritdoc}
*/
protected $apiDocumentation = 'https://www.keycloak.org/docs/latest/securing_apps/#_oidc';
/**
* {@inheritdoc}
*/
protected function configure()
{
parent::configure();
if (!$this->config->exists('url')) {
throw new InvalidApplicationCredentialsException(
'You must define a provider url'
);
}
$url = $this->config->get('url');
if (!$this->config->exists('realm')) {
throw new InvalidApplicationCredentialsException(
'You must define a realm'
);
}
$realm = $this->config->get('realm');
$this->apiBaseUrl = $url . '/realms/' . $realm . '/protocol/openid-connect/';
$this->authorizeUrl = $this->apiBaseUrl . 'auth';
$this->accessTokenUrl = $this->apiBaseUrl . 'token';
}
/**
* {@inheritdoc}
*/
public function getUserProfile()
{
$response = $this->apiRequest('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->displayName = $data->get('preferred_username');
$userProfile->email = $data->get('email');
$userProfile->firstName = $data->get('given_name');
$userProfile->lastName = $data->get('family_name');
$userProfile->emailVerified = $data->get('email_verified');
return $userProfile;
}
}
PK ‚¹%U´ru× × src/Provider/Dribbble.phpnu €žÙ˜ 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->profileURL = $data->get('html_url');
$userProfile->photoURL = $data->get('avatar_url');
$userProfile->description = $data->get('bio');
$userProfile->region = $data->get('location');
$userProfile->displayName = $data->get('name');
$userProfile->displayName = $userProfile->displayName ?: $data->get('username');
$userProfile->webSiteURL = $data->filter('links')->get('web');
return $userProfile;
}
}
PK ‚¹%UëÃtLç ç src/Provider/BlizzardEU.phpnu €žÙ˜ openIdClient->identity = $this->openidIdentifier;
$this->openIdClient->returnUrl = $this->callback;
$this->openIdClient->required = [
'namePerson/prefix',
'namePerson/first',
'namePerson/last',
'namePerson/middle',
'namePerson/suffix',
'namePerson/friendly',
'person/guid',
'birthDate/birthYear',
'birthDate/birthMonth',
'birthDate/birthday',
'gender',
'language/pref',
'contact/phone/default',
'contact/phone/home',
'contact/phone/business',
'contact/phone/cell',
'contact/phone/fax',
'contact/postaladdress/home',
'contact/postaladdressadditional/home',
'contact/city/home',
'contact/state/home',
'contact/country/home',
'contact/postalcode/home',
'contact/postaladdress/business',
'contact/postaladdressadditional/business',
'contact/city/business',
'contact/state/business',
'contact/country/business',
'contact/postalcode/business',
'company/name',
'company/title',
];
HttpClient\Util::redirect($this->openIdClient->authUrl());
}
}
PK ‚¹%UÓÀÂp p src/Provider/BitBucket.phpnu €žÙ˜ /workspace/settings/api
*/
/**
* BitBucket OAuth2 provider adapter.
*/
class BitBucket extends OAuth2
{
/**
* {@inheritdoc}
*/
protected $scope = 'email';
/**
* {@inheritdoc}
*/
protected $apiBaseUrl = 'https://api.bitbucket.org/2.0/';
/**
* {@inheritdoc}
*/
protected $authorizeUrl = 'https://bitbucket.org/site/oauth2/authorize';
/**
* {@inheritdoc}
*/
protected $accessTokenUrl = 'https://bitbucket.org/site/oauth2/access_token';
/**
* {@inheritdoc}
*/
protected $apiDocumentation = 'https://developer.atlassian.com/bitbucket/concepts/oauth2.html';
/**
* {@inheritdoc}
*/
public function getUserProfile()
{
$response = $this->apiRequest('user');
$data = new Data\Collection($response);
if (!$data->exists('uuid')) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
$userProfile = new User\Profile();
$userProfile->identifier = $data->get('uuid');
$userProfile->profileURL = 'https://bitbucket.org/' . $data->get('username') . '/';
$userProfile->displayName = $data->get('display_name');
$userProfile->email = $data->get('email');
$userProfile->webSiteURL = $data->get('website');
$userProfile->region = $data->get('location');
$userProfile->displayName = $userProfile->displayName ?: $data->get('username');
if (empty($userProfile->email) && strpos($this->scope, 'email') !== false) {
try {
// user email is not mandatory so keep it quiet
$userProfile = $this->requestUserEmail($userProfile);
} catch (\Exception $e) {
}
}
return $userProfile;
}
/**
* Request user email
*
* @param $userProfile
*
* @return User\Profile
*
* @throws \Exception
*/
protected function requestUserEmail($userProfile)
{
$response = $this->apiRequest('user/emails');
foreach ($response->values as $idx => $item) {
if (!empty($item->is_primary) && $item->is_primary == true) {
$userProfile->email = $item->email;
if (!empty($item->is_confirmed) && $item->is_confirmed == true) {
$userProfile->emailVerified = $userProfile->email;
}
break;
}
}
return $userProfile;
}
}
PK ‚¹%UmQo( ( src/Provider/Apple.phpnu €žÙ˜ 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';
if ($this->isRefreshTokenAvailable()) {
$this->tokenRefreshParameters += [
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret,
];
}
}
/**
* {@inheritdoc}
* @throws InvalidApplicationCredentialsException
*/
protected function configure()
{
$keys = $this->config->get('keys');
$keys['secret'] = $this->getSecret();
$this->config->set('keys', $keys);
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;
}
/**
* Get the user profile
*
* @throws HttpClientFailureException
* @throws InvalidAccessTokenException
* @throws UnexpectedValueException
* @throws HttpRequestFailedException
* @throws Exception
*/
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');
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 = (version_compare($this->getJwtVersion(), '6.2') < 0) ?
JWT::decode($id_token, $pem, ['RS256']) :
JWT::decode($id_token, new Key($pem, 'RS256'));
break;
} catch (Exception $e) {
$error = $e->getMessage();
if ($e instanceof 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;
}
/**
* Get the Apple secret as a JWT token
*
* @return string secret token
* @throws InvalidApplicationCredentialsException
*/
private function getSecret()
{
// Your 10-character Team ID
$team_id = $this->config->filter('keys')->get('team_id');
if (!$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
$client_id = $this->config->filter('keys')->get('id') ?: $this->config->filter('keys')->get('key');
if (!$client_id) {
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
$key_id = $this->config->filter('keys')->get('key_id');
if (!$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) {
$key_file = $this->config->filter('keys')->get('key_file');
if (!$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
];
return JWT::encode($data, $key_content, 'ES256', $key_id);
}
/**
* Try to get the installed JWT version
*
* If composer 2 is installed use InstalledVersions::getVersion,
* otherwise return an empty string because no version check is available
*
* @return string|null
*/
private function getJwtVersion()
{
// assume old JWT version if no version check is possible because composer 1 is installed
return class_exists('Composer\InstalledVersions') ?
InstalledVersions::getVersion('firebase/php-jwt') :
'';
}
}
PK ‚¹%UjWwÜ% % src/Provider/WordPress.phpnu €žÙ˜ 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;
}
}
PK ‚¹%U$&g g src/Provider/Twitter.phpnu €žÙ˜ 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;
}
}
PK ‚¹%Uü!Ðó ó src/Provider/WeChatChina.phpnu €žÙ˜ 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);
}
}
PK ‚¹%Uu@¨N N src/Provider/Amazon.phpnu €žÙ˜ isRefreshTokenAvailable()) {
$this->tokenRefreshParameters += [
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret,
];
}
}
/**
* {@inheritdoc}
*/
public function getUserProfile()
{
$response = $this->apiRequest('user/profile');
$data = new Data\Collection($response);
if (!$data->exists('user_id')) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
$userProfile = new User\Profile();
$userProfile->identifier = $data->get('user_id');
$userProfile->displayName = $data->get('name');
$userProfile->email = $data->get('email');
return $userProfile;
}
}
PK ‚¹%U;S
S
src/Provider/Tumblr.phpnu €žÙ˜ apiRequest('user/info');
$data = new Data\Collection($response);
if (!$data->exists('response')) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
$userProfile = new User\Profile();
$userProfile->displayName = $data->filter('response')->filter('user')->get('name');
foreach ($data->filter('response')->filter('user')->filter('blogs')->toArray() as $blog) {
$blog = new Data\Collection($blog);
if ($blog->get('primary') && $blog->exists('url')) {
$userProfile->identifier = $blog->get('url');
$userProfile->profileURL = $blog->get('url');
$userProfile->webSiteURL = $blog->get('url');
$userProfile->description = strip_tags($blog->get('description'));
$bloghostname = explode('://', $blog->get('url'));
$bloghostname = substr($bloghostname[1], 0, -1);
// store user's primary blog which will be used as target by setUserStatus
$this->storeData('primary_blog', $bloghostname);
break;
}
}
return $userProfile;
}
/**
* {@inheritdoc}
*/
public function setUserStatus($status)
{
$status = is_string($status)
? ['type' => 'text', 'body' => $status]
: $status;
$response = $this->apiRequest('blog/' . $this->getStoredData('primary_blog') . '/post', 'POST', $status);
return $response;
}
}
PK ‚¹%UûC_ß@ @ src/Provider/Medium.phpnu €žÙ˜ 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;
}
}
PK ‚¹%U vSe e src/Provider/Foursquare.phpnu €žÙ˜ config->get('api_version') ?: '20140201';
$this->apiRequestParameters = [
'oauth_token' => $this->getStoredData('access_token'),
'v' => $apiVersion,
];
}
/**
* {@inheritdoc}
*/
public function getUserProfile()
{
$response = $this->apiRequest('users/self');
$data = new Data\Collection($response);
if (!$data->exists('response')) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
$userProfile = new User\Profile();
$data = $data->filter('response')->filter('user');
$userProfile->identifier = $data->get('id');
$userProfile->firstName = $data->get('firstName');
$userProfile->lastName = $data->get('lastName');
$userProfile->gender = $data->get('gender');
$userProfile->city = $data->get('homeCity');
$userProfile->email = $data->filter('contact')->get('email');
$userProfile->emailVerified = $userProfile->email;
$userProfile->profileURL = 'https://www.foursquare.com/user/' . $userProfile->identifier;
$userProfile->displayName = trim($userProfile->firstName . ' ' . $userProfile->lastName);
if ($data->exists('photo')) {
$photoSize = $this->config->get('photo_size') ?: '150x150';
$userProfile->photoURL = $data->filter('photo')->get('prefix');
$userProfile->photoURL .= $photoSize . $data->filter('photo')->get('suffix');
}
return $userProfile;
}
/**
* {@inheritdoc}
*/
public function getUserContacts()
{
$response = $this->apiRequest('users/self/friends');
$data = new Data\Collection($response);
if (!$data->exists('response')) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
$contacts = [];
foreach ($data->filter('response')->filter('friends')->filter('items')->toArray() as $item) {
$contacts[] = $this->fetchUserContact($item);
}
return $contacts;
}
/**
* @param $item
*
* @return User\Contact
*/
protected function fetchUserContact($item)
{
$photoSize = $this->config->get('photo_size') ?: '150x150';
$item = new Data\Collection($item);
$userContact = new User\Contact();
$userContact->identifier = $item->get('id');
$userContact->photoURL = $item->filter('photo')->get('prefix');
$userContact->photoURL .= $photoSize . $item->filter('photo')->get('suffix');
$userContact->displayName = trim($item->get('firstName') . ' ' . $item->get('lastName'));
$userContact->email = $item->filter('contact')->get('email');
return $userContact;
}
}
PK ‚¹%UüÀÄXâ â src/Provider/TwitchTV.phpnu €žÙ˜ 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;
}
}
PK ‚¹%U•»×õ$ $ src/Provider/MicrosoftGraph.phpnu €žÙ˜ 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';
/**
* {@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();
$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);
}
}
/**
* {@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;
}
}
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;
}
}
PK ‚¹%UHøu¾ ¾ src/Provider/DeviantArt.phpnu €žÙ˜ 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;
}
}
PK ‚¹%U”Zf^¶ ¶ src/Provider/Reddit.phpnu €žÙ˜ AuthorizeUrlParameters += [
'duration' => 'permanent'
];
$this->tokenExchangeParameters = [
'client_id' => $this->clientId,
'grant_type' => 'authorization_code',
'redirect_uri' => $this->callback
];
$this->tokenExchangeHeaders = [
'Authorization' => 'Basic ' . base64_encode($this->clientId . ':' . $this->clientSecret)
];
$this->tokenRefreshHeaders = $this->tokenExchangeHeaders;
}
/**
* {@inheritdoc}
*/
public function getUserProfile()
{
$response = $this->apiRequest('me.json');
$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->profileURL = 'https://www.reddit.com/user/' . $data->get('name') . '/';
$userProfile->photoURL = $data->get('icon_img');
return $userProfile;
}
}
PK ‚¹%UëÆ:) src/Provider/Instagram.phpnu €žÙ˜ getStoredData($this->accessTokenName);
$this->apiRequestParameters[$this->accessTokenName] = $accessToken;
}
/**
* {@inheritdoc}
*/
protected function validateAccessTokenExchange($response)
{
$collection = parent::validateAccessTokenExchange($response);
if (!$collection->exists('expires_in')) {
// Instagram tokens always expire in an hour, but this is implicit not explicit
$expires_in = 60 * 60;
$expires_at = time() + $expires_in;
$this->storeData('expires_in', $expires_in);
$this->storeData('expires_at', $expires_at);
}
return $collection;
}
/**
* {@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 InvalidAccessTokenException
*/
public function exchangeAccessToken()
{
if ($this->getStoredData('expires_in') >= 5000000) {
/*
Refresh a long-lived token (needed on Instagram, but not Facebook).
It's not an oAuth style refresh using a refresh token.
Actually it's really just another exchange, and invalidates the old token.
Facebook/Instagram documentation is not very helpful at explaining that!
*/
$exchangeTokenParameters = [
'grant_type' => 'ig_refresh_token',
'client_secret' => $this->clientSecret,
'access_token' => $this->getStoredData('access_token'),
];
$url = 'https://graph.instagram.com/refresh_access_token';
} else {
// Exchange short-lived to long-lived
$exchangeTokenParameters = [
'grant_type' => 'ig_exchange_token',
'client_secret' => $this->clientSecret,
'access_token' => $this->getStoredData('access_token'),
];
$url = 'https://graph.instagram.com/access_token';
}
$response = $this->httpClient->request(
$url,
'GET',
$exchangeTokenParameters
);
$this->validateApiResponse('Unable to exchange the access token');
$this->validateAccessTokenExchange($response);
return $response;
}
/**
* {@inheritdoc}
*/
public function getUserProfile()
{
$response = $this->apiRequest('me', 'GET', [
'fields' => 'id,username,account_type,media_count',
]);
$data = new 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('username');
$userProfile->profileURL = "https://instagram.com/{$userProfile->displayName}";
$userProfile->data = [
'account_type' => $data->get('account_type'),
'media_count' => $data->get('media_count'),
];
return $userProfile;
}
/**
* Fetch user medias.
*
* @param int $limit Number of elements per page.
* @param string $pageId Current pager ID.
* @param array|null $fields Fields to fetch per media.
*
* @return \Hybridauth\Data\Collection
*
* @throws \Hybridauth\Exception\HttpClientFailureException
* @throws \Hybridauth\Exception\HttpRequestFailedException
* @throws \Hybridauth\Exception\InvalidAccessTokenException
* @throws \Hybridauth\Exception\UnexpectedApiResponseException
*/
public function getUserMedia($limit = 12, $pageId = null, array $fields = null)
{
if (empty($fields)) {
$fields = [
'id',
'caption',
'media_type',
'media_url',
'thumbnail_url',
'permalink',
'timestamp',
'username',
];
}
$params = [
'fields' => implode(',', $fields),
'limit' => $limit,
];
if ($pageId !== null) {
$params['after'] = $pageId;
}
$response = $this->apiRequest('me/media', 'GET', $params);
$data = new Collection($response);
if (!$data->exists('data')) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
return $data;
}
/**
* Fetches a single user's media.
*
* @param string $mediaId Media ID.
* @param array|null $fields Fields to fetch per media.
*
* @return \Hybridauth\Data\Collection
*
* @throws \Hybridauth\Exception\HttpClientFailureException
* @throws \Hybridauth\Exception\HttpRequestFailedException
* @throws \Hybridauth\Exception\InvalidAccessTokenException
* @throws \Hybridauth\Exception\UnexpectedApiResponseException
*/
public function getMedia($mediaId, array $fields = null)
{
if (empty($fields)) {
$fields = [
'id',
'caption',
'media_type',
'media_url',
'thumbnail_url',
'permalink',
'timestamp',
'username',
];
}
$response = $this->apiRequest($mediaId, 'GET', [
'fields' => implode(',', $fields),
]);
$data = new Collection($response);
if (!$data->exists('id')) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
return $data;
}
}
PK ‚¹%UʦÁ
Á
src/Provider/StackExchange.phpnu €žÙ˜ Hybridauth\HttpClient\Util::getCurrentUrl(),
* 'keys' => ['id' => '', 'secret' => ''],
* 'site' => 'stackoverflow' // required parameter to call getUserProfile()
* 'api_key' => '...' // that thing to receive a higher request quota.
* ];
*
* $adapter = new Hybridauth\Provider\StackExchange($config);
*
* try {
* $adapter->authenticate();
*
* $userProfile = $adapter->getUserProfile();
* $tokens = $adapter->getAccessToken();
* } catch (\Exception $e ){
* echo $e->getMessage() ;
* }
*/
class StackExchange extends OAuth2
{
/**
* {@inheritdoc}
*/
protected $scope = null;
/**
* {@inheritdoc}
*/
protected $apiBaseUrl = 'https://api.stackexchange.com/2.2/';
/**
* {@inheritdoc}
*/
protected $authorizeUrl = 'https://stackexchange.com/oauth';
/**
* {@inheritdoc}
*/
protected $accessTokenUrl = 'https://stackexchange.com/oauth/access_token';
/**
* {@inheritdoc}
*/
protected $apiDocumentation = 'https://api.stackexchange.com/docs/authentication';
/**
* {@inheritdoc}
*/
protected function initialize()
{
parent::initialize();
$apiKey = $this->config->get('api_key');
$this->apiRequestParameters = ['key' => $apiKey];
}
/**
* {@inheritdoc}
*/
public function getUserProfile()
{
$site = $this->config->get('site');
$response = $this->apiRequest('me', 'GET', [
'site' => $site,
'access_token' => $this->getStoredData('access_token'),
]);
if (!$response || !isset($response->items) || !isset($response->items[0])) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
$data = new Data\Collection($response->items[0]);
$userProfile = new User\Profile();
$userProfile->identifier = strval($data->get('user_id'));
$userProfile->displayName = $data->get('display_name');
$userProfile->photoURL = $data->get('profile_image');
$userProfile->profileURL = $data->get('link');
$userProfile->region = $data->get('location');
$userProfile->age = $data->get('age');
return $userProfile;
}
}
PK ‚¹%UþÏÄù ù src/Provider/AOLOpenID.phpnu €žÙ˜ tokenExchangeHeaders = [
'Authorization' => 'Basic ' . base64_encode($this->clientId . ':' . $this->clientSecret)
];
$this->tokenRefreshHeaders = $this->tokenExchangeHeaders;
}
/**
* {@inheritdoc}
*/
public function getUserProfile()
{
$response = $this->apiRequest('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->gender = $data->get('gender');
$userProfile->language = $data->get('locale');
$userProfile->email = $data->get('email');
$userProfile->emailVerified = $data->get('email_verified') ? $userProfile->email : '';
$profileImages = $data->get('profile_images');
if ($this->config->get('photo_size')) {
$prop = 'image' . $this->config->get('photo_size');
} else {
$prop = 'image192';
}
$userProfile->photoURL = $profileImages->$prop;
return $userProfile;
}
}
PK ‚¹%UOV6n n src/Provider/GitHub.phpnu €žÙ˜ 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;
}
}
PK ‚¹%UÆÎkœ œ src/Provider/Paypal.phpnu €žÙ˜ AuthorizeUrlParameters += [
'flowEntry' => 'static'
];
$this->tokenExchangeHeaders = [
'Authorization' => 'Basic ' . base64_encode($this->clientId . ':' . $this->clientSecret)
];
$this->tokenRefreshHeaders = [
'Authorization' => 'Basic ' . base64_encode($this->clientId . ':' . $this->clientSecret)
];
}
/**
* {@inheritdoc}
*
* See: https://developer.paypal.com/docs/api/identity/v1/
* See: https://developer.paypal.com/docs/connect-with-paypal/integrate/
*/
public function getUserProfile()
{
$headers = [
'Content-Type' => 'application/json',
];
$parameters = [
'schema' => 'paypalv1.1'
];
$response = $this->apiRequest('v1/identity/oauth2/userinfo', 'GET', $parameters, $headers);
$data = new Data\Collection($response);
if (!$data->exists('user_id')) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
$userProfile = new User\Profile();
$userProfile->identifier = $data->get('user_id');
$userProfile->firstName = $data->get('given_name');
$userProfile->lastName = $data->get('family_name');
$userProfile->displayName = $data->get('name');
$userProfile->address = $data->filter('address')->get('street_address');
$userProfile->city = $data->filter('address')->get('locality');
$userProfile->country = $data->filter('address')->get('country');
$userProfile->region = $data->filter('address')->get('region');
$userProfile->zip = $data->filter('address')->get('postal_code');
$emails = $data->filter('emails')->toArray();
foreach ($emails as $email) {
$email = new Data\Collection($email);
if ($email->get('confirmed')) {
$userProfile->emailVerified = $email->get('value');
}
if ($email->get('primary')) {
$userProfile->email = $email->get('value');
}
}
return $userProfile;
}
}
PK ‚¹%Uåìùm m src/Provider/Slack.phpnu €žÙ˜ apiRequest('api/users.identity');
$data = new Data\Collection($response);
if (!$data->exists('ok') || !$data->get('ok')) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
$userProfile = new User\Profile();
$userProfile->identifier = $data->filter('user')->get('id');
$userProfile->displayName = $data->filter('user')->get('name');
$userProfile->email = $data->filter('user')->get('email');
$userProfile->photoURL = $this->findLargestImage($data);
return $userProfile;
}
/**
* Returns the url of the image with the highest resolution in the user
* object.
*
* Slack sends multiple image urls with different resolutions. As they make
* no guarantees which resolutions will be included we have to search all
* image_*
properties for the one with the highest resolution.
* The resolution is attached to the property name such as
* image_32
or image_192
.
*
* @param Data\Collection $data response object as returned by
* api/users.identity
*
* @return string|null the value of the image_*
property with
* the highest resolution.
*/
private function findLargestImage(Data\Collection $data)
{
$maxSize = 0;
foreach ($data->filter('user')->properties() as $property) {
if (preg_match('/^image_(\d+)$/', $property, $matches) === 1) {
$availableSize = (int)$matches[1];
if ($maxSize < $availableSize) {
$maxSize = $availableSize;
}
}
}
if ($maxSize > 0) {
return $data->filter('user')->get('image_' . $maxSize);
}
return null;
}
}
PK ‚¹%UNúkÒ src/Provider/Strava.phpnu €žÙ˜ apiRequest('athlete');
$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->firstName = $data->get('firstname');
$userProfile->lastName = $data->get('lastname');
$userProfile->gender = $data->get('sex');
$userProfile->country = $data->get('country');
$userProfile->city = $data->get('city');
$userProfile->email = $data->get('email');
$userProfile->displayName = $userProfile->displayName ?: $data->get('username');
return $userProfile;
}
}
PK ‚¹%Uô„kìq q src/Provider/Discord.phpnu €žÙ˜ 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;
}
}
PK ‚¹%U·’ÑÐ9 9 src/Provider/Steam.phpnu €žÙ˜ Hybridauth\HttpClient\Util::getCurrentUrl(),
* 'keys' => ['secret' => 'steam-api-key']
* ];
*
* $adapter = new Hybridauth\Provider\Steam($config);
*
* try {
* $adapter->authenticate();
*
* $userProfile = $adapter->getUserProfile();
* } catch (\Exception $e) {
* echo $e->getMessage() ;
* }
*/
class Steam extends OpenID
{
/**
* {@inheritdoc}
*/
protected $openidIdentifier = 'http://steamcommunity.com/openid';
/**
* {@inheritdoc}
*/
protected $apiDocumentation = 'https://steamcommunity.com/dev';
/**
* {@inheritdoc}
*/
public function authenticateFinish()
{
parent::authenticateFinish();
$userProfile = $this->storage->get($this->providerId . '.user');
$userProfile->identifier = str_ireplace([
'http://steamcommunity.com/openid/id/',
'https://steamcommunity.com/openid/id/',
], '', $userProfile->identifier);
if (!$userProfile->identifier) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
try {
$apiKey = $this->config->filter('keys')->get('secret');
// if api key is provided, we attempt to use steam web api
if ($apiKey) {
$result = $this->getUserProfileWebAPI($apiKey, $userProfile->identifier);
} else {
// otherwise we fallback to community data
$result = $this->getUserProfileLegacyAPI($userProfile->identifier);
}
// fetch user profile
foreach ($result as $k => $v) {
$userProfile->$k = $v ?: $userProfile->$k;
}
} catch (\Exception $e) {
}
// store user profile
$this->storage->set($this->providerId . '.user', $userProfile);
}
/**
* Fetch user profile on Steam web API
*
* @param $apiKey
* @param $steam64
*
* @return array
*/
public function getUserProfileWebAPI($apiKey, $steam64)
{
$q = http_build_query(['key' => $apiKey, 'steamids' => $steam64]);
$apiUrl = 'http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?' . $q;
$response = $this->httpClient->request($apiUrl);
$data = json_decode($response);
$data = isset($data->response->players[0]) ? $data->response->players[0] : null;
$data = new Data\Collection($data);
$userProfile = [];
$userProfile['displayName'] = (string)$data->get('personaname');
$userProfile['firstName'] = (string)$data->get('realname');
$userProfile['photoURL'] = (string)$data->get('avatarfull');
$userProfile['profileURL'] = (string)$data->get('profileurl');
$userProfile['country'] = (string)$data->get('loccountrycode');
return $userProfile;
}
/**
* Fetch user profile on community API
* @param $steam64
* @return array
*/
public function getUserProfileLegacyAPI($steam64)
{
libxml_use_internal_errors(false);
$apiUrl = 'http://steamcommunity.com/profiles/' . $steam64 . '/?xml=1';
$response = $this->httpClient->request($apiUrl);
$data = new \SimpleXMLElement($response);
$data = new Data\Collection($data);
$userProfile = [];
$userProfile['displayName'] = (string)$data->get('steamID');
$userProfile['firstName'] = (string)$data->get('realname');
$userProfile['photoURL'] = (string)$data->get('avatarFull');
$userProfile['description'] = (string)$data->get('summary');
$userProfile['region'] = (string)$data->get('location');
$userProfile['profileURL'] = (string)$data->get('customURL')
? 'http://steamcommunity.com/id/' . (string)$data->get('customURL')
: 'http://steamcommunity.com/profiles/' . $steam64;
return $userProfile;
}
}
PK ‚¹%UH]@ src/Provider/GitLab.phpnu €žÙ˜ 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('web_url');
$userProfile->email = $data->get('email');
$userProfile->webSiteURL = $data->get('website_url');
$userProfile->displayName = $userProfile->displayName ?: $data->get('username');
return $userProfile;
}
}
PK ‚¹%Ué.ŠMÀ À src/Provider/Blizzard.phpnu €žÙ˜ apiRequest('oauth/userinfo');
$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('battletag') ?: $data->get('login');
return $userProfile;
}
}
PK ‚¹%U—hˆ ˆ src/Provider/SteemConnect.phpnu €žÙ˜ apiRequest('api/me');
$data = new Data\Collection($response);
if (!$data->exists('result')) {
throw new UnexpectedApiResponseException('Provider API returned an unexpected response.');
}
$userProfile = new User\Profile();
$data = $data->filter('result');
$userProfile->identifier = $data->get('id');
$userProfile->description = $data->get('about');
$userProfile->photoURL = $data->get('profile_image');
$userProfile->webSiteURL = $data->get('website');
$userProfile->displayName = $data->get('name');
return $userProfile;
}
}
PK ‚¹%U¹˜C
src/Provider/AutoDesk.phpnu €žÙ˜ 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;
}
}
PK ‚¹%U…=ÛR src/index.htmlnu €žÙ˜ 403.PK ‚¹%UòdÜ src/Hybridauth.phpnu [y è 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 ($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();
}
}
}
}
PK ‚¹%U6ø´Ð«¯ «¯ % src/Thirdparty/OpenID/LightOpenID.phpnu €žÙ˜ = 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
* @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 / 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('#(.*?)#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('#\s*'.$ns.'(server|signon)\s* #s', $content, $type)) {
if ($type[1] == 'server') {
$this->identifier_select = true;
}
preg_match('#(.*)#', $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, 'http://openid.net/srv/ax/1.0 ');
$this->sreg = strpos($content, 'http://openid.net/sreg/1.0 ')
|| strpos($content, 'http://openid.net/extensions/sreg/1.1 ');
$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('#\s*'.$ns.'\s* #s', $content)) {
preg_match('#(.*)#', $content, $server);
preg_match('#<.*?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, 'http://openid.net/sreg/1.0 ')
|| strpos($content, 'http://openid.net/extensions/sreg/1.1 ');
$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;
}
}
PK ‚¹%U-÷iÓ Ó src/Thirdparty/OpenID/README.mdnu €žÙ˜ 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
PK ‚¹%U
|ßB B &