Merge branch 'master' into 1.0

Conflicts:
	README.md
	src/Provider/Facebook.php
	src/Provider/Vkontakte.php
	test/src/Provider/FacebookTest.php
1.0
Ben Ramsey 2015-02-24 10:41:43 -06:00
commit 5e500e44c6
9 changed files with 102 additions and 46 deletions

View File

@ -35,12 +35,12 @@ The following versions of PHP are supported.
### Authorization Code Flow ### Authorization Code Flow
```php ```php
$provider = new League\OAuth2\Client\Provider\<ProviderName>(array( $provider = new League\OAuth2\Client\Provider\<ProviderName>([
'clientId' => 'XXXXXXXX', 'clientId' => 'XXXXXXXX',
'clientSecret' => 'XXXXXXXX', 'clientSecret' => 'XXXXXXXX',
'redirectUri' => 'https://your-registered-redirect-uri/', 'redirectUri' => 'https://your-registered-redirect-uri/',
'scopes' => array('email', '...', '...'), 'scopes' => ['email', '...', '...'],
)); ]);
if (!isset($_GET['code'])) { if (!isset($_GET['code'])) {
@ -58,15 +58,9 @@ if (!isset($_GET['code'])) {
} else { } else {
// Try to get an access token (using the authorization code grant) // Try to get an access token (using the authorization code grant)
$token = $provider->getAccessToken('authorization_code', [ $token = $provider->getAccessToken('authorization_code', [
'code' => $_GET['code'] 'code' => $_GET['code']
]);
// If you are using Eventbrite you will need to add the grant_type parameter (see below)
$token = $provider->getAccessToken('authorization_code', [
'code' => $_GET['code'],
'grant_type' => 'authorization_code'
]); ]);
// Optional: Now you have a token you can look up a users profile data // Optional: Now you have a token you can look up a users profile data
@ -76,7 +70,7 @@ if (!isset($_GET['code'])) {
$userDetails = $provider->getUserDetails($token); $userDetails = $provider->getUserDetails($token);
// Use these details to create a new profile // Use these details to create a new profile
printf('Hello %s!', $userDetails->firstName); printf('Hello %s!', $userDetails->firstName);
} catch (Exception $e) { } catch (Exception $e) {
@ -98,11 +92,11 @@ if (!isset($_GET['code'])) {
### Refreshing a Token ### Refreshing a Token
```php ```php
$provider = new League\OAuth2\Client\Provider\<ProviderName>(array( $provider = new League\OAuth2\Client\Provider\<ProviderName>([
'clientId' => 'XXXXXXXX', 'clientId' => 'XXXXXXXX',
'clientSecret' => 'XXXXXXXX', 'clientSecret' => 'XXXXXXXX',
'redirectUri' => 'https://your-registered-redirect-uri/' 'redirectUri' => 'https://your-registered-redirect-uri/',
)); ]);
$grant = new \League\OAuth2\Client\Grant\RefreshToken(); $grant = new \League\OAuth2\Client\Grant\RefreshToken();
$token = $provider->getAccessToken($grant, ['refresh_token' => $refreshToken]); $token = $provider->getAccessToken($grant, ['refresh_token' => $refreshToken]);
@ -132,13 +126,18 @@ These providers allow integration with other providers not supported by `oauth2-
so please help them out with a pull request if you notice this. so please help them out with a pull request if you notice this.
- [Battle.net](https://packagist.org/packages/depotwarehouse/oauth2-bnet) - [Battle.net](https://packagist.org/packages/depotwarehouse/oauth2-bnet)
- [Dropbox](https://github.com/pixelfear/oauth2-dropbox)
- [Facebook](https://packagist.org/packages/league/oauth2-facebook)
- [FreeAgent](https://github.com/CloudManaged/oauth2-freeagent)
- [Google Nest](https://github.com/JC5/nest-oauth2-provider)
- [Mail.ru](https://packagist.org/packages/aego/oauth2-mailru) - [Mail.ru](https://packagist.org/packages/aego/oauth2-mailru)
- [Meetup](https://github.com/howlowck/meetup-oauth2-provider) - [Meetup](https://github.com/howlowck/meetup-oauth2-provider)
- [Odnoklassniki](https://packagist.org/packages/aego/oauth2-odnoklassniki)
- [Yandex](https://packagist.org/packages/aego/oauth2-yandex)
- [Vkontakte](https://packagist.org/packages/j4k/oauth2-vkontakte)
- [Naver](https://packagist.org/packages/deminoth/oauth2-naver) - [Naver](https://packagist.org/packages/deminoth/oauth2-naver)
- [Facebook](https://packagist.org/packages/league/oauth2-facebook) - [Odnoklassniki](https://packagist.org/packages/aego/oauth2-odnoklassniki)
- [Square](https://packagist.org/packages/wheniwork/oauth2-square)
- [Twitch.tv](https://github.com/tpavlek/oauth2-twitch)
- [Vkontakte](https://packagist.org/packages/j4k/oauth2-vkontakte)
- [Yandex](https://packagist.org/packages/aego/oauth2-yandex)
### Implementing your own provider ### Implementing your own provider

View File

@ -41,7 +41,7 @@
}, },
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "0.7.x-dev" "dev-master": "0.8.x-dev"
} }
} }
} }

View File

@ -128,7 +128,7 @@ abstract class AbstractProvider implements ProviderInterface
'state' => $this->state, 'state' => $this->state,
'scope' => is_array($this->scopes) ? implode($this->scopeSeparator, $this->scopes) : $this->scopes, 'scope' => is_array($this->scopes) ? implode($this->scopeSeparator, $this->scopes) : $this->scopes,
'response_type' => isset($options['response_type']) ? $options['response_type'] : 'code', 'response_type' => isset($options['response_type']) ? $options['response_type'] : 'code',
'approval_prompt' => 'auto', 'approval_prompt' => isset($options['approval_prompt']) ? $options['approval_prompt'] : 'auto',
]; ];
return $this->urlAuthorize().'?'.$this->httpBuildQuery($params, '', '&'); return $this->urlAuthorize().'?'.$this->httpBuildQuery($params, '', '&');
@ -199,8 +199,7 @@ abstract class AbstractProvider implements ProviderInterface
} }
} catch (BadResponseException $e) { } catch (BadResponseException $e) {
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
$raw_response = explode("\n", $e->getResponse()); $response = $e->getResponse()->getBody();
$response = end($raw_response);
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
} }
@ -220,11 +219,24 @@ abstract class AbstractProvider implements ProviderInterface
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
} }
$this->setResultUid($result); $result = $this->prepareAccessTokenResult($result);
return $grant->handleResponse($result); return $grant->handleResponse($result);
} }
/**
* Prepare the access token response for the grant. Custom mapping of
* expirations, etc should be done here.
*
* @param array $result
* @return array
*/
protected function prepareAccessTokenResult(array $result)
{
$this->setResultUid($result);
return $result;
}
/** /**
* Sets any result keys we've received matching our provider-defined uidKey to the key "uid". * Sets any result keys we've received matching our provider-defined uidKey to the key "uid".
* *

View File

@ -10,6 +10,8 @@ class Github extends AbstractProvider
public $domain = 'https://github.com'; public $domain = 'https://github.com';
public $apiDomain = 'https://api.github.com';
public function urlAuthorize() public function urlAuthorize()
{ {
return $this->domain.'/login/oauth/authorize'; return $this->domain.'/login/oauth/authorize';
@ -23,7 +25,7 @@ class Github extends AbstractProvider
public function urlUserDetails(\League\OAuth2\Client\Token\AccessToken $token) public function urlUserDetails(\League\OAuth2\Client\Token\AccessToken $token)
{ {
if ($this->domain === 'https://github.com') { if ($this->domain === 'https://github.com') {
return $this->domain.'/user?access_token='.$token; return $this->apiDomain.'/user?access_token='.$token;
} }
return $this->domain.'/api/v3/user?access_token='.$token; return $this->domain.'/api/v3/user?access_token='.$token;
} }
@ -31,7 +33,7 @@ class Github extends AbstractProvider
public function urlUserEmails(\League\OAuth2\Client\Token\AccessToken $token) public function urlUserEmails(\League\OAuth2\Client\Token\AccessToken $token)
{ {
if ($this->domain === 'https://github.com') { if ($this->domain === 'https://github.com') {
return $this->domain.'/user/emails?access_token='.$token; return $this->apiDomain.'/user/emails?access_token='.$token;
} }
return $this->domain.'/api/v3/user/emails?access_token='.$token; return $this->domain.'/api/v3/user/emails?access_token='.$token;
} }

View File

@ -29,6 +29,22 @@ class Google extends AbstractProvider
return $this->hostedDomain; return $this->hostedDomain;
} }
/**
* @var string If set, this will be sent to google as the "access_type" parameter.
* @link https://developers.google.com/accounts/docs/OAuth2WebServer#offline
*/
public $accessType = '';
public function setAccessType($accessType)
{
$this->accessType = $accessType;
}
public function getAccessType()
{
return $this->accessType;
}
public function urlAuthorize() public function urlAuthorize()
{ {
return 'https://accounts.google.com/o/oauth2/auth'; return 'https://accounts.google.com/o/oauth2/auth';
@ -43,7 +59,7 @@ class Google extends AbstractProvider
{ {
return return
'https://www.googleapis.com/plus/v1/people/me?'. 'https://www.googleapis.com/plus/v1/people/me?'.
'fields=name(familyName%2CgivenName)%2CdisplayName%2C'. 'fields=id%2Cname(familyName%2CgivenName)%2CdisplayName%2C'.
'emails%2Fvalue%2Cimage%2Furl&alt=json&access_token='.$token; 'emails%2Fvalue%2Cimage%2Furl&alt=json&access_token='.$token;
} }
@ -97,6 +113,10 @@ class Google extends AbstractProvider
$url .= '&' . $this->httpBuildQuery(['hd' => $this->hostedDomain]); $url .= '&' . $this->httpBuildQuery(['hd' => $this->hostedDomain]);
} }
if (!empty($this->accessType)) {
$url .= '&' . $this->httpBuildQuery(['access_type'=> $this->accessType]);
}
return $url; return $url;
} }
} }

View File

@ -43,17 +43,13 @@ class AccessToken
$this->accessToken = $options['access_token']; $this->accessToken = $options['access_token'];
// Some providers (not many) give the uid here, so lets take it if (!empty($options['uid'])) {
isset($options['uid']) and $this->uid = $options['uid']; $this->uid = $options['uid'];
}
// Vkontakte uses user_id instead of uid if (!empty($options['refresh_token'])) {
isset($options['user_id']) and $this->uid = $options['user_id']; $this->refreshToken = $options['refresh_token'];
}
// Mailru uses x_mailru_vid instead of uid
isset($options['x_mailru_vid']) and $this->uid = $options['x_mailru_vid'];
//Battle.net uses accountId instead of uid
isset($options['accountId']) and $this->uid = $options['accountId'];
// We need to know when the token expires. Show preference to // We need to know when the token expires. Show preference to
// 'expires_in' since it is defined in RFC6749 Section 5.1. // 'expires_in' since it is defined in RFC6749 Section 5.1.
@ -67,9 +63,6 @@ class AccessToken
$expiresInFuture = $expires > time(); $expiresInFuture = $expires > time();
$this->expires = $expiresInFuture ? $expires : time() + ((int) $expires); $this->expires = $expiresInFuture ? $expires : time() + ((int) $expires);
} }
// Grab a refresh token so we can update access tokens when they expires
isset($options['refresh_token']) and $this->refreshToken = $options['refresh_token'];
} }
/** /**

View File

@ -1,6 +1,6 @@
<?php <?php
namespace League\OAuth2\Client\Test\Token; namespace League\OAuth2\Client\Test\Entity;
use League\OAuth2\Client\Entity\User; use League\OAuth2\Client\Entity\User;

View File

@ -112,6 +112,23 @@ class GithubTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('mock_email', $this->provider->getUserEmail($token)); $this->assertEquals('mock_email', $this->provider->getUserEmail($token));
} }
public function testGithubDomainUrls()
{
$client = m::mock('Guzzle\Service\Client');
$response = m::mock('Guzzle\Http\Message\Response');
$response->shouldReceive('getBody')->times(1)->andReturn('access_token=mock_access_token&expires=3600&refresh_token=mock_refresh_token&otherKey={1234}');
$client->shouldReceive('setBaseUrl')->times(1);
$client->shouldReceive('post->send')->times(1)->andReturn($response);
$this->provider->setHttpClient($client);
$token = $this->provider->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']);
$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));
}
public function testGithubEnterpriseDomainUrls() public function testGithubEnterpriseDomainUrls()
{ {
$this->provider->domain = 'https://github.company.com'; $this->provider->domain = 'https://github.company.com';

View File

@ -15,6 +15,7 @@ class GoogleTest extends \PHPUnit_Framework_TestCase
'clientSecret' => 'mock_secret', 'clientSecret' => 'mock_secret',
'redirectUri' => 'none', 'redirectUri' => 'none',
'hostedDomain' => 'mock_domain', 'hostedDomain' => 'mock_domain',
'accessType' => 'mock_access_type'
]); ]);
} }
@ -37,6 +38,7 @@ class GoogleTest extends \PHPUnit_Framework_TestCase
$this->assertArrayHasKey('response_type', $query); $this->assertArrayHasKey('response_type', $query);
$this->assertArrayHasKey('approval_prompt', $query); $this->assertArrayHasKey('approval_prompt', $query);
$this->assertArrayHasKey('hd', $query); $this->assertArrayHasKey('hd', $query);
$this->assertArrayHasKey('access_type', $query);
$this->assertNotNull($this->provider->state); $this->assertNotNull($this->provider->state);
} }
@ -105,4 +107,15 @@ class GoogleTest extends \PHPUnit_Framework_TestCase
$this->provider->setHostedDomain('changed_domain'); $this->provider->setHostedDomain('changed_domain');
$this->assertEquals('changed_domain', $this->provider->hostedDomain); $this->assertEquals('changed_domain', $this->provider->hostedDomain);
} }
public function testGetAccessType()
{
$this->assertEquals('mock_access_type', $this->provider->getAccessType());
}
public function testSetAccessType()
{
$this->provider->setAccessType('changed_access_type');
$this->assertEquals('changed_access_type', $this->provider->accessType);
}
} }