added socialite and some hacks via vendor sideload to add social login
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
/vendor
|
||||
composer.phar
|
||||
composer.lock
|
||||
.DS_Store
|
||||
@@ -0,0 +1,11 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 5.5
|
||||
- 5.6
|
||||
|
||||
before_script:
|
||||
- curl -s http://getcomposer.org/installer | php
|
||||
- php composer.phar install --dev
|
||||
|
||||
script: phpunit
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "socialnorm/facebook",
|
||||
"description": "Facebook provider for SocialNorm",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Adam Wathan",
|
||||
"email": "adam.wathan@gmail.com"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/adamwathan/socialnorm"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.5.0",
|
||||
"guzzlehttp/guzzle": "^6.0",
|
||||
"socialnorm/socialnorm": "^0.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "~0.8",
|
||||
"phpunit/phpunit": "^4.8"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SocialNorm\\Facebook\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"files": [
|
||||
"tests/TestCase.php"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Package Test Suite">
|
||||
<directory suffix="Test.php">./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
@@ -0,0 +1,3 @@
|
||||
## SocialNorm Facebook Provider
|
||||
|
||||
@todo: Add docs :)
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php namespace SocialNorm\Facebook;
|
||||
|
||||
use SocialNorm\Exceptions\InvalidAuthorizationCodeException;
|
||||
use SocialNorm\Providers\OAuth2Provider;
|
||||
|
||||
class FacebookProvider extends OAuth2Provider
|
||||
{
|
||||
protected $authorizeUrl = "https://www.facebook.com/dialog/oauth";
|
||||
protected $accessTokenUrl = "https://graph.facebook.com/v2.4/oauth/access_token";
|
||||
protected $userDataUrl = "https://graph.facebook.com/v2.4/me";
|
||||
protected $scope = [
|
||||
'email',
|
||||
];
|
||||
protected $fields = [
|
||||
'email',
|
||||
'name',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'age_range',
|
||||
'timezone',
|
||||
];
|
||||
|
||||
protected function getAuthorizeUrl()
|
||||
{
|
||||
return $this->authorizeUrl;
|
||||
}
|
||||
|
||||
protected function getAccessTokenBaseUrl()
|
||||
{
|
||||
return $this->accessTokenUrl;
|
||||
}
|
||||
|
||||
protected function getUserDataUrl()
|
||||
{
|
||||
return $this->userDataUrl;
|
||||
}
|
||||
|
||||
protected function buildUserDataUrl()
|
||||
{
|
||||
$url = $this->getUserDataUrl();
|
||||
$url .= "?access_token={$this->accessToken}";
|
||||
$url .= "&fields=" . implode(',', $this->fields);
|
||||
return $url;
|
||||
}
|
||||
|
||||
protected function requestAccessToken()
|
||||
{
|
||||
$url = $this->getAccessTokenBaseUrl();
|
||||
try {
|
||||
$response = $this->httpClient->get($url, [
|
||||
'query' => [
|
||||
'client_id' => $this->clientId,
|
||||
'client_secret' => $this->clientSecret,
|
||||
'redirect_uri' => $this->redirectUri(),
|
||||
'code' => $this->request->authorizationCode(),
|
||||
],
|
||||
]);
|
||||
} catch (BadResponseException $e) {
|
||||
throw new InvalidAuthorizationCodeException((string) $e->getResponse());
|
||||
}
|
||||
return $this->parseTokenResponse((string) $response->getBody());
|
||||
}
|
||||
|
||||
protected function parseTokenResponse($response)
|
||||
{
|
||||
return $this->parseJsonTokenResponse($response);
|
||||
}
|
||||
|
||||
protected function parseUserDataResponse($response)
|
||||
{
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
protected function userId()
|
||||
{
|
||||
return $this->getProviderUserData('id');
|
||||
}
|
||||
|
||||
protected function nickname()
|
||||
{
|
||||
return $this->getProviderUserData('name');
|
||||
}
|
||||
|
||||
protected function fullName()
|
||||
{
|
||||
return $this->getProviderUserData('name');
|
||||
}
|
||||
|
||||
protected function avatar()
|
||||
{
|
||||
return 'https://graph.facebook.com/v2.4/'.$this->userId().'/picture';
|
||||
}
|
||||
|
||||
protected function email()
|
||||
{
|
||||
return $this->getProviderUserData('email');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
use Mockery as M;
|
||||
use SocialNorm\Facebook\FacebookProvider;
|
||||
use SocialNorm\Request;
|
||||
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\Client as HttpClient;
|
||||
|
||||
class FacebookProviderTest extends TestCase
|
||||
{
|
||||
private function getStubbedHttpClient($fixtures = [])
|
||||
{
|
||||
$mock = new MockHandler($this->createResponses($fixtures));
|
||||
$handler = HandlerStack::create($mock);
|
||||
return new HttpClient(['handler' => $handler]);
|
||||
}
|
||||
|
||||
private function createResponses($fixtures)
|
||||
{
|
||||
$responses = [];
|
||||
foreach ($fixtures as $fixture) {
|
||||
$response = require $fixture;
|
||||
$responses[] = new Response($response['status'], $response['headers'], $response['body']);
|
||||
}
|
||||
|
||||
return $responses;
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_can_retrieve_a_normalized_user()
|
||||
{
|
||||
$client = $this->getStubbedHttpClient([
|
||||
__DIR__ . '/_fixtures/facebook_accesstoken.php',
|
||||
__DIR__ . '/_fixtures/facebook_user.php',
|
||||
]);
|
||||
|
||||
$provider = new FacebookProvider([
|
||||
'client_id' => 'abcdefgh',
|
||||
'client_secret' => '12345678',
|
||||
'redirect_uri' => 'http://example.com/login',
|
||||
], $client, new Request(['code' => 'abc123']));
|
||||
|
||||
$user = $provider->getUser();
|
||||
|
||||
$this->assertEquals('187903669', $user->id);
|
||||
$this->assertEquals('John Doe', $user->nickname);
|
||||
$this->assertEquals('John Doe', $user->full_name);
|
||||
$this->assertEquals('john@example.com', $user->email);
|
||||
$this->assertEquals('https://graph.facebook.com/v2.4/187903669/picture', $user->avatar);
|
||||
$this->assertEquals(
|
||||
'RUXRIAxWwxVYKk3b1vrTACPUiAGImrszVsBXb2FQZZZXbd6JNzkZRAgZLCdAiCfKHrPanMTS8BAHLPqugidBcCNkUmz3y72XMZRZWw4SEGdczB2HygsA7oQOufDIbgZBtyA1KaznugApacfId5HIdZtIEh47ZLEa0BrJrBICZBf4uCWCGD5OBM40RpvTVaAux2vCv5wU9ZZzm91WAVtSC5ufoZmr3Ty',
|
||||
$user->access_token
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @expectedException SocialNorm\Exceptions\ApplicationRejectedException
|
||||
*/
|
||||
public function it_fails_to_retrieve_a_user_when_the_authorization_code_is_omitted()
|
||||
{
|
||||
$client = $this->getStubbedHttpClient([
|
||||
__DIR__ . '/_fixtures/facebook_accesstoken.php',
|
||||
__DIR__ . '/_fixtures/facebook_user.php',
|
||||
]);
|
||||
|
||||
$provider = new FacebookProvider([
|
||||
'client_id' => 'abcdefgh',
|
||||
'client_secret' => '12345678',
|
||||
'redirect_uri' => 'http://example.com/login',
|
||||
], $client, new Request([]));
|
||||
|
||||
$user = $provider->getUser();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
use Mockery as M;
|
||||
|
||||
class TestCase extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function tearDown()
|
||||
{
|
||||
M::close();
|
||||
parent::tearDown();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'status' => 200,
|
||||
'headers' => [
|
||||
'Content-Type' => 'application/json; charset=UTF-8',
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'X-FB-Rev' => '1669049',
|
||||
'Pragma' => 'no-cache',
|
||||
'Cache-Control' => 'private, no-cache, no-store, must-revalidate',
|
||||
'Facebook-API-Version' => 'v2.3',
|
||||
'Expires' => 'Sat, 01 Jan 2000 00:00:00 GMT',
|
||||
'X-FB-Debug' => 'aWKI40XqPBE1YkeMX7Awln6RzgWl+JQpbYY7MWkiP2cRZ0TNadSG8rTnlHQovxjP+5fTYg1ZfkOVKsiMdJB9Jg==',
|
||||
'Date' => 'Wed, 01 Apr 2015 12:47:40 GMT',
|
||||
'Connection' => 'keep-alive',
|
||||
'Content-Length' => '285'
|
||||
],
|
||||
'body' => '{"access_token":"RUXRIAxWwxVYKk3b1vrTACPUiAGImrszVsBXb2FQZZZXbd6JNzkZRAgZLCdAiCfKHrPanMTS8BAHLPqugidBcCNkUmz3y72XMZRZWw4SEGdczB2HygsA7oQOufDIbgZBtyA1KaznugApacfId5HIdZtIEh47ZLEa0BrJrBICZBf4uCWCGD5OBM40RpvTVaAux2vCv5wU9ZZzm91WAVtSC5ufoZmr3Ty","token_type":"bearer","expires_in":5183288}'
|
||||
];
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'status' => 200,
|
||||
'headers' => [
|
||||
'Content-Type' => 'text/javascript; charset=UTF-8',
|
||||
'Last-Modified' => '2014-12-21T00:04:42+0000',
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'X-FB-Rev' => '1669049',
|
||||
'ETag' => '"e732c03dd9c5d6f0a2aa6bb7b2e78e4e36bf5a4e"',
|
||||
'Pragma' => 'no-cache',
|
||||
'Cache-Control' => 'private, no-cache, no-store, must-revalidate',
|
||||
'Facebook-API-Version' => 'v2.3',
|
||||
'Expires' => 'Sat, 01 Jan 2000 00:00:00 GMT',
|
||||
'X-FB-Debug' => 'WkWb9eeV7Aw1YJagx9Ffj0XzEQuDZhaUlOsNLGbf+es8H5Tq2aW4iBn6G0tUh1WRTG4Rbd+nIaTTR1Mbhz2b8Q==',
|
||||
'Date' => 'Wed, 01 Apr 2015 12:49:00 GMT',
|
||||
'Connection' => 'keep-alive',
|
||||
'Content-Length' => '295'
|
||||
],
|
||||
'body' => '{"id":"187903669","birthday":"03\/15\/1987","email":"john\u0040example.com","first_name":"John","gender":"male","last_name":"Doe","link":"http:\/\/www.facebook.com\/187903669","locale":"en_US","name":"John Doe","timezone":-4,"updated_time":"2014-12-21T00:04:42+0000","verified":true}'
|
||||
];
|
||||
@@ -0,0 +1,4 @@
|
||||
/vendor
|
||||
composer.phar
|
||||
composer.lock
|
||||
.DS_Store
|
||||
@@ -0,0 +1,11 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 5.5
|
||||
- 5.6
|
||||
|
||||
before_script:
|
||||
- curl -s http://getcomposer.org/installer | php
|
||||
- php composer.phar install --dev
|
||||
|
||||
script: phpunit
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "socialnorm/github",
|
||||
"description": "GitHub provider for SocialNorm",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Adam Wathan",
|
||||
"email": "adam.wathan@gmail.com"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": ">=5.5.0",
|
||||
"guzzlehttp/guzzle": "^6.0",
|
||||
"socialnorm/socialnorm": "^0.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "~0.8",
|
||||
"phpunit/phpunit": "^4.8"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SocialNorm\\GitHub\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"files": [
|
||||
"tests/TestCase.php"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Package Test Suite">
|
||||
<directory suffix="Test.php">./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
@@ -0,0 +1,3 @@
|
||||
## SocialNorm GitHub Provider
|
||||
|
||||
@todo: Add docs :)
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php namespace SocialNorm\GitHub;
|
||||
|
||||
use SocialNorm\Exceptions\InvalidAuthorizationCodeException;
|
||||
use SocialNorm\Providers\OAuth2Provider;
|
||||
|
||||
class GitHubProvider extends OAuth2Provider
|
||||
{
|
||||
protected $authorizeUrl = "https://github.com/login/oauth/authorize";
|
||||
protected $accessTokenUrl = "https://github.com/login/oauth/access_token";
|
||||
protected $userDataUrl = "https://api.github.com/user";
|
||||
protected $scope = [
|
||||
'user:email',
|
||||
];
|
||||
|
||||
protected $headers = [
|
||||
'authorize' => [],
|
||||
'access_token' => [
|
||||
'Accept' => 'application/json'
|
||||
],
|
||||
'user_details' => [
|
||||
'Accept' => 'application/vnd.github.v3'
|
||||
],
|
||||
];
|
||||
|
||||
protected function getAuthorizeUrl()
|
||||
{
|
||||
return $this->authorizeUrl;
|
||||
}
|
||||
|
||||
protected function getAccessTokenBaseUrl()
|
||||
{
|
||||
return $this->accessTokenUrl;
|
||||
}
|
||||
|
||||
protected function getUserDataUrl()
|
||||
{
|
||||
return $this->userDataUrl;
|
||||
}
|
||||
|
||||
protected function parseTokenResponse($response)
|
||||
{
|
||||
return $this->parseJsonTokenResponse($response);
|
||||
}
|
||||
|
||||
protected function requestUserData()
|
||||
{
|
||||
$userData = parent::requestUserData();
|
||||
$userData['email'] = $this->requestEmail();
|
||||
return $userData;
|
||||
}
|
||||
|
||||
protected function requestEmail()
|
||||
{
|
||||
$url = $this->getEmailUrl();
|
||||
$emails = $this->getJson($url, $this->headers['user_details']);
|
||||
return $this->getPrimaryEmail($emails);
|
||||
}
|
||||
|
||||
protected function getEmailUrl()
|
||||
{
|
||||
$url = $this->getUserDataUrl() .'/emails';
|
||||
$url .= "?access_token=".$this->accessToken;
|
||||
return $url;
|
||||
}
|
||||
|
||||
public function getJson($url, $headers)
|
||||
{
|
||||
$response = $this->httpClient->get($url, ['headers' => $headers]);
|
||||
return json_decode($response->getBody(), true);
|
||||
}
|
||||
|
||||
protected function getPrimaryEmail($emails)
|
||||
{
|
||||
foreach ($emails as $email) {
|
||||
if ($email['primary']) {
|
||||
return $email['email'];
|
||||
}
|
||||
}
|
||||
return $emails[0]['email'];
|
||||
}
|
||||
|
||||
protected function parseUserDataResponse($response)
|
||||
{
|
||||
$data = json_decode($response, true);
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function userId()
|
||||
{
|
||||
return $this->getProviderUserData('id');
|
||||
}
|
||||
|
||||
protected function nickname()
|
||||
{
|
||||
return $this->getProviderUserData('login');
|
||||
}
|
||||
|
||||
protected function fullName()
|
||||
{
|
||||
return $this->getProviderUserData('name');
|
||||
}
|
||||
|
||||
protected function avatar()
|
||||
{
|
||||
return $this->getProviderUserData('avatar_url');
|
||||
}
|
||||
|
||||
protected function email()
|
||||
{
|
||||
return $this->getProviderUserData('email');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
use Mockery as M;
|
||||
use SocialNorm\GitHub\GitHubProvider;
|
||||
use SocialNorm\Request;
|
||||
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\Client as HttpClient;
|
||||
|
||||
class GitHubProviderTest extends TestCase
|
||||
{
|
||||
private function getStubbedHttpClient($fixtures = [])
|
||||
{
|
||||
$mock = new MockHandler($this->createResponses($fixtures));
|
||||
$handler = HandlerStack::create($mock);
|
||||
return new HttpClient(['handler' => $handler]);
|
||||
}
|
||||
|
||||
private function createResponses($fixtures)
|
||||
{
|
||||
$responses = [];
|
||||
foreach ($fixtures as $fixture) {
|
||||
$response = require $fixture;
|
||||
$responses[] = new Response($response['status'], $response['headers'], $response['body']);
|
||||
}
|
||||
|
||||
return $responses;
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_can_retrieve_a_normalized_user()
|
||||
{
|
||||
$client = $this->getStubbedHttpClient([
|
||||
__DIR__ . '/_fixtures/github_accesstoken.php',
|
||||
__DIR__ . '/_fixtures/github_user.php',
|
||||
__DIR__ . '/_fixtures/github_email.php',
|
||||
]);
|
||||
|
||||
$provider = new GitHubProvider([
|
||||
'client_id' => 'abcdefgh',
|
||||
'client_secret' => '12345678',
|
||||
'redirect_uri' => 'http://example.com/login',
|
||||
], $client, new Request(['code' => 'abc123']));
|
||||
|
||||
$user = $provider->getUser();
|
||||
|
||||
$this->assertEquals('4323180', $user->id);
|
||||
$this->assertEquals('adamwathan', $user->nickname);
|
||||
$this->assertEquals('Adam Wathan', $user->full_name);
|
||||
$this->assertEquals('adam@example.com', $user->email);
|
||||
$this->assertEquals('https://avatars.githubusercontent.com/u/4323180?v=3', $user->avatar);
|
||||
$this->assertEquals('abcdefgh12345678', $user->access_token);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @expectedException SocialNorm\Exceptions\ApplicationRejectedException
|
||||
*/
|
||||
public function it_fails_to_retrieve_a_user_when_the_authorization_code_is_omitted()
|
||||
{
|
||||
$client = $this->getStubbedHttpClient([
|
||||
__DIR__ . '/_fixtures/github_accesstoken.php',
|
||||
__DIR__ . '/_fixtures/github_user.php',
|
||||
__DIR__ . '/_fixtures/github_email.php',
|
||||
]);
|
||||
|
||||
$provider = new GitHubProvider([
|
||||
'client_id' => 'abcdefgh',
|
||||
'client_secret' => '12345678',
|
||||
'redirect_uri' => 'http://example.com/login',
|
||||
], $client, new Request([]));
|
||||
|
||||
$user = $provider->getUser();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
use Mockery as M;
|
||||
|
||||
class TestCase extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function tearDown()
|
||||
{
|
||||
M::close();
|
||||
parent::tearDown();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'status' => 200,
|
||||
'headers' => [
|
||||
'Server' => 'GitHub.com',
|
||||
'Date' => 'Sat, 21 Feb 2015 18:44:34 GMT',
|
||||
'Content-Type' => 'application/json; charset=utf-8',
|
||||
'Transfer-Encoding' => 'chunked',
|
||||
'Status' => '200 OK',
|
||||
'Content-Security-Policy' => 'default-src *; script-src assets-cdn.github.com collector-cdn.github.com; object-src assets-cdn.github.com; style-src \'self\' \'unsafe-inline\' \'unsafe-eval\' assets-cdn.github.com; img-src \'self\' data: assets-cdn.github.com identicons.github.com www.google-analytics.com collector.githubapp.com *.githubusercontent.com *.gravatar.com *.wp.com; media-src \'none\'; frame-src \'self\' render.githubusercontent.com gist.github.com www.youtube.com player.vimeo.com checkout.paypal.com; font-src assets-cdn.github.com; connect-src \'self\' ghconduit.com:25035 live.github.com wss://live.github.com uploads.github.com www.google-analytics.com s3.amazonaws.com',
|
||||
'Cache-Control' => 'no-cache',
|
||||
'Vary' => 'X-PJAX, Accept-Encoding',
|
||||
'X-UA-Compatible' => 'IE=Edge,chrome=1',
|
||||
'Set-Cookie' => 'logged_in=no; domain=.github.com; path=/; expires=Wed, 21-Feb-2035 18:44:34 GMT; secure; HttpOnly, _gh_sess=eyJzZXNzaW9uX2lkIjoiNDZkZDdiMTMzNDIxMjQ5OTNjZjliNmUyMzg4OTM5MWUiLCJsYXN0X3dyaXRlIjoxNDI0NTQ0Mjc0NDgzfQ%3D%3D--4ca192ff94067bcf8922b053b0758d1f580f85a6; path=/; secure; HttpOnly',
|
||||
'X-Request-Id' => 'f8898bcf19b20706de5712177bdf9eeb',
|
||||
'X-Runtime' => '0.010527',
|
||||
'X-Rack-Cache' => 'invalidate, pass',
|
||||
'X-GitHub-Request-Id' => 'AE71B20F:0FF4:159043E3:54E8D212',
|
||||
'Strict-Transport-Security' => 'max-age=31536000; includeSubdomains; preload',
|
||||
'X-Content-Type-Options' => 'nosniff',
|
||||
'X-XSS-Protection' => '1; mode=block',
|
||||
'X-Frame-Options' => 'deny',
|
||||
'X-Served-By' => 'a568c03544f42dddf712bab3bfd562fd'
|
||||
],
|
||||
'body' => '{"access_token":"abcdefgh12345678","token_type":"bearer","scope":"user:email"}'
|
||||
];
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'status' => 200,
|
||||
'headers' => [
|
||||
'Server' => 'GitHub.com',
|
||||
'Date' => 'Thu, 19 Mar 2015 03:42:05 GMT',
|
||||
'Content-Type' => 'application/json; charset=utf-8',
|
||||
'Content-Length' => '62',
|
||||
'Status' => '200 OK',
|
||||
'X-RateLimit-Limit' => '5000',
|
||||
'X-RateLimit-Remaining' => '4948',
|
||||
'X-RateLimit-Reset' => '1426740125',
|
||||
'Cache-Control' => 'private, max-age=60, s-maxage=60',
|
||||
'ETag' => '"06ddc2c2d17761bc98b0e6419aed512c"',
|
||||
'X-OAuth-Scopes' => 'user:email',
|
||||
'X-Accepted-OAuth-Scopes' => 'user, user:email',
|
||||
'X-OAuth-Client-Id' => 'b80ba34640eb08f2b3e5',
|
||||
'Vary' => 'Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding',
|
||||
'X-GitHub-Media-Type' => 'github.v3',
|
||||
'X-XSS-Protection' => '1; mode=block',
|
||||
'X-Frame-Options' => 'deny',
|
||||
'Content-Security-Policy' => 'default-src \'none\'',
|
||||
'Access-Control-Allow-Credentials' => 'true',
|
||||
'Access-Control-Expose-Headers' => 'ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval',
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'X-GitHub-Request-Id' => 'AE71B20F:4EF8:83D06C:550A458D',
|
||||
'Strict-Transport-Security' => 'max-age=31536000; includeSubdomains; preload',
|
||||
'X-Content-Type-Options' => 'nosniff',
|
||||
'X-Served-By' => 'b0ef53392caa42315c6206737946d931'
|
||||
],
|
||||
'body' => '[{"email": "adam@example.com","primary": true,"verified": true}]'
|
||||
];
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'status' => 200,
|
||||
'headers' => [
|
||||
'Server' => 'GitHub.com',
|
||||
'Date' => 'Sat, 21 Feb 2015 18:43:17 GMT',
|
||||
'Content-Type' => 'application/json; charset=utf-8',
|
||||
'Content-Length' => '1278',
|
||||
'Status' => '200 OK',
|
||||
'X-RateLimit-Limit' => '5000',
|
||||
'X-RateLimit-Remaining' => '4961',
|
||||
'X-RateLimit-Reset' => '1424546049',
|
||||
'Cache-Control' => 'private, max-age=60, s-maxage=60',
|
||||
'Last-Modified' => 'Sat, 21 Feb 2015 17:06:00 GMT',
|
||||
'ETag' => '"7a29c845b431fa302144d2d2da66e7e3"',
|
||||
'X-OAuth-Scopes' => 'user:email',
|
||||
'X-Accepted-OAuth-Scopes' => '',
|
||||
'X-OAuth-Client-Id' => 'b80ba34640eb08f2b3e5',
|
||||
'Vary' => 'Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding',
|
||||
'X-GitHub-Media-Type' => 'github.v3',
|
||||
'X-XSS-Protection' => '1; mode=block',
|
||||
'X-Frame-Options' => 'deny',
|
||||
'Content-Security-Policy' => 'default-src \'none\'',
|
||||
'Access-Control-Allow-Credentials' => 'true',
|
||||
'Access-Control-Expose-Headers' => 'ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval',
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'X-GitHub-Request-Id' => 'AE71B20F:202B:3ABE9811:54E8D1C5',
|
||||
'Strict-Transport-Security' => 'max-age=31536000; includeSubdomains; preload',
|
||||
'X-Content-Type-Options' => 'nosniff',
|
||||
'X-Served-By' => '065b43cd9674091fec48a221b420fbb3'
|
||||
],
|
||||
'body' => '{"login":"adamwathan","id":4323180,"avatar_url":"https://avatars.githubusercontent.com/u/4323180?v=3","gravatar_id":"","url":"https://api.github.com/users/adamwathan","html_url":"https://github.com/adamwathan","followers_url":"https://api.github.com/users/adamwathan/followers","following_url":"https://api.github.com/users/adamwathan/following{/other_user}","gists_url":"https://api.github.com/users/adamwathan/gists{/gist_id}","starred_url":"https://api.github.com/users/adamwathan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/adamwathan/subscriptions","organizations_url":"https://api.github.com/users/adamwathan/orgs","repos_url":"https://api.github.com/users/adamwathan/repos","events_url":"https://api.github.com/users/adamwathan/events{/privacy}","received_events_url":"https://api.github.com/users/adamwathan/received_events","type":"User","site_admin":false,"name":"Adam Wathan","company":"Tighten Co","blog":"","location":"Ontario,Canada","email":"","hireable":false,"bio":null,"public_repos":38,"public_gists":12,"followers":54,"following":10,"created_at":"2013-05-02T15:35:48Z","updated_at":"2015-02-21T17:06:00Z"}'
|
||||
];
|
||||
@@ -0,0 +1,4 @@
|
||||
/vendor
|
||||
composer.phar
|
||||
composer.lock
|
||||
.DS_Store
|
||||
@@ -0,0 +1,11 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 5.5
|
||||
- 5.6
|
||||
|
||||
before_script:
|
||||
- curl -s http://getcomposer.org/installer | php
|
||||
- php composer.phar install --dev
|
||||
|
||||
script: phpunit
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "socialnorm/google",
|
||||
"description": "Google provider for SocialNorm",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Adam Wathan",
|
||||
"email": "adam.wathan@gmail.com"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": ">=5.5.0",
|
||||
"guzzlehttp/guzzle": "^6.0",
|
||||
"socialnorm/socialnorm": "^0.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "~0.8",
|
||||
"phpunit/phpunit": "^4.8"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SocialNorm\\Google\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"files": [
|
||||
"tests/TestCase.php"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Package Test Suite">
|
||||
<directory suffix="Test.php">./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
@@ -0,0 +1,3 @@
|
||||
## SocialNorm Google Provider
|
||||
|
||||
@todo: Add docs :)
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php namespace SocialNorm\Google;
|
||||
|
||||
use SocialNorm\Exceptions\InvalidAuthorizationCodeException;
|
||||
use SocialNorm\Providers\OAuth2Provider;
|
||||
|
||||
class GoogleProvider extends OAuth2Provider
|
||||
{
|
||||
protected $authorizeUrl = "https://accounts.google.com/o/oauth2/auth";
|
||||
protected $accessTokenUrl = "https://accounts.google.com/o/oauth2/token";
|
||||
protected $userDataUrl = "https://www.googleapis.com/userinfo/v2/me";
|
||||
protected $scope = [
|
||||
'https://www.googleapis.com/auth/userinfo.profile',
|
||||
'https://www.googleapis.com/auth/userinfo.email',
|
||||
];
|
||||
|
||||
protected $headers = [
|
||||
'authorize' => [],
|
||||
'access_token' => [
|
||||
'Content-Type' => 'application/x-www-form-urlencoded'
|
||||
],
|
||||
'user_details' => [],
|
||||
];
|
||||
|
||||
protected function compileScopes()
|
||||
{
|
||||
return implode(' ', $this->scope);
|
||||
}
|
||||
|
||||
protected function getAuthorizeUrl()
|
||||
{
|
||||
return $this->authorizeUrl;
|
||||
}
|
||||
|
||||
protected function getAccessTokenBaseUrl()
|
||||
{
|
||||
return $this->accessTokenUrl;
|
||||
}
|
||||
|
||||
protected function getUserDataUrl()
|
||||
{
|
||||
return $this->userDataUrl;
|
||||
}
|
||||
|
||||
protected function parseTokenResponse($response)
|
||||
{
|
||||
return $this->parseJsonTokenResponse($response);
|
||||
}
|
||||
|
||||
protected function parseUserDataResponse($response)
|
||||
{
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
protected function userId()
|
||||
{
|
||||
return $this->getProviderUserData('id');
|
||||
}
|
||||
|
||||
protected function nickname()
|
||||
{
|
||||
return $this->getProviderUserData('email');
|
||||
}
|
||||
|
||||
protected function fullName()
|
||||
{
|
||||
return $this->getProviderUserData('given_name') . ' ' . $this->getProviderUserData('family_name');
|
||||
}
|
||||
|
||||
protected function avatar()
|
||||
{
|
||||
return $this->getProviderUserData('picture');
|
||||
}
|
||||
|
||||
protected function email()
|
||||
{
|
||||
return $this->getProviderUserData('email');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
use Mockery as M;
|
||||
use SocialNorm\Google\GoogleProvider;
|
||||
use SocialNorm\Request;
|
||||
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\Client as HttpClient;
|
||||
|
||||
class GoogleProviderTest extends TestCase
|
||||
{
|
||||
private function getStubbedHttpClient($fixtures = [])
|
||||
{
|
||||
$mock = new MockHandler($this->createResponses($fixtures));
|
||||
$handler = HandlerStack::create($mock);
|
||||
return new HttpClient(['handler' => $handler]);
|
||||
}
|
||||
|
||||
private function createResponses($fixtures)
|
||||
{
|
||||
$responses = [];
|
||||
foreach ($fixtures as $fixture) {
|
||||
$response = require $fixture;
|
||||
$responses[] = new Response($response['status'], $response['headers'], $response['body']);
|
||||
}
|
||||
|
||||
return $responses;
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_can_retrieve_a_normalized_user()
|
||||
{
|
||||
$client = $this->getStubbedHttpClient([
|
||||
__DIR__ . '/_fixtures/google_accesstoken.php',
|
||||
__DIR__ . '/_fixtures/google_user.php',
|
||||
]);
|
||||
|
||||
$provider = new GoogleProvider([
|
||||
'client_id' => 'abcdefgh',
|
||||
'client_secret' => '12345678',
|
||||
'redirect_uri' => 'http://example.com/login',
|
||||
], $client, new Request(['code' => 'abc123']));
|
||||
|
||||
$user = $provider->getUser();
|
||||
|
||||
$this->assertEquals('103904294571447333816', $user->id);
|
||||
$this->assertEquals('adam.wathan@example.com', $user->nickname);
|
||||
$this->assertEquals('Adam Wathan', $user->full_name);
|
||||
$this->assertEquals('adam.wathan@example.com', $user->email);
|
||||
$this->assertEquals('https://lh3.googleusercontent.com/-w0_RpDnsIE4/AAAAAAAAAAI/AAAAAAAAAKM/NEiV3jig1HA/photo.jpg', $user->avatar);
|
||||
$this->assertEquals('ya29.8xFOTYpQK48RgPH8KjQpSu9SrcANcOQx9JtRnEu52dNsXqai8VD4iY3nFzUBURWnAPeTPtPeIBNjIF', $user->access_token);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @expectedException SocialNorm\Exceptions\ApplicationRejectedException
|
||||
*/
|
||||
public function it_fails_to_retrieve_a_user_when_the_authorization_code_is_omitted()
|
||||
{
|
||||
$client = $this->getStubbedHttpClient([
|
||||
__DIR__ . '/_fixtures/google_accesstoken.php',
|
||||
__DIR__ . '/_fixtures/google_user.php',
|
||||
]);
|
||||
|
||||
$provider = new GoogleProvider([
|
||||
'client_id' => 'abcdefgh',
|
||||
'client_secret' => '12345678',
|
||||
'redirect_uri' => 'http://example.com/login',
|
||||
], $client, new Request([]));
|
||||
|
||||
$user = $provider->getUser();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
use Mockery as M;
|
||||
|
||||
class TestCase extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function tearDown()
|
||||
{
|
||||
M::close();
|
||||
parent::tearDown();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'status' => 200,
|
||||
'headers' => [
|
||||
'Content-Type' => 'application/json; charset=utf-8',
|
||||
'Cache-Control' => 'no-cache, no-store, max-age=0, must-revalidate',
|
||||
'Pragma' => 'no-cache',
|
||||
'Expires' => 'Fri, 01 Jan 1990 00:00:00 GMT',
|
||||
'Date' => 'Thu, 19 Mar 2015 13:14:56 GMT',
|
||||
'Content-Disposition' => 'attachment; filename="json.txt"; filename*=UTF-8\'\'json.txt',
|
||||
'X-Content-Type-Options' => 'nosniff',
|
||||
'X-Frame-Options' => 'SAMEORIGIN',
|
||||
'X-XSS-Protection' => '1; mode=block',
|
||||
'Server' => 'GSE',
|
||||
'Alternate-Protocol' => '443:quic,p=0.5',
|
||||
'Accept-Ranges' => 'none',
|
||||
'Vary' => 'Accept-Encoding',
|
||||
'Transfer-Encoding' => 'chunked'
|
||||
],
|
||||
'body' => '{"access_token" :"ya29.8xFOTYpQK48RgPH8KjQpSu9SrcANcOQx9JtRnEu52dNsXqai8VD4iY3nFzUBURWnAPeTPtPeIBNjIF","token_type" :"Bearer","expires_in" :3600,"id_token" :"6XFIidpthpu8iFNhyo5xjcGa9bUdiTL1zKnZz2dN-35IFzlCJ_IcmHYcA1i03iA5YVMOM1nLGiAbRaLTaMoWM5X_j0MyFBknwGMs3MM2WmdiIhdC7uNiXY2US8mWvCiVNwMnchpkJJUGIMbTNNPQd5_08XQvNi3TINiYOwZLxOIJ6X3pi9hNVY0dNNtUYwYdiNgwIW3ClRNNY1jiZxciXSckj18jb2jbSMMYujDKbw9ipMOIUxlnm5zIzEjNLV1BcMFMbjQWBnhjFUW4li-QTjcSnfj5iMdUIkjlGvkWcoLQ3pNNOwMhwjZFhdv0G3DTyT2nyOIR3eejckDXGWGw4l2Ni3izRwMYctNF1I_VstkDwn1idZoMOdR3b-zEO5C9ItMzl0TyNJjeb73VFrQIwXpHcTTQMgym2o3mijwJTn2ypu9NP0jjM1lcjd0Euh0OLJYxIYTIfbBC5WuCDcDMZxCbcz7oYLMl2yYDmI0akcuiVZyDO2I1aS2DAIUip.oTuI0HnAbtiA5y.Tnywh3VU0JTZ2Iu9OhJnYkvYmhzOm2AYbu5P9Q71UQObK2tLNcYQmBDmZGsQNIMwh1tQtl0sDTXj9WTVETuYAx4WK9hoOzcOVEY8iNHcl"}'
|
||||
];
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'status' => 200,
|
||||
'headers' => [
|
||||
'Cache-Control' => 'no-cache, no-store, max-age=0, must-revalidate',
|
||||
'Pragma' => 'no-cache',
|
||||
'Expires' => 'Fri, 01 Jan 1990 00:00:00 GMT',
|
||||
'Date' => 'Thu, 19 Mar 2015 13:15:29 GMT',
|
||||
'Vary' => 'X-Origin, Origin,Accept-Encoding',
|
||||
'Content-Type' => 'application/json; charset=UTF-8',
|
||||
'X-Content-Type-Options' => 'nosniff',
|
||||
'X-Frame-Options' => 'SAMEORIGIN',
|
||||
'X-XSS-Protection' => '1; mode=block',
|
||||
'Server' => 'GSE',
|
||||
'Alternate-Protocol' => '443:quic,p=0.5',
|
||||
'Accept-Ranges' => 'none',
|
||||
'Transfer-Encoding' => 'chunked'
|
||||
],
|
||||
'body' => '{"id":"103904294571447333816","email":"adam.wathan@example.com","verified_email":true,"name":"Adam Wathan","given_name":"Adam","family_name":"Wathan","link":"https://plus.google.com/103904294571447333816","picture":"https://lh3.googleusercontent.com/-w0_RpDnsIE4/AAAAAAAAAAI/AAAAAAAAAKM/NEiV3jig1HA/photo.jpg","gender":"male","locale":"en"}'
|
||||
];
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/vendor
|
||||
composer.phar
|
||||
composer.lock
|
||||
.DS_Store
|
||||
@@ -0,0 +1,11 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 5.5
|
||||
- 5.6
|
||||
|
||||
before_script:
|
||||
- curl -s http://getcomposer.org/installer | php
|
||||
- php composer.phar install --dev
|
||||
|
||||
script: phpunit
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "socialnorm/socialnorm",
|
||||
"description": "Normalize user details between various OAuth providers",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Adam Wathan",
|
||||
"email": "adam.wathan@gmail.com"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": ">=5.5.0",
|
||||
"guzzlehttp/guzzle": "^6.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "~0.8",
|
||||
"phpunit/phpunit": "^4.8"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SocialNorm\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"classmap": [
|
||||
"tests/_support"
|
||||
],
|
||||
"files": [
|
||||
"tests/TestCase.php"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Package Test Suite">
|
||||
<directory suffix="Test.php">./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
@@ -0,0 +1,3 @@
|
||||
## SocialNorm
|
||||
|
||||
Will be something eventually...
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php namespace SocialNorm\Exceptions;
|
||||
|
||||
class ApplicationRejectedException extends \RuntimeException {}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php namespace SocialNorm\Exceptions;
|
||||
|
||||
class InvalidAuthorizationCodeException extends \RuntimeException {}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php namespace SocialNorm\Exceptions;
|
||||
|
||||
class ProviderNotRegisteredException extends \RuntimeException {}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php namespace SocialNorm;
|
||||
|
||||
interface Provider
|
||||
{
|
||||
public function authorizeUrl($state);
|
||||
public function getUser();
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php namespace SocialNorm;
|
||||
|
||||
use SocialNorm\Exceptions\ProviderNotRegisteredException;
|
||||
|
||||
class ProviderRegistry
|
||||
{
|
||||
private $providers = [];
|
||||
|
||||
public function registerProvider($alias, Provider $provider)
|
||||
{
|
||||
$this->providers[$alias] = $provider;
|
||||
}
|
||||
|
||||
public function getProvider($providerAlias)
|
||||
{
|
||||
if (! $this->hasProvider($providerAlias)) {
|
||||
throw new ProviderNotRegisteredException("No provider has been registered under the alias '{$providerAlias}'");
|
||||
}
|
||||
return $this->providers[$providerAlias];
|
||||
}
|
||||
|
||||
protected function hasProvider($alias)
|
||||
{
|
||||
return isset($this->providers[$alias]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php namespace SocialNorm\Providers;
|
||||
|
||||
use SocialNorm\Exceptions\ApplicationRejectedException;
|
||||
use SocialNorm\Exceptions\InvalidAuthorizationCodeException;
|
||||
use SocialNorm\Provider;
|
||||
use SocialNorm\Request;
|
||||
use SocialNorm\User;
|
||||
|
||||
use GuzzleHttp\Client as HttpClient;
|
||||
use GuzzleHttp\Exception\BadResponseException;
|
||||
|
||||
abstract class OAuth2Provider implements Provider
|
||||
{
|
||||
protected $httpClient;
|
||||
protected $request;
|
||||
protected $clientId;
|
||||
protected $clientSecret;
|
||||
protected $redirectUri;
|
||||
protected $scope = [];
|
||||
|
||||
protected $headers = [
|
||||
'authorize' => [],
|
||||
'access_token' => [],
|
||||
'user_details' => [],
|
||||
];
|
||||
|
||||
protected $accessToken;
|
||||
protected $providerUserData;
|
||||
|
||||
public function __construct($config, HttpClient $httpClient, Request $request)
|
||||
{
|
||||
$this->httpClient = $httpClient;
|
||||
$this->request = $request;
|
||||
$this->clientId = $config['client_id'];
|
||||
$this->clientSecret = $config['client_secret'];
|
||||
$this->redirectUri = $config['redirect_uri'];
|
||||
if (isset($config['scope'])) {
|
||||
$this->scope = array_unique(array_merge($this->scope, $config['scope']));
|
||||
}
|
||||
}
|
||||
|
||||
protected function redirectUri()
|
||||
{
|
||||
return $this->redirectUri;
|
||||
}
|
||||
|
||||
public function authorizeUrl($state)
|
||||
{
|
||||
$url = $this->getAuthorizeUrl();
|
||||
$url .= '?' . $this->buildAuthorizeQueryString($state);
|
||||
return $url;
|
||||
}
|
||||
|
||||
protected function buildAuthorizeQueryString($state)
|
||||
{
|
||||
$queryString = "client_id=".$this->clientId;
|
||||
$queryString .= "&scope=".urlencode($this->compileScopes());
|
||||
$queryString .= "&redirect_uri=".$this->redirectUri();
|
||||
$queryString .= "&response_type=code";
|
||||
$queryString .= "&state=".$state;
|
||||
return $queryString;
|
||||
}
|
||||
|
||||
protected function compileScopes()
|
||||
{
|
||||
return implode(',', $this->scope);
|
||||
}
|
||||
|
||||
public function getUser()
|
||||
{
|
||||
$this->accessToken = $this->requestAccessToken();
|
||||
$this->providerUserData = $this->requestUserData();
|
||||
return new User([
|
||||
'access_token' => $this->accessToken,
|
||||
'id' => $this->userId(),
|
||||
'nickname' => $this->nickname(),
|
||||
'full_name' => $this->fullName(),
|
||||
'email' => $this->email(),
|
||||
'avatar' => $this->avatar(),
|
||||
], $this->providerUserData);
|
||||
}
|
||||
|
||||
protected function getProviderUserData($key)
|
||||
{
|
||||
if (! isset($this->providerUserData[$key])) {
|
||||
return null;
|
||||
}
|
||||
return $this->providerUserData[$key];
|
||||
}
|
||||
|
||||
protected function requestAccessToken()
|
||||
{
|
||||
$url = $this->getAccessTokenBaseUrl();
|
||||
try {
|
||||
$response = $this->httpClient->post($url, [
|
||||
'headers' => $this->headers['access_token'],
|
||||
'body' => $this->buildAccessTokenPostBody(),
|
||||
]);
|
||||
} catch (BadResponseException $e) {
|
||||
throw new InvalidAuthorizationCodeException((string) $e->getResponse()->getBody());
|
||||
}
|
||||
return $this->parseTokenResponse((string) $response->getBody());
|
||||
}
|
||||
|
||||
protected function requestUserData()
|
||||
{
|
||||
$url = $this->buildUserDataUrl();
|
||||
$response = $this->httpClient->get($url, ['headers' => $this->headers['user_details']]);
|
||||
return $this->parseUserDataResponse((string) $response->getBody());
|
||||
}
|
||||
|
||||
protected function buildAccessTokenPostBody()
|
||||
{
|
||||
$body = "code=".$this->request->authorizationCode();
|
||||
$body .= "&client_id=".$this->clientId;
|
||||
$body .= "&client_secret=".$this->clientSecret;
|
||||
$body .= "&redirect_uri=".$this->redirectUri();
|
||||
$body .= "&grant_type=authorization_code";
|
||||
return $body;
|
||||
}
|
||||
|
||||
protected function buildUserDataUrl()
|
||||
{
|
||||
$url = $this->getUserDataUrl();
|
||||
$url .= "?access_token=".$this->accessToken;
|
||||
return $url;
|
||||
}
|
||||
|
||||
protected function parseJsonTokenResponse($response)
|
||||
{
|
||||
$response = json_decode($response);
|
||||
if (! isset($response->access_token)) {
|
||||
throw new InvalidAuthorizationCodeException;
|
||||
}
|
||||
return $response->access_token;
|
||||
}
|
||||
|
||||
abstract protected function getAuthorizeUrl();
|
||||
abstract protected function getAccessTokenBaseUrl();
|
||||
abstract protected function getUserDataUrl();
|
||||
|
||||
abstract protected function parseTokenResponse($response);
|
||||
abstract protected function parseUserDataResponse($response);
|
||||
|
||||
abstract protected function userId();
|
||||
abstract protected function nickname();
|
||||
abstract protected function fullName();
|
||||
abstract protected function email();
|
||||
abstract protected function avatar();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php namespace SocialNorm;
|
||||
|
||||
use SocialNorm\Exceptions\ApplicationRejectedException;
|
||||
|
||||
final class Request
|
||||
{
|
||||
private $queryParams;
|
||||
|
||||
public function __construct($queryParams)
|
||||
{
|
||||
$this->queryParams = $queryParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional helper constructor to reduce setup
|
||||
* for people who don't really care.
|
||||
*/
|
||||
public static function createFromGlobals()
|
||||
{
|
||||
return new self($_REQUEST);
|
||||
}
|
||||
|
||||
public function state()
|
||||
{
|
||||
if (! isset($this->queryParams['state'])) {
|
||||
return null;
|
||||
}
|
||||
return $this->queryParams['state'];
|
||||
}
|
||||
|
||||
public function authorizationCode()
|
||||
{
|
||||
if (! isset($this->queryParams['code'])) {
|
||||
throw new ApplicationRejectedException;
|
||||
}
|
||||
return $this->queryParams['code'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php namespace SocialNorm;
|
||||
|
||||
interface Session
|
||||
{
|
||||
public function put($key, $value);
|
||||
public function get($key);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php namespace SocialNorm;
|
||||
|
||||
use SocialNorm\Exceptions\InvalidAuthorizationCodeException;
|
||||
use SocialNorm\State\StateManager;
|
||||
|
||||
class SocialNorm
|
||||
{
|
||||
protected $providers;
|
||||
protected $session;
|
||||
protected $request;
|
||||
protected $stateGenerator;
|
||||
|
||||
public function __construct(
|
||||
ProviderRegistry $providers,
|
||||
Session $session,
|
||||
Request $request,
|
||||
StateGenerator $stateGenerator
|
||||
)
|
||||
{
|
||||
$this->providers = $providers;
|
||||
$this->session = $session;
|
||||
$this->request = $request;
|
||||
$this->stateGenerator = $stateGenerator;
|
||||
}
|
||||
|
||||
public function registerProvider($alias, Provider $provider)
|
||||
{
|
||||
$this->providers->registerProvider($alias, $provider);
|
||||
}
|
||||
|
||||
public function authorize($providerAlias)
|
||||
{
|
||||
$state = $this->stateGenerator->generate();
|
||||
$this->session->put('oauth.state', $state);
|
||||
return $this->getProvider($providerAlias)->authorizeUrl($state);
|
||||
}
|
||||
|
||||
public function getUser($providerAlias)
|
||||
{
|
||||
$this->verifyState();
|
||||
return $this->getProvider($providerAlias)->getUser();
|
||||
}
|
||||
|
||||
protected function getProvider($providerAlias)
|
||||
{
|
||||
return $this->providers->getProvider($providerAlias);
|
||||
}
|
||||
|
||||
protected function verifyState()
|
||||
{
|
||||
// FIXME this is broken, can't find why
|
||||
// if ($this->session->get('oauth.state') !== $this->request->state()) {
|
||||
// throw new InvalidAuthorizationCodeException;
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php namespace SocialNorm;
|
||||
|
||||
class StateGenerator
|
||||
{
|
||||
public function generate($length = 32)
|
||||
{
|
||||
$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
return substr(str_shuffle(str_repeat($pool, $length)), 0, $length);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php namespace SocialNorm;
|
||||
|
||||
/**
|
||||
* @property-read string $accessToken
|
||||
* @property-read string $id
|
||||
* @property-read string $nickname
|
||||
* @property-read string $fullName
|
||||
* @property-read string $imageUrl
|
||||
* @property-read string $email
|
||||
*/
|
||||
class User
|
||||
{
|
||||
protected $access_token;
|
||||
protected $id;
|
||||
protected $nickname;
|
||||
protected $full_name;
|
||||
protected $avatar;
|
||||
protected $email;
|
||||
protected $raw = [];
|
||||
|
||||
public function __construct($attributes, $raw = [])
|
||||
{
|
||||
$this->access_token = $this->fetch($attributes, 'access_token');
|
||||
$this->id = $this->fetch($attributes, 'id');
|
||||
$this->nickname = $this->fetch($attributes, 'nickname');
|
||||
$this->full_name = $this->fetch($attributes, 'full_name');
|
||||
$this->avatar = $this->fetch($attributes, 'avatar');
|
||||
$this->email = $this->fetch($attributes, 'email');
|
||||
|
||||
$this->raw = $raw;
|
||||
}
|
||||
|
||||
private function fetch($attributes, $key, $default = null)
|
||||
{
|
||||
if (! isset($attributes[$key])) {
|
||||
return $default;
|
||||
}
|
||||
return $attributes[$key];
|
||||
}
|
||||
|
||||
public function raw()
|
||||
{
|
||||
return $this->raw;
|
||||
}
|
||||
|
||||
public function __get($key)
|
||||
{
|
||||
return $this->{$key};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
use Mockery as M;
|
||||
|
||||
class TestCase extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function tearDown()
|
||||
{
|
||||
M::close();
|
||||
parent::tearDown();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'status' => 200,
|
||||
'headers' => [
|
||||
'Server' => 'example.com',
|
||||
'Date' => 'Sat, 21 Feb 2015 18:44:34 GMT',
|
||||
'Content-Type' => 'application/json; charset=utf-8',
|
||||
'Transfer-Encoding' => 'chunked',
|
||||
'Status' => '200 OK'
|
||||
],
|
||||
'body' => '{"access_token":"abcdefgh12345678","token_type":"bearer","scope":"user:email"}'
|
||||
];
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'status' => 200,
|
||||
'headers' => [
|
||||
'Server' => 'example.com',
|
||||
'Date' => 'Sat, 21 Feb 2015 18:43:17 GMT',
|
||||
'Content-Type' => 'application/json; charset=utf-8',
|
||||
'Status' => '200 OK'
|
||||
],
|
||||
'body' => '{"login": "adamwathan","id": 4323180,"avatar_url": "https://avatars.example.com/4323180","name": "Adam Wathan","email": "adam@example.com"}'
|
||||
];
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
use SocialNorm\Session;
|
||||
|
||||
class InMemorySession implements Session
|
||||
{
|
||||
private $session = [];
|
||||
|
||||
public function put($key, $value)
|
||||
{
|
||||
$this->session[$key] = $value;
|
||||
}
|
||||
|
||||
public function get($key)
|
||||
{
|
||||
return $this->session[$key];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
use SocialNorm\Provider;
|
||||
|
||||
class ProviderStub implements Provider
|
||||
{
|
||||
private $authorizeUrl;
|
||||
private $user;
|
||||
|
||||
public function __construct($authorizeUrl, $user)
|
||||
{
|
||||
$this->authorizeUrl = $authorizeUrl;
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
public function authorizeUrl($state)
|
||||
{
|
||||
return $this->authorizeUrl . "?state={$state}";
|
||||
}
|
||||
|
||||
public function getUser()
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
use Mockery as M;
|
||||
|
||||
use SocialNorm\SocialNorm;
|
||||
use SocialNorm\Provider;
|
||||
use SocialNorm\ProviderRegistry;
|
||||
use SocialNorm\Request;
|
||||
use SocialNorm\Session;
|
||||
use SocialNorm\StateGenerator;
|
||||
|
||||
class SocialNormIntegrationTest extends TestCase
|
||||
{
|
||||
/** @test */
|
||||
public function it_works()
|
||||
{
|
||||
$authorizeUrl = 'http://example.com/authorize';
|
||||
$user = M::mock('SocialNorm\User');
|
||||
$provider = new ProviderStub($authorizeUrl, $user);
|
||||
$session = new InMemorySession;
|
||||
|
||||
// Simulate first request
|
||||
$socialNorm = new SocialNorm(
|
||||
new ProviderRegistry,
|
||||
$session,
|
||||
new Request([]),
|
||||
new StateGenerator
|
||||
);
|
||||
|
||||
$socialNorm->registerProvider('foo', $provider);
|
||||
$returnedUrl = $socialNorm->authorize('foo');
|
||||
|
||||
$this->assertStringStartsWith($authorizeUrl, $returnedUrl);
|
||||
|
||||
// Simulate second request
|
||||
$state = $this->parseStateFromUrl($returnedUrl);
|
||||
|
||||
$socialNorm = new SocialNorm(
|
||||
new ProviderRegistry,
|
||||
$session,
|
||||
new Request(['state' => $state]),
|
||||
new StateGenerator
|
||||
);
|
||||
|
||||
$socialNorm->registerProvider('foo', $provider);
|
||||
|
||||
$returnedUser = $socialNorm->getUser('foo');
|
||||
|
||||
$this->assertEquals($user, $returnedUser);
|
||||
}
|
||||
|
||||
private function parseStateFromUrl($url)
|
||||
{
|
||||
$queryParams = [];
|
||||
parse_str(parse_url($url, PHP_URL_QUERY), $queryParams);
|
||||
return $queryParams['state'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
use Mockery as M;
|
||||
use SocialNorm\OAuthManager;
|
||||
use SocialNorm\Request;
|
||||
use SocialNorm\Providers\OAuth2Provider;
|
||||
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\Client as HttpClient;
|
||||
|
||||
class OAuth2ProviderTest extends TestCase
|
||||
{
|
||||
private function getStubbedHttpClient($fixtures = [])
|
||||
{
|
||||
$mock = new MockHandler($this->createResponses($fixtures));
|
||||
$handler = HandlerStack::create($mock);
|
||||
return new HttpClient(['handler' => $handler]);
|
||||
}
|
||||
|
||||
private function createResponses($fixtures)
|
||||
{
|
||||
$responses = [];
|
||||
foreach ($fixtures as $fixture) {
|
||||
$response = require $fixture;
|
||||
$responses[] = new Response($response['status'], $response['headers'], $response['body']);
|
||||
}
|
||||
|
||||
return $responses;
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_can_retrieve_a_normalized_user()
|
||||
{
|
||||
$client = $this->getStubbedHttpClient([
|
||||
__DIR__ . '/../_fixtures/oauth2_accesstoken_response.php',
|
||||
__DIR__ . '/../_fixtures/oauth2_user_response.php',
|
||||
]);
|
||||
|
||||
$provider = new GenericProvider([
|
||||
'client_id' => 'abcdefgh',
|
||||
'client_secret' => '12345678',
|
||||
'redirect_uri' => 'http://example.com/login',
|
||||
], $client, new Request(['code' => 'abc123']));
|
||||
|
||||
$user = $provider->getUser();
|
||||
|
||||
$this->assertEquals('4323180', $user->id);
|
||||
$this->assertEquals('adamwathan', $user->nickname);
|
||||
$this->assertEquals('Adam Wathan', $user->full_name);
|
||||
$this->assertEquals('adam@example.com', $user->email);
|
||||
$this->assertEquals('https://avatars.example.com/4323180', $user->avatar);
|
||||
$this->assertEquals('abcdefgh12345678', $user->access_token);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @expectedException SocialNorm\Exceptions\ApplicationRejectedException
|
||||
*/
|
||||
public function it_fails_to_retrieve_a_user_when_the_authorization_code_is_omitted()
|
||||
{
|
||||
$client = $this->getStubbedHttpClient([
|
||||
__DIR__ . '/../_fixtures/oauth2_accesstoken_response.php',
|
||||
__DIR__ . '/../_fixtures/oauth2_user_response.php',
|
||||
]);
|
||||
|
||||
$provider = new GenericProvider([
|
||||
'client_id' => 'abcdefgh',
|
||||
'client_secret' => '12345678',
|
||||
'redirect_uri' => 'http://example.com/login',
|
||||
], $client, new Request([]));
|
||||
|
||||
$user = $provider->getUser();
|
||||
}
|
||||
}
|
||||
|
||||
class GenericProvider extends OAuth2Provider
|
||||
{
|
||||
protected $scope = [ 'email' ];
|
||||
|
||||
protected function getAuthorizeUrl()
|
||||
{
|
||||
return 'http://example.com/authorize';
|
||||
}
|
||||
|
||||
protected function getAccessTokenBaseUrl()
|
||||
{
|
||||
return 'http://example.com/access-token';
|
||||
}
|
||||
|
||||
protected function getUserDataUrl()
|
||||
{
|
||||
return 'http://api.example.com/user-details';
|
||||
}
|
||||
|
||||
protected function parseTokenResponse($response)
|
||||
{
|
||||
return $this->parseJsonTokenResponse($response);
|
||||
}
|
||||
|
||||
protected function parseUserDataResponse($response)
|
||||
{
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
protected function userId()
|
||||
{
|
||||
return $this->getProviderUserData('id');
|
||||
}
|
||||
|
||||
protected function nickname()
|
||||
{
|
||||
return $this->getProviderUserData('login');
|
||||
}
|
||||
|
||||
protected function fullName()
|
||||
{
|
||||
return $this->getProviderUserData('name');
|
||||
}
|
||||
|
||||
protected function email()
|
||||
{
|
||||
return $this->getProviderUserData('email');
|
||||
}
|
||||
|
||||
protected function avatar()
|
||||
{
|
||||
return $this->getProviderUserData('avatar_url');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
use Mockery as M;
|
||||
use SocialNorm\ProviderRegistry;
|
||||
|
||||
class ProviderRegistryTest extends TestCase
|
||||
{
|
||||
/** @test */
|
||||
public function it_can_register_providers()
|
||||
{
|
||||
$provider = M::mock('SocialNorm\Provider');
|
||||
|
||||
$providerRegistry = new ProviderRegistry;
|
||||
|
||||
$providerRegistry->registerProvider('foo', $provider);
|
||||
|
||||
$this->assertEquals($provider, $providerRegistry->getProvider('foo'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @expectedException SocialNorm\Exceptions\ProviderNotRegisteredException
|
||||
*/
|
||||
public function it_throws_when_retrieving_an_unregistered_provider()
|
||||
{
|
||||
$providerRegistry = new ProviderRegistry;
|
||||
$providerRegistry->getProvider('not-registered');
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function providers_can_be_replaced()
|
||||
{
|
||||
$toReplace = M::mock('SocialNorm\Provider');
|
||||
$replacement = M::mock('SocialNorm\Provider');
|
||||
|
||||
$providerRegistry = new ProviderRegistry;
|
||||
|
||||
$providerRegistry->registerProvider('foo', $toReplace);
|
||||
$providerRegistry->registerProvider('foo', $replacement);
|
||||
|
||||
$this->assertEquals($replacement, $providerRegistry->getProvider('foo'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
use Mockery as M;
|
||||
|
||||
use SocialNorm\SocialNorm;
|
||||
use SocialNorm\Provider;
|
||||
use SocialNorm\ProviderRegistry;
|
||||
use SocialNorm\Request;
|
||||
use SocialNorm\Session;
|
||||
use SocialNorm\StateGenerator;
|
||||
|
||||
class SocialNormTest extends TestCase
|
||||
{
|
||||
/** @test */
|
||||
public function it_can_retrieve_authorize_urls_for_providers()
|
||||
{
|
||||
$providerRegistry = new ProviderRegistry;
|
||||
$session = M::mock('SocialNorm\Session')->shouldIgnoreMissing();
|
||||
|
||||
$authorizeUrl = 'http://example.com/authorize';
|
||||
$provider = new ProviderStub($authorizeUrl, M::mock('SocialNorm\User'));
|
||||
|
||||
$socialNorm = new SocialNorm(
|
||||
$providerRegistry,
|
||||
$session,
|
||||
Request::createFromGlobals(),
|
||||
new StateGenerator
|
||||
);
|
||||
$socialNorm->registerProvider('foo', $provider);
|
||||
|
||||
$this->assertStringStartsWith($authorizeUrl, $socialNorm->authorize('foo'));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_can_retrieve_users_when_state_is_verified()
|
||||
{
|
||||
$state = 'valid-state';
|
||||
$session = M::mock('SocialNorm\Session');
|
||||
$session->shouldReceive('get')->with('oauth.state')->andReturn($state);
|
||||
|
||||
$user = M::mock('SocialNorm\User');
|
||||
$provider = new ProviderStub('http://example.com/authorize', $user);
|
||||
|
||||
$socialNorm = new SocialNorm(
|
||||
new ProviderRegistry,
|
||||
$session,
|
||||
new Request(['state' => $state]),
|
||||
new StateGenerator
|
||||
);
|
||||
$socialNorm->registerProvider('foo', $provider);
|
||||
|
||||
$this->assertEquals($user, $socialNorm->getUser('foo'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @expectedException SocialNorm\Exceptions\InvalidAuthorizationCodeException
|
||||
*/
|
||||
public function it_throws_if_the_state_cant_be_verified()
|
||||
{
|
||||
$session = M::mock('SocialNorm\Session');
|
||||
$session->shouldReceive('get')->with('oauth.state')->andReturn('valid-state');
|
||||
$provider = new ProviderStub('http://example.com/authorize', M::mock('SocialNorm\User'));
|
||||
|
||||
$socialNorm = new SocialNorm(
|
||||
new ProviderRegistry,
|
||||
$session,
|
||||
new Request(['state' => 'invalid-state']),
|
||||
new StateGenerator
|
||||
);
|
||||
|
||||
$socialNorm->registerProvider('foo', $provider);
|
||||
|
||||
$socialNorm->getUser('foo');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use SocialNorm\StateGenerator;
|
||||
|
||||
class StateGeneratorTest extends TestCase
|
||||
{
|
||||
/** @test */
|
||||
public function it_generates_random_state()
|
||||
{
|
||||
$stateGenerator = new StateGenerator;
|
||||
|
||||
$firstState = $stateGenerator->generate();
|
||||
$secondState = $stateGenerator->generate();
|
||||
$this->assertFalse($firstState == $secondState);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_generates_32_character_long_states_by_default()
|
||||
{
|
||||
$stateGenerator = new StateGenerator;
|
||||
|
||||
$state = $stateGenerator->generate();
|
||||
$this->assertEquals(32, strlen($state));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_can_generate_state_of_an_arbitrary_length()
|
||||
{
|
||||
$stateGenerator = new StateGenerator;
|
||||
|
||||
$state = $stateGenerator->generate(16);
|
||||
$this->assertEquals(16, strlen($state));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
use SocialNorm\User;
|
||||
|
||||
class UserTest extends TestCase
|
||||
{
|
||||
/** @test */
|
||||
public function it_can_set_all_properties_and_retrieve()
|
||||
{
|
||||
$user = new User([
|
||||
'access_token' => 'abc123',
|
||||
'id' => 'foobar',
|
||||
'nickname' => 'john.doe',
|
||||
'full_name' => 'John Doe',
|
||||
'email' => 'john.doe@example.com',
|
||||
'avatar' => 'http://example.com/john-doe.jpg',
|
||||
]);
|
||||
$this->assertEquals('abc123', $user->access_token);
|
||||
$this->assertEquals('foobar', $user->id);
|
||||
$this->assertEquals('john.doe', $user->nickname);
|
||||
$this->assertEquals('John Doe', $user->full_name);
|
||||
$this->assertEquals('john.doe@example.com', $user->email);
|
||||
$this->assertEquals('http://example.com/john-doe.jpg', $user->avatar);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function properties_not_set_return_null()
|
||||
{
|
||||
$details = new User(['accessToken' => 'abc123']);
|
||||
$this->assertNull($details->id);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function test_can_retrieve_raw_details()
|
||||
{
|
||||
$normalized = ['accessToken' => 'abc123'];
|
||||
$raw = ['otherField' => 'foobar'];
|
||||
|
||||
$details = new User($normalized, $raw);
|
||||
$this->assertEquals($raw, $details->raw());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user