fixing up the authenticator to allow identiy chaining
This commit is contained in:
@@ -20,6 +20,7 @@ class EloquentOAuthServiceProvider extends ServiceProvider {
|
||||
'facebook' => 'SocialNorm\Facebook\FacebookProvider',
|
||||
'github' => 'SocialNorm\GitHub\GitHubProvider',
|
||||
'google' => 'SocialNorm\Google\GoogleProvider',
|
||||
'stack' => 'SocialNorm\StackOverflow\StackOverflowProvider',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -74,7 +75,7 @@ class EloquentOAuthServiceProvider extends ServiceProvider {
|
||||
});
|
||||
}
|
||||
|
||||
protected function registerProviders($socialnorm, $request)
|
||||
protected function registerProviders(SocialNorm $socialnorm, $request)
|
||||
{
|
||||
if (! $providerAliases = $this->app['config']['services.oauth_providers']) {
|
||||
return;
|
||||
|
||||
@@ -1,77 +1,92 @@
|
||||
<?php namespace AdamWathan\EloquentOAuth;
|
||||
|
||||
use SocialNorm\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use SocialNorm\ProviderUser;
|
||||
|
||||
class Authenticator
|
||||
{
|
||||
protected $auth;
|
||||
/** @var \Illuminate\Auth\SessionGuard */
|
||||
protected $guard;
|
||||
/** @var UserStore */
|
||||
protected $users;
|
||||
/** @var IdentityStore */
|
||||
protected $identities;
|
||||
|
||||
public function __construct($auth, $users, $identities)
|
||||
public function __construct(\Illuminate\Auth\SessionGuard $auth, UserStore $users, IdentityStore $identities)
|
||||
{
|
||||
$this->auth = $auth;
|
||||
$this->guard = $auth;
|
||||
$this->users = $users;
|
||||
$this->identities = $identities;
|
||||
}
|
||||
|
||||
public function login($providerAlias, $userDetails, $callback = null)
|
||||
public function login($providerAlias, ProviderUser $userDetails, $callback = null)
|
||||
{
|
||||
/** @var Model|Authenticatable $user */
|
||||
$user = $this->getUser($providerAlias, $userDetails);
|
||||
$user = $this->runCallback($callback, $user, $userDetails);
|
||||
$this->updateUser($user, $providerAlias, $userDetails);
|
||||
$this->auth->login($user);
|
||||
$this->guard->login($user);
|
||||
}
|
||||
|
||||
protected function getUser($providerAlias, \SocialNorm\User $details)
|
||||
protected function getUser($providerAlias, ProviderUser $details) // returns User
|
||||
{
|
||||
if ($this->identities->userExists($providerAlias, $details)) {
|
||||
if ($this->identities->userExists($providerAlias, $details, false)) {
|
||||
return $this->getExistingUser($providerAlias, $details);
|
||||
}
|
||||
|
||||
/** @var \App\Models\User $newuser */
|
||||
$newuser = $this->users->create();
|
||||
$newuser->name = $details->nickname ?: ($details->full_name ?: $details->email);
|
||||
$newuser->email = $details->email;
|
||||
return $newuser;
|
||||
// this is potentially unsafe - allows user to attach a social identity to an
|
||||
// existing account just my matching the e-mail.
|
||||
|
||||
// we have to assume the providers are trustworthy.
|
||||
if ($details->email && $this->identities->userExists($providerAlias, $details, true)) {
|
||||
return $this->users->findByEmail($details->email);
|
||||
}
|
||||
|
||||
return $this->users->create();
|
||||
}
|
||||
|
||||
protected function runCallback($callback, $user, $userDetails)
|
||||
protected function runCallback($callback, Model $user, ProviderUser $userDetails)
|
||||
{
|
||||
$callback = $callback ?: function () {};
|
||||
$callbackUser = $callback($user, $userDetails);
|
||||
return $callbackUser ?: $user;
|
||||
}
|
||||
|
||||
protected function updateUser($user, $providerAlias, $details)
|
||||
protected function updateUser(Model $user, $providerAlias, ProviderUser $details)
|
||||
{
|
||||
$this->users->store($user);
|
||||
$this->storeProviderIdentity($user, $providerAlias, $details);
|
||||
}
|
||||
|
||||
protected function getExistingUser($providerAlias, $details)
|
||||
protected function getExistingUser($providerAlias, ProviderUser $details)
|
||||
{
|
||||
$identity = $this->identities->getByProvider($providerAlias, $details);
|
||||
return $this->users->findByIdentity($identity);
|
||||
}
|
||||
|
||||
protected function storeProviderIdentity($user, $providerAlias, $details)
|
||||
protected function storeProviderIdentity(Model $user, string $providerAlias, ProviderUser $details)
|
||||
{
|
||||
if ($this->identities->userExists($providerAlias, $details)) {
|
||||
if ($this->identities->userExists($providerAlias, $details, false)) {
|
||||
$this->updateProviderIdentity($providerAlias, $details);
|
||||
} else {
|
||||
$this->addProviderIdentity($user, $providerAlias, $details);
|
||||
}
|
||||
}
|
||||
|
||||
protected function updateProviderIdentity($providerAlias, $details)
|
||||
protected function updateProviderIdentity(string $providerAlias, ProviderUser $details)
|
||||
{
|
||||
$identity = $this->identities->getByProvider($providerAlias, $details);
|
||||
$identity->access_token = $details->access_token;
|
||||
$this->identities->store($identity);
|
||||
}
|
||||
|
||||
protected function addProviderIdentity($user, $providerAlias, $details)
|
||||
/**
|
||||
* @param Model $user
|
||||
* @param $providerAlias
|
||||
* @param $details
|
||||
*/
|
||||
protected function addProviderIdentity(Model $user, string $providerAlias, ProviderUser $details)
|
||||
{
|
||||
$identity = new OAuthIdentity;
|
||||
$identity->user_id = $user->getKey();
|
||||
|
||||
@@ -1,28 +1,58 @@
|
||||
<?php namespace AdamWathan\EloquentOAuth;
|
||||
<?php
|
||||
namespace AdamWathan\EloquentOAuth;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use SocialNorm\ProviderUser;
|
||||
|
||||
class EloquentIdentityStore implements IdentityStore
|
||||
{
|
||||
public function getByProvider($provider, $providerUser)
|
||||
public function getByProvider(string $provider, ProviderUser $providerUser)
|
||||
{
|
||||
return OAuthIdentity::where('provider', $provider)
|
||||
->where('provider_user_id', $providerUser->id)
|
||||
->first();
|
||||
}
|
||||
|
||||
public function flush($user, $provider)
|
||||
/**
|
||||
* Try to retrieve user by their e-mail
|
||||
* (!!! this is coupled to the application code !!!)
|
||||
*
|
||||
* @param ProviderUser $providerUser
|
||||
* @return User|Model|null|object
|
||||
*/
|
||||
public function getByEmail(ProviderUser $providerUser)
|
||||
{
|
||||
$first = User::where('email', $providerUser->email)->first();
|
||||
|
||||
if($first->email) return $first;
|
||||
return null;
|
||||
}
|
||||
|
||||
public function flush(Model $user, string $provider)
|
||||
{
|
||||
OAuthIdentity::where('user_id', $user->getKey())
|
||||
->where('provider', $provider)
|
||||
->delete();
|
||||
}
|
||||
|
||||
public function store($identity)
|
||||
public function store(OAuthIdentity $identity)
|
||||
{
|
||||
$identity->save();
|
||||
}
|
||||
|
||||
public function userExists($provider, $providerUser)
|
||||
public function userExists(string $provider, ProviderUser $providerUser, bool $allowByEmail)
|
||||
{
|
||||
return (bool) $this->getByProvider($provider, $providerUser);
|
||||
$byProvider = (bool) $this->getByProvider($provider, $providerUser);
|
||||
if ($byProvider) return true;
|
||||
|
||||
if (!$allowByEmail) return false;
|
||||
|
||||
if ($providerUser->email) {
|
||||
$byEmail = $this->getByEmail($providerUser);
|
||||
if ($byEmail) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
<?php namespace AdamWathan\EloquentOAuth;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use SocialNorm\ProviderUser;
|
||||
|
||||
interface IdentityStore
|
||||
{
|
||||
public function getByProvider($provider, $providerUser);
|
||||
public function flush($user, $provider);
|
||||
public function store($identity);
|
||||
public function userExists($provider, $providerUser);
|
||||
public function getByProvider(string $provider, ProviderUser $providerUser);
|
||||
public function flush(Model $user, string $provider);
|
||||
public function getByEmail(ProviderUser $providerUser);
|
||||
public function store(OAuthIdentity $identity);
|
||||
public function userExists(string $provider, ProviderUser $providerUser, bool $allowByEmail);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?php namespace AdamWathan\EloquentOAuth;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model as Eloquent;
|
||||
use Config;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property $id
|
||||
@@ -10,14 +9,14 @@ use Config;
|
||||
* @property $provider_user_id
|
||||
* @property $access_token
|
||||
*/
|
||||
class OAuthIdentity extends Eloquent
|
||||
class OAuthIdentity extends Model
|
||||
{
|
||||
protected static $configuredTable = 'oauth_identities';
|
||||
|
||||
// public static function configureTable($table)
|
||||
// {
|
||||
// static::$configuredTable = $table;
|
||||
// }
|
||||
public static function configureTable($table)
|
||||
{
|
||||
static::$configuredTable = $table;
|
||||
}
|
||||
|
||||
public function getTable()
|
||||
{
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<?php namespace AdamWathan\EloquentOAuth;
|
||||
|
||||
use SocialNorm\Provider;
|
||||
use SocialNorm\SocialNorm;
|
||||
|
||||
class OAuthManager
|
||||
{
|
||||
/** @var string */
|
||||
/** @var \Illuminate\Routing\Redirector */
|
||||
protected $redirect;
|
||||
|
||||
/** @var Authenticator */
|
||||
@@ -13,25 +14,32 @@ class OAuthManager
|
||||
/** @var SocialNorm */
|
||||
protected $socialnorm;
|
||||
|
||||
public function __construct($redirect, $authenticator, $socialnorm)
|
||||
public function __construct(
|
||||
\Illuminate\Routing\Redirector $redirect,
|
||||
Authenticator $authenticator,
|
||||
SocialNorm $socialnorm)
|
||||
{
|
||||
$this->redirect = $redirect;
|
||||
$this->authenticator = $authenticator;
|
||||
$this->socialnorm = $socialnorm;
|
||||
}
|
||||
|
||||
public function authorize($providerAlias)
|
||||
public function authorize(string $providerAlias)
|
||||
{
|
||||
return $this->redirect->to($this->socialnorm->authorize($providerAlias));
|
||||
}
|
||||
|
||||
public function login($providerAlias, $callback = null)
|
||||
/**
|
||||
* @param $providerAlias
|
||||
* @param \Closure|null $callback
|
||||
*/
|
||||
public function login(string $providerAlias, $callback = null)
|
||||
{
|
||||
$details = $this->socialnorm->getUser($providerAlias);
|
||||
return $this->authenticator->login($providerAlias, $details, $callback);
|
||||
}
|
||||
|
||||
public function registerProvider($alias, $provider)
|
||||
public function registerProvider(string $alias, Provider $provider)
|
||||
{
|
||||
$this->socialnorm->registerProvider($alias, $provider);
|
||||
}
|
||||
|
||||
@@ -4,20 +4,29 @@ use SocialNorm\Session as SocialNormSession;
|
||||
|
||||
class Session implements SocialNormSession
|
||||
{
|
||||
/** @var \Illuminate\Session\SessionManager */
|
||||
private $store;
|
||||
|
||||
public function __construct($store)
|
||||
public function __construct(\Illuminate\Session\SessionManager $store)
|
||||
{
|
||||
$this->store = $store;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
return $this->store->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param mixed|null $value
|
||||
*/
|
||||
public function put($key, $value)
|
||||
{
|
||||
return $this->store->put($key, $value);
|
||||
$this->store->put($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,35 @@
|
||||
<?php namespace AdamWathan\EloquentOAuth;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class UserStore
|
||||
{
|
||||
protected $model;
|
||||
/** @var string */
|
||||
protected $modelClass;
|
||||
|
||||
public function __construct($model)
|
||||
public function __construct(string $modelClass)
|
||||
{
|
||||
$this->model = $model;
|
||||
$this->modelClass = $modelClass;
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
$user = new $this->model;
|
||||
$user = new $this->modelClass;
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function store($user)
|
||||
public function store(Model $user)
|
||||
{
|
||||
return $user->save();
|
||||
}
|
||||
|
||||
public function findByIdentity($identity)
|
||||
public function findByIdentity(OAuthIdentity $identity)
|
||||
{
|
||||
return $identity->belongsTo($this->model, 'user_id')->firstOrFail();
|
||||
return $identity->belongsTo($this->modelClass, 'user_id')->firstOrFail();
|
||||
}
|
||||
|
||||
public function findByEmail(string $email)
|
||||
{
|
||||
return $this->modelClass::where('email', $email)->firstOrFail();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php namespace SocialNorm\Facebook;
|
||||
|
||||
use GuzzleHttp\Exception\BadResponseException;
|
||||
use SocialNorm\Exceptions\InvalidAuthorizationCodeException;
|
||||
use SocialNorm\Providers\OAuth2Provider;
|
||||
|
||||
|
||||
@@ -58,12 +58,12 @@ class GoogleProvider extends OAuth2Provider
|
||||
|
||||
protected function nickname()
|
||||
{
|
||||
return $this->getProviderUserData('email');
|
||||
return $this->getProviderUserData('given_name');
|
||||
}
|
||||
|
||||
protected function fullName()
|
||||
{
|
||||
return $this->getProviderUserData('given_name') . ' ' . $this->getProviderUserData('family_name');
|
||||
return $this->getProviderUserData('name'); // this is the full name
|
||||
}
|
||||
|
||||
protected function avatar()
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
interface Provider
|
||||
{
|
||||
public function authorizeUrl($state);
|
||||
public function getUser();
|
||||
public function authorizeUrl(string $state) : string;
|
||||
public function getUser() : ProviderUser;
|
||||
}
|
||||
|
||||
@@ -4,14 +4,15 @@ use SocialNorm\Exceptions\ProviderNotRegisteredException;
|
||||
|
||||
class ProviderRegistry
|
||||
{
|
||||
/** @var Provider[] */
|
||||
private $providers = [];
|
||||
|
||||
public function registerProvider($alias, Provider $provider)
|
||||
public function registerProvider(string $alias, Provider $provider)
|
||||
{
|
||||
$this->providers[$alias] = $provider;
|
||||
}
|
||||
|
||||
public function getProvider($providerAlias)
|
||||
public function getProvider(string $providerAlias)
|
||||
{
|
||||
if (! $this->hasProvider($providerAlias)) {
|
||||
throw new ProviderNotRegisteredException("No provider has been registered under the alias '{$providerAlias}'");
|
||||
@@ -19,7 +20,7 @@ class ProviderRegistry
|
||||
return $this->providers[$providerAlias];
|
||||
}
|
||||
|
||||
protected function hasProvider($alias)
|
||||
protected function hasProvider(string $alias)
|
||||
{
|
||||
return isset($this->providers[$alias]);
|
||||
}
|
||||
|
||||
+5
-3
@@ -1,14 +1,16 @@
|
||||
<?php namespace SocialNorm;
|
||||
|
||||
/**
|
||||
* @property-read string $accessToken
|
||||
* @property-read string $access_token
|
||||
* @property-read string $id
|
||||
* @property-read string $nickname
|
||||
* @property-read string $fullName
|
||||
* @property-read string $imageUrl
|
||||
* @property-read string $avatar
|
||||
* @property-read string $email
|
||||
* @property-read string $full_name
|
||||
* @property-read array $raw
|
||||
*/
|
||||
class User
|
||||
class ProviderUser
|
||||
{
|
||||
protected $access_token;
|
||||
protected $id;
|
||||
@@ -4,7 +4,7 @@ use SocialNorm\Exceptions\ApplicationRejectedException;
|
||||
use SocialNorm\Exceptions\InvalidAuthorizationCodeException;
|
||||
use SocialNorm\Provider;
|
||||
use SocialNorm\Request;
|
||||
use SocialNorm\User;
|
||||
use SocialNorm\ProviderUser;
|
||||
|
||||
use GuzzleHttp\Client as HttpClient;
|
||||
use GuzzleHttp\Exception\BadResponseException;
|
||||
@@ -27,7 +27,7 @@ abstract class OAuth2Provider implements Provider
|
||||
protected $accessToken;
|
||||
protected $providerUserData;
|
||||
|
||||
public function __construct($config, HttpClient $httpClient, Request $request)
|
||||
public function __construct(array $config, HttpClient $httpClient, Request $request)
|
||||
{
|
||||
$this->httpClient = $httpClient;
|
||||
$this->request = $request;
|
||||
@@ -44,7 +44,7 @@ abstract class OAuth2Provider implements Provider
|
||||
return $this->redirectUri;
|
||||
}
|
||||
|
||||
public function authorizeUrl($state)
|
||||
public function authorizeUrl(string $state) : string
|
||||
{
|
||||
$url = $this->getAuthorizeUrl();
|
||||
$url .= '?' . $this->buildAuthorizeQueryString($state);
|
||||
@@ -66,11 +66,11 @@ abstract class OAuth2Provider implements Provider
|
||||
return implode(',', $this->scope);
|
||||
}
|
||||
|
||||
public function getUser()
|
||||
public function getUser() : ProviderUser
|
||||
{
|
||||
$this->accessToken = $this->requestAccessToken();
|
||||
$this->providerUserData = $this->requestUserData();
|
||||
return new User([
|
||||
return new ProviderUser([
|
||||
'access_token' => $this->accessToken,
|
||||
'id' => $this->userId(),
|
||||
'nickname' => $this->nickname(),
|
||||
|
||||
@@ -4,9 +4,13 @@ use SocialNorm\Exceptions\InvalidAuthorizationCodeException;
|
||||
|
||||
class SocialNorm
|
||||
{
|
||||
/** @var ProviderRegistry */
|
||||
protected $providers;
|
||||
/** @var Session */
|
||||
protected $session;
|
||||
/** @var Request */
|
||||
protected $request;
|
||||
/** @var StateGenerator */
|
||||
protected $stateGenerator;
|
||||
|
||||
public function __construct(
|
||||
@@ -22,12 +26,12 @@ class SocialNorm
|
||||
$this->stateGenerator = $stateGenerator;
|
||||
}
|
||||
|
||||
public function registerProvider($alias, Provider $provider)
|
||||
public function registerProvider(string $alias, Provider $provider)
|
||||
{
|
||||
$this->providers->registerProvider($alias, $provider);
|
||||
}
|
||||
|
||||
public function authorize($providerAlias)
|
||||
public function authorize(string $providerAlias)
|
||||
{
|
||||
$state = $this->stateGenerator->generate();
|
||||
|
||||
@@ -36,13 +40,13 @@ class SocialNorm
|
||||
return $this->getProvider($providerAlias)->authorizeUrl($state);
|
||||
}
|
||||
|
||||
public function getUser($providerAlias)
|
||||
public function getUser(string $providerAlias)
|
||||
{
|
||||
$this->verifyState();
|
||||
return $this->getProvider($providerAlias)->getUser();
|
||||
}
|
||||
|
||||
protected function getProvider($providerAlias)
|
||||
protected function getProvider(string $providerAlias)
|
||||
{
|
||||
return $this->providers->getProvider($providerAlias);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
class StateGenerator
|
||||
{
|
||||
public function generate($length = 32)
|
||||
public function generate(int $length = 32)
|
||||
{
|
||||
$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
return substr(str_shuffle(str_repeat($pool, $length)), 0, $length);
|
||||
|
||||
@@ -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,29 @@
|
||||
{
|
||||
"name": "socialnorm/stackoverflow",
|
||||
"description": "StackOverflow provider for SocialNorm",
|
||||
"authors": [
|
||||
{
|
||||
"name": "MightyPork"
|
||||
}
|
||||
],
|
||||
"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\\StackOverflow\\": "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,100 @@
|
||||
<?php namespace SocialNorm\StackOverflow;
|
||||
|
||||
use SocialNorm\Providers\OAuth2Provider;
|
||||
use GuzzleHttp\Client as HttpClient;
|
||||
use SocialNorm\Request;
|
||||
|
||||
// https://stackapps.com/apps/oauth/view/12789
|
||||
// https://api.stackexchange.com/docs/authentication
|
||||
|
||||
// This is entirely untested; it might work, but since we don't get e-mail from it,
|
||||
// it's use as an auth method is problematic.
|
||||
class StackOverflowProvider extends OAuth2Provider
|
||||
{
|
||||
protected $authorizeUrl = "https://stackoverflow.com/oauth";
|
||||
protected $accessTokenUrl = "https://stackoverflow.com/oauth/access_token";
|
||||
protected $userDataUrl = "https://api.stackexchange.com/2.2/me";
|
||||
protected $scope = [];
|
||||
|
||||
private $key;
|
||||
|
||||
protected $headers = [
|
||||
'authorize' => [],
|
||||
'access_token' => [
|
||||
'Content-Type' => 'application/x-www-form-urlencoded'
|
||||
],
|
||||
'user_details' => [],
|
||||
];
|
||||
|
||||
public function __construct(array $config, HttpClient $httpClient, Request $request)
|
||||
{
|
||||
parent::__construct($config, $httpClient, $request);
|
||||
$this->key = $config['key'];
|
||||
}
|
||||
|
||||
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 buildUserDataUrl()
|
||||
{
|
||||
$url = $this->getUserDataUrl();
|
||||
|
||||
$url .= "?access_token=".$this->accessToken
|
||||
. "&site=stackoverflow"
|
||||
. "&key=".$this->key;
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
protected function parseTokenResponse($response)
|
||||
{
|
||||
return $this->parseJsonTokenResponse($response);
|
||||
}
|
||||
|
||||
protected function parseUserDataResponse($response)
|
||||
{
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
protected function userId()
|
||||
{
|
||||
return array_get($this->providerUserData, 'items.0.account_id');
|
||||
}
|
||||
|
||||
protected function nickname()
|
||||
{
|
||||
return array_get($this->providerUserData, 'items.0.display_name');
|
||||
}
|
||||
|
||||
protected function fullName()
|
||||
{
|
||||
return array_get($this->providerUserData, 'items.0.display_name');
|
||||
}
|
||||
|
||||
protected function avatar()
|
||||
{
|
||||
return array_get($this->providerUserData, 'items.0.profile_image');
|
||||
}
|
||||
|
||||
protected function email()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -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"}'
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user