diff --git a/README.md b/README.md index 1189563..3b533cb 100644 --- a/README.md +++ b/README.md @@ -35,12 +35,12 @@ The following versions of PHP are supported. ### Authorization Code Flow ```php -$provider = new League\OAuth2\Client\Provider\(array( - 'clientId' => 'XXXXXXXX', - 'clientSecret' => 'XXXXXXXX', - 'redirectUri' => 'https://your-registered-redirect-uri/', - 'scopes' => array('email', '...', '...'), -)); +$provider = new League\OAuth2\Client\Provider\([ + 'clientId' => 'XXXXXXXX', + 'clientSecret' => 'XXXXXXXX', + 'redirectUri' => 'https://your-registered-redirect-uri/', + 'scopes' => ['email', '...', '...'], +]); if (!isset($_GET['code'])) { @@ -58,15 +58,9 @@ if (!isset($_GET['code'])) { } 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', [ - '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' + 'code' => $_GET['code'] ]); // 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); // Use these details to create a new profile - printf('Hello %s!', $userDetails->firstName); + printf('Hello %s!', $userDetails->firstName); } catch (Exception $e) { @@ -98,11 +92,11 @@ if (!isset($_GET['code'])) { ### Refreshing a Token ```php -$provider = new League\OAuth2\Client\Provider\(array( - 'clientId' => 'XXXXXXXX', - 'clientSecret' => 'XXXXXXXX', - 'redirectUri' => 'https://your-registered-redirect-uri/' -)); +$provider = new League\OAuth2\Client\Provider\([ + 'clientId' => 'XXXXXXXX', + 'clientSecret' => 'XXXXXXXX', + 'redirectUri' => 'https://your-registered-redirect-uri/', +]); $grant = new \League\OAuth2\Client\Grant\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. - [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) - [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) -- [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 diff --git a/composer.json b/composer.json index 2e5797d..72ce5cc 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,7 @@ }, "extra": { "branch-alias": { - "dev-master": "0.7.x-dev" + "dev-master": "0.8.x-dev" } } } diff --git a/src/Provider/AbstractProvider.php b/src/Provider/AbstractProvider.php index 94271ff..90474b3 100644 --- a/src/Provider/AbstractProvider.php +++ b/src/Provider/AbstractProvider.php @@ -128,7 +128,7 @@ abstract class AbstractProvider implements ProviderInterface 'state' => $this->state, 'scope' => is_array($this->scopes) ? implode($this->scopeSeparator, $this->scopes) : $this->scopes, '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, '', '&'); @@ -199,8 +199,7 @@ abstract class AbstractProvider implements ProviderInterface } } catch (BadResponseException $e) { // @codeCoverageIgnoreStart - $raw_response = explode("\n", $e->getResponse()); - $response = end($raw_response); + $response = $e->getResponse()->getBody(); // @codeCoverageIgnoreEnd } @@ -220,11 +219,24 @@ abstract class AbstractProvider implements ProviderInterface // @codeCoverageIgnoreEnd } - $this->setResultUid($result); + $result = $this->prepareAccessTokenResult($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". * diff --git a/src/Provider/Github.php b/src/Provider/Github.php index b78727b..4dc01e2 100644 --- a/src/Provider/Github.php +++ b/src/Provider/Github.php @@ -10,6 +10,8 @@ class Github extends AbstractProvider public $domain = 'https://github.com'; + public $apiDomain = 'https://api.github.com'; + public function urlAuthorize() { return $this->domain.'/login/oauth/authorize'; @@ -23,7 +25,7 @@ class Github extends AbstractProvider public function urlUserDetails(\League\OAuth2\Client\Token\AccessToken $token) { 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; } @@ -31,7 +33,7 @@ class Github extends AbstractProvider public function urlUserEmails(\League\OAuth2\Client\Token\AccessToken $token) { 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; } diff --git a/src/Provider/Google.php b/src/Provider/Google.php index 30f5392..1896885 100644 --- a/src/Provider/Google.php +++ b/src/Provider/Google.php @@ -29,6 +29,22 @@ class Google extends AbstractProvider 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() { return 'https://accounts.google.com/o/oauth2/auth'; @@ -43,7 +59,7 @@ class Google extends AbstractProvider { return '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; } @@ -97,6 +113,10 @@ class Google extends AbstractProvider $url .= '&' . $this->httpBuildQuery(['hd' => $this->hostedDomain]); } + if (!empty($this->accessType)) { + $url .= '&' . $this->httpBuildQuery(['access_type'=> $this->accessType]); + } + return $url; } } diff --git a/src/Token/AccessToken.php b/src/Token/AccessToken.php index 12cdb67..bcfbfb1 100755 --- a/src/Token/AccessToken.php +++ b/src/Token/AccessToken.php @@ -43,17 +43,13 @@ class AccessToken $this->accessToken = $options['access_token']; - // Some providers (not many) give the uid here, so lets take it - isset($options['uid']) and $this->uid = $options['uid']; + if (!empty($options['uid'])) { + $this->uid = $options['uid']; + } - // Vkontakte uses user_id instead of uid - isset($options['user_id']) and $this->uid = $options['user_id']; - - // 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']; + if (!empty($options['refresh_token'])) { + $this->refreshToken = $options['refresh_token']; + } // We need to know when the token expires. Show preference to // 'expires_in' since it is defined in RFC6749 Section 5.1. @@ -67,9 +63,6 @@ class AccessToken $expiresInFuture = $expires > time(); $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']; } /** diff --git a/test/src/Entity/UserTest.php b/test/src/Entity/UserTest.php index 3a0670c..7772332 100644 --- a/test/src/Entity/UserTest.php +++ b/test/src/Entity/UserTest.php @@ -1,6 +1,6 @@ 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() { $this->provider->domain = 'https://github.company.com'; diff --git a/test/src/Provider/GoogleTest.php b/test/src/Provider/GoogleTest.php index 54c9c0a..8fca5f9 100644 --- a/test/src/Provider/GoogleTest.php +++ b/test/src/Provider/GoogleTest.php @@ -15,6 +15,7 @@ class GoogleTest extends \PHPUnit_Framework_TestCase 'clientSecret' => 'mock_secret', 'redirectUri' => 'none', 'hostedDomain' => 'mock_domain', + 'accessType' => 'mock_access_type' ]); } @@ -37,6 +38,7 @@ class GoogleTest extends \PHPUnit_Framework_TestCase $this->assertArrayHasKey('response_type', $query); $this->assertArrayHasKey('approval_prompt', $query); $this->assertArrayHasKey('hd', $query); + $this->assertArrayHasKey('access_type', $query); $this->assertNotNull($this->provider->state); } @@ -105,4 +107,15 @@ class GoogleTest extends \PHPUnit_Framework_TestCase $this->provider->setHostedDomain('changed_domain'); $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); + } }