Merge branch 'master' into 1.0

Conflicts:
	src/Provider/AbstractProvider.php
	test/src/Exception/IDPExceptionTest.php
	test/src/Provider/GithubTest.php
	test/src/Provider/GoogleTest.php
	test/src/Provider/LinkedInTest.php
1.0
Ben Ramsey 2015-03-10 16:38:02 -05:00
commit 299373fba6
14 changed files with 133 additions and 41 deletions

View File

@ -8,8 +8,8 @@ php:
before_script:
- travis_retry composer self-update
- travis_retry composer require satooshi/php-coveralls:dev-master --no-update --dev
- travis_retry composer install --no-interaction --prefer-source --dev
- travis_retry composer require satooshi/php-coveralls:dev-master --no-update
- travis_retry composer install --no-interaction --prefer-source
script:
- mkdir -p build/logs

View File

@ -138,6 +138,7 @@ so please help them out with a pull request if you notice this.
- [Twitch.tv](https://github.com/tpavlek/oauth2-twitch)
- [Vkontakte](https://packagist.org/packages/j4k/oauth2-vkontakte)
- [Yandex](https://packagist.org/packages/aego/oauth2-yandex)
- [ZenPayroll](https://packagist.org/packages/wheniwork/oauth2-zenpayroll)
### Implementing your own provider

View File

@ -12,10 +12,10 @@ class IDPException extends \Exception
$code = isset($result['code']) ? $result['code'] : 0;
if (isset($result['error'])) {
if (isset($result['error']) && $result['error'] !== '') {
// OAuth 2.0 Draft 10 style
$message = $result['error'];
} elseif (isset($result['message'])) {
} elseif (isset($result['message']) && $result['message'] !== '') {
// cURL style
$message = $result['message'];
} else {
@ -25,6 +25,11 @@ class IDPException extends \Exception
parent::__construct($message, $code);
}
public function getResponseBody()
{
return $this->result;
}
public function getType()
{
$result = 'Exception';

View File

@ -32,7 +32,9 @@ abstract class AbstractProvider implements ProviderInterface
public $responseType = 'json';
public $headers = null;
public $headers = [];
public $authorizationHeader;
/**
* @var HttpAdapterInterface
@ -325,15 +327,17 @@ abstract class AbstractProvider implements ProviderInterface
{
$url = $this->urlUserDetails($token);
return $this->fetchProviderData($url);
$headers = $this->getHeaders($token);
return $this->fetchProviderData($url, $headers);
}
protected function fetchProviderData($url)
protected function fetchProviderData($url, array $headers = [])
{
try {
$client = $this->getHttpClient();
$httpResponse = $client->get($url, $this->headers ?: []);
$httpResponse = $client->get($url, ['headers' => $headers]);
$response = (string) $httpResponse->getBody();
} catch (HttpAdapterException $e) {
@ -346,6 +350,24 @@ abstract class AbstractProvider implements ProviderInterface
return $response;
}
protected function getAuthorizationHeaders($token)
{
$headers = [];
if ($this->authorizationHeader) {
$headers['Authorization'] = $this->authorizationHeader . ' ' . $token;
}
return $headers;
}
public function getHeaders($token = null)
{
$headers = $this->headers;
if ($token) {
$headers = array_merge($headers, $this->getAuthorizationHeaders($token));
}
return $headers;
}
public function setRedirectHandler(Closure $handler)
{
$this->redirectHandler = $handler;

View File

@ -6,13 +6,7 @@ use League\OAuth2\Client\Entity\User;
class Eventbrite extends AbstractProvider
{
public function __construct($options)
{
parent::__construct($options);
$this->headers = [
'Authorization' => 'Bearer',
];
}
public $authorizationHeader = 'Bearer';
public function urlAuthorize()
{
@ -26,7 +20,7 @@ class Eventbrite extends AbstractProvider
public function urlUserDetails(\League\OAuth2\Client\Token\AccessToken $token)
{
return 'https://www.eventbrite.com/json/user_get?access_token='.$token;
return 'https://www.eventbrite.com/json/user_get';
}
public function userDetails($response, \League\OAuth2\Client\Token\AccessToken $token)

View File

@ -3,11 +3,14 @@
namespace League\OAuth2\Client\Provider;
use League\OAuth2\Client\Entity\User;
use League\OAuth2\Client\Token\AccessToken;
class Github extends AbstractProvider
{
public $responseType = 'string';
public $authorizationHeader = 'token';
public $domain = 'https://github.com';
public $apiDomain = 'https://api.github.com';
@ -22,23 +25,23 @@ class Github extends AbstractProvider
return $this->domain.'/login/oauth/access_token';
}
public function urlUserDetails(\League\OAuth2\Client\Token\AccessToken $token)
public function urlUserDetails(AccessToken $token)
{
if ($this->domain === 'https://github.com') {
return $this->apiDomain.'/user?access_token='.$token;
return $this->apiDomain.'/user';
}
return $this->domain.'/api/v3/user?access_token='.$token;
return $this->domain.'/api/v3/user';
}
public function urlUserEmails(\League\OAuth2\Client\Token\AccessToken $token)
public function urlUserEmails(AccessToken $token)
{
if ($this->domain === 'https://github.com') {
return $this->apiDomain.'/user/emails?access_token='.$token;
return $this->apiDomain.'/user/emails';
}
return $this->domain.'/api/v3/user/emails?access_token='.$token;
return $this->domain.'/api/v3/user/emails';
}
public function userDetails($response, \League\OAuth2\Client\Token\AccessToken $token)
public function userDetails($response, AccessToken $token)
{
$user = new User();
@ -58,37 +61,39 @@ class Github extends AbstractProvider
return $user;
}
public function userUid($response, \League\OAuth2\Client\Token\AccessToken $token)
public function userUid($response, AccessToken $token)
{
return $response->id;
}
public function getUserEmails(\League\OAuth2\Client\Token\AccessToken $token)
public function getUserEmails(AccessToken $token)
{
$response = $this->fetchUserEmails($token);
return $this->userEmails(json_decode($response), $token);
}
public function userEmail($response, \League\OAuth2\Client\Token\AccessToken $token)
public function userEmail($response, AccessToken $token)
{
return isset($response->email) && $response->email ? $response->email : null;
}
public function userEmails($response, \League\OAuth2\Client\Token\AccessToken $token)
public function userEmails($response, AccessToken $token)
{
return $response;
}
public function userScreenName($response, \League\OAuth2\Client\Token\AccessToken $token)
public function userScreenName($response, AccessToken $token)
{
return $response->name;
}
protected function fetchUserEmails(\League\OAuth2\Client\Token\AccessToken $token)
protected function fetchUserEmails(AccessToken $token)
{
$url = $this->urlUserEmails($token);
return $this->fetchProviderData($url);
$headers = $this->getHeaders($token);
return $this->fetchProviderData($url, $headers);
}
}

View File

@ -13,6 +13,8 @@ class Google extends AbstractProvider
'email',
];
public $authorizationHeader = 'OAuth';
/**
* @var string If set, this will be sent to google as the "hd" parameter.
* @link https://developers.google.com/accounts/docs/OAuth2Login#hd-param
@ -60,7 +62,7 @@ class Google extends AbstractProvider
return
'https://www.googleapis.com/plus/v1/people/me?'.
'fields=id%2Cname(familyName%2CgivenName)%2CdisplayName%2C'.
'emails%2Fvalue%2Cimage%2Furl&alt=json&access_token='.$token;
'emails%2Fvalue%2Cimage%2Furl&alt=json';
}
public function userDetails($response, \League\OAuth2\Client\Token\AccessToken $token)

View File

@ -9,6 +9,7 @@ class LinkedIn extends AbstractProvider
{
public $scopes = ['r_basicprofile r_emailaddress r_contactinfo'];
public $responseType = 'json';
public $authorizationHeader = 'Bearer';
public $fields = [
'id', 'email-address', 'first-name', 'last-name', 'headline',
'location', 'industry', 'picture-url', 'public-profile-url',
@ -26,8 +27,8 @@ class LinkedIn extends AbstractProvider
public function urlUserDetails(AccessToken $token)
{
return 'https://api.linkedin.com/v1/people/~:('.implode(",", $this->fields)
.')?format=json&oauth2_access_token='.$token;
$fields = implode(',', $this->fields);
return 'https://api.linkedin.com/v1/people/~:(' . $fields . ')?format=json';
}
public function userDetails($response, AccessToken $token)

View File

@ -24,6 +24,8 @@ interface ProviderInterface
public function getAccessToken($grant = 'authorization_code', $params = []);
public function getHeaders($token = null);
public function getUserDetails(AccessToken $token);
public function getUserUid(AccessToken $token);

View File

@ -41,4 +41,41 @@ class IDPExceptionTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('message: 404: message', (string)$exception);
}
public function testGetResponseBody()
{
$exception = new IDPException(array('error' => 'message', 'code' => 404));
$this->assertEquals(
[
'error' => 'message',
'code' => 404
],
$exception->getResponseBody()
);
}
public function testEmptyMessage()
{
$exception = new IDPException(array('error' => 'error_message', 'message' => ''));
$this->assertEquals('error_message', $exception->getMessage());
}
public function testNonEmptyErrorAndMessage()
{
$exception = new IDPException(array('error' => 'error_message', 'message' => 'message'));
$this->assertEquals('error_message', $exception->getMessage());
}
public function testEmptyError()
{
$exception = new IDPException(array('error' => '', 'message' => 'message'));
$this->assertEquals('message', $exception->getMessage());
}
public function testEmptyErrorAndMessage()
{
$exception = new IDPException(array('error' => '', 'message' => ''));
$this->assertEquals('Unknown Error.', $exception->getMessage());
}
}

View File

@ -69,6 +69,7 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
'scopeSeparator' => ';',
'responseType' => 'csv',
'headers' => ['Foo' => 'Bar'],
'authorizationHeader' => 'Bearer',
];
$mockProvider = new MockProvider($options);
@ -150,6 +151,32 @@ class AbstractProviderTest extends \PHPUnit_Framework_TestCase
[$response3],
];
}
public function getHeadersTest()
{
$provider = $this->getMockForAbstractClass(
'\League\OAuth2\Client\Provider\AbstractProvider',
[
[
'clientId' => 'mock_client_id',
'clientSecret' => 'mock_secret',
'redirectUri' => 'none',
]
]
);
/**
* @var $provider AbstractProvider
*/
$this->assertEquals([], $provider->getHeaders());
$this->assertEquals([], $provider->getHeaders('mock_token'));
$provider->authorizationHeader = 'Bearer';
$this->assertEquals(['Authorization' => 'Bearer abc'], $provider->getHeaders('abc'));
$token = new AccessToken(['access_token' => 'xyz', 'expires_in' => 3600]);
$this->assertEquals(['Authorization' => 'Bearer xyz'], $provider->getHeaders($token));
}
}
class MockProvider extends \League\OAuth2\Client\Provider\AbstractProvider

View File

@ -114,8 +114,8 @@ class GithubTest extends ConcreteProviderTest
$this->assertEquals($this->provider->domain.'/login/oauth/authorize', $this->provider->urlAuthorize());
$this->assertEquals($this->provider->domain.'/login/oauth/access_token', $this->provider->urlAccessToken());
$this->assertEquals($this->provider->apiDomain.'/user?access_token=mock_access_token', $this->provider->urlUserDetails($token));
$this->assertEquals($this->provider->apiDomain.'/user/emails?access_token=mock_access_token', $this->provider->urlUserEmails($token));
$this->assertEquals($this->provider->apiDomain.'/user', $this->provider->urlUserDetails($token));
$this->assertEquals($this->provider->apiDomain.'/user/emails', $this->provider->urlUserEmails($token));
}
public function testGithubEnterpriseDomainUrls()
@ -130,8 +130,8 @@ class GithubTest extends ConcreteProviderTest
$this->assertEquals($this->provider->domain.'/login/oauth/authorize', $this->provider->urlAuthorize());
$this->assertEquals($this->provider->domain.'/login/oauth/access_token', $this->provider->urlAccessToken());
$this->assertEquals($this->provider->domain.'/api/v3/user?access_token=mock_access_token', $this->provider->urlUserDetails($token));
$this->assertEquals($this->provider->domain.'/api/v3/user/emails?access_token=mock_access_token', $this->provider->urlUserEmails($token));
$this->assertEquals($this->provider->domain.'/api/v3/user', $this->provider->urlUserDetails($token));
$this->assertEquals($this->provider->domain.'/api/v3/user/emails', $this->provider->urlUserEmails($token));
}
public function testUserEmails()

View File

@ -56,8 +56,6 @@ class GoogleTest extends ConcreteProviderTest
$token = $this->provider->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']);
# print_r($token);die();
$this->assertEquals('mock_access_token', $token->accessToken);
$this->assertLessThanOrEqual(time() + 3600, $token->expires);
$this->assertGreaterThanOrEqual(time(), $token->expires);

View File

@ -49,8 +49,6 @@ class LinkedInTest extends ConcreteProviderTest
$token = $this->provider->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']);
# print_r($token);die();
$this->assertEquals('mock_access_token', $token->accessToken);
$this->assertLessThanOrEqual(time() + 3600, $token->expires);
$this->assertGreaterThanOrEqual(time(), $token->expires);