Skip to content

Instantly share code, notes, and snippets.

@cursedcoder
Last active March 20, 2019 22:00
Show Gist options
  • Save cursedcoder/7725072df443d4f65ad9 to your computer and use it in GitHub Desktop.
Save cursedcoder/7725072df443d4f65ad9 to your computer and use it in GitHub Desktop.
HWIOAuth with FOSUserBundle intergration + phpspec coverage
fos_user:
db_driver: orm
firewall_name: main
user_class: App\Entity\User
service:
user_manager: app.user_manager
hwi_oauth:
firewall_name: main
fosub:
username_iterations: 30
properties: ~
resource_owners:
facebook:
type: facebook
client_id: %oauth_facebook_key%
client_secret: %oauth_facebook_secret%
options:
display: popup #dialog is optimized for popup window
google:
type: google
client_id: %oauth_google_key%
client_secret: %oauth_google_secret%
scope: "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"
twitter:
type: twitter
client_id: %oauth_twitter_key%
client_secret: %oauth_twitter_secret%
vkontakte:
type: vkontakte
client_id: %oauth_vkontakte_key%
client_secret: %oauth_vkontakte_secret%
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class OAuthIdentity
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\OneToOne(targetEntity="User", inversedBy="oAuthIdentity")
*/
private $user;
/**
* @ORM\Column(nullable=true)
*/
private $facebookId;
/**
* @ORM\Column(nullable=true)
*/
private $facebookToken;
/**
* @ORM\Column(nullable=true)
*/
private $googleId;
/**
* @ORM\Column(nullable=true)
*/
private $googleToken;
/**
* @ORM\Column(nullable=true)
*/
private $twitterId;
/**
* @ORM\Column(nullable=true)
*/
private $twitterToken;
/**
* @ORM\Column(nullable=true)
*/
private $vkontakteId;
/**
* @ORM\Column(nullable=true)
*/
private $vkontakteToken;
public function __construct(User $user)
{
$this->user = $user;
}
public function getId()
{
return $this->id;
}
public function getUser()
{
return $this->user;
}
public function setFacebookToken($facebookToken)
{
$this->facebookToken = $facebookToken;
}
public function getFacebookToken()
{
return $this->facebookToken;
}
public function setFacebookId($facebookId)
{
$this->facebookId = $facebookId;
}
public function getFacebookId()
{
return $this->facebookId;
}
public function setGoogleToken($googleToken)
{
$this->googleToken = $googleToken;
}
public function getGoogleToken()
{
return $this->googleToken;
}
public function setGoogleId($googleId)
{
$this->googleId = $googleId;
}
public function getGoogleId()
{
return $this->googleId;
}
public function setTwitterToken($twitterToken)
{
$this->twitterToken = $twitterToken;
}
public function getTwitterToken()
{
return $this->twitterToken;
}
public function setTwitterId($twitterId)
{
$this->twitterId = $twitterId;
}
public function getTwitterId()
{
return $this->twitterId;
}
public function setVkontakteToken($vkontakteToken)
{
$this->vkontakteToken = $vkontakteToken;
}
public function getVkontakteToken()
{
return $this->vkontakteToken;
}
public function setVkontakteId($vkontakteId)
{
$this->vkontakteId = $vkontakteId;
}
public function getVkontakteId()
{
return $this->vkontakteId;
}
}
<?php
namespace App\User;
use FOS\UserBundle\Doctrine\UserManager as BaseManager;
class UserManager extends BaseManager
{
public function findUserByOAuthService($service, $id)
{
$repository = $this->objectManager->getRepository('App:OAuthIdentity');
if ($identity = $repository->findOneBy([$service.'Id' => $id])) {
return $identity->getUser();
}
}
}
<?php
namespace App\User;
use App\Entity\User;
use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
use HWI\Bundle\OAuthBundle\Security\Core\User\FOSUBUserProvider;
use Symfony\Component\Security\Core\User\UserInterface;
class OAuthProvider extends FOSUBUserProvider
{
/**
* @var \Symfony\Component\PropertyAccess\PropertyAccessor
*/
public $propertyAccessor;
public function connect(UserInterface $user, UserResponseInterface $response)
{
$username = $response->getUsername();
$token = $response->getAccessToken();
$service = $response->getResourceOwner()->getName();
if (null !== $previousUser = $this->userManager->findUserByOAuthService($service, $username)) {
$this->assignServiceToUser($previousUser, $service);
$this->userManager->updateUser($previousUser);
}
$this->assignServiceToUser($user, $service, $username, $token);
$this->userManager->updateUser($user);
}
public function loadUserByOAuthUserResponse(UserResponseInterface $response)
{
$username = $response->getUsername();
$token = $response->getAccessToken();
$service = $response->getResourceOwner()->getName();
if ($user = $this->userManager->findUserByOAuthService($service, $username)) {
$this->assignServiceToUser($user, $service, $username, $token);
} else {
$user = $this->userManager->createUser();
$user->setUsername($username);
if ($response->getEmail()) {
$user->setEmail($response->getEmail());
} else {
$user->setEmail($username.'@domain.com');
}
// !!! CUSTOMIZE THAT !!! \\
$user->setPlainPassword($username);
$user->setEnabled(true);
$user->addRole('ROLE_OAUTH_USER');
$this->assignServiceToUser($user, $service, $username, $token);
$this->userManager->updateUser($user);
}
return $user;
}
private function assignServiceToUser(User $user, $service, $id = null, $token = null)
{
if ($user->isOAuthConnected()) {
$identity = $user->getOAuthIdentity();
} else {
$identity = $user->initializeOAuth();
}
$this->propertyAccessor->setValue($identity, $service.'Id', $id);
$this->propertyAccessor->setValue($identity, $service.'Token', $token);
}
}
fos_user_security:
resource: "@FOSUserBundle/Resources/config/routing/security.xml"
fos_user_register:
resource: "@FOSUserBundle/Resources/config/routing/registration.xml"
prefix: /register
hwi_oauth_redirect:
resource: "@HWIOAuthBundle/Resources/config/routing/redirect.xml"
prefix: /connect
facebook_login:
pattern: /login/check-facebook
google_login:
pattern: /login/check-google
twitter_login:
pattern: /login/check-twitter
vkontakte_login:
pattern: /login/check-vkontakte
security:
encoders:
FOS\UserBundle\Model\UserInterface: sha512
role_hierarchy:
ROLE_OAUTH_USER: ROLE_USER
ROLE_ORG: ROLE_USER
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
fos_userbundle:
id: fos_user.user_provider.username_email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_provider: form.csrf_provider
oauth:
resource_owners:
facebook: /login/check-facebook
google: /login/check-google
twitter: /login/check-twitter
vkontakte: /login/check-vkontakte
login_path: /login
failure_path: /login
oauth_user_provider:
service: app.oauth_user_provider
logout: true
anonymous: true
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
parameters:
app.oauth_user_provider.class: App\User\OAuthProvider
app.user_manager.class: App\User\UserManager
services:
app.oauth_user_provider:
class: %app.oauth_user_provider.class%
arguments: [@app.user_manager, []]
properties:
propertyAccessor: @property_accessor
app.user_manager:
class: %app.user_manager.class%
arguments:
- @security.encoder_factory
- @fos_user.util.username_canonicalizer
- @fos_user.util.email_canonicalizer
- @doctrine.orm.entity_manager
- %fos_user.model.user.class%
<?php
namespace spec\App\User;
use App\Entity\OAuthIdentity;
use App\Entity\User;
use App\User\UserManager;
use HWI\Bundle\OAuthBundle\OAuth\ResourceOwnerInterface;
use HWI\Bundle\OAuthBundle\Tests\Fixtures\CustomUserResponse;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Symfony\Component\PropertyAccess\PropertyAccessor;
class OAuthProviderSpec extends ObjectBehavior
{
public function let(UserManager $um)
{
$this->beConstructedWith($um, []);
}
public function it_connects_user_form_oauth_response
(
UserManager $um,
User $user,
CustomUserResponse $response,
ResourceOwnerInterface $owner,
OAuthIdentity $identity,
PropertyAccessor $pa
)
{
$this->propertyAccessor = $pa;
$service = 'vkontakte';
$username = 111;
$token = 'abasdasdasdcv323';
$owner->getName()->willReturn($service);
$response->getUsername()->willReturn($username);
$response->getAccessToken()->willReturn($token);
$response->getResourceOwner()->willReturn($owner);
$um->findUserByOAuthService($service, $username)->willReturn(null);
$um->updateUser($user)->shouldBeCalled();
$user->isOAuthConnected()->willReturn(true);
$user->getOAuthIdentity()->willReturn($identity);
$pa->setValue($identity, $service.'Id', $username)->shouldBeCalled();
$pa->setValue($identity, $service.'Token', $token)->shouldBeCalled();
$this->connect($user, $response);
}
public function it_disconnects_previous_user_and_connects_new
(
UserManager $um,
User $prevUser,
User $newUser,
CustomUserResponse $response,
ResourceOwnerInterface $owner,
OAuthIdentity $prevIdentity,
OAuthIdentity $newIdentity,
PropertyAccessor $pa
)
{
$this->propertyAccessor = $pa;
$service = 'vkontakte';
$username = 111;
$token = 'abasdasdasdcv323';
$owner->getName()->willReturn($service);
$response->getUsername()->willReturn($username);
$response->getAccessToken()->willReturn($token);
$response->getResourceOwner()->willReturn($owner);
$prevUser->isOAuthConnected()->willReturn(true);
$prevUser->getOAuthIdentity()->willReturn($prevIdentity);
$newUser->isOAuthConnected()->willReturn(true);
$newUser->getOAuthIdentity()->willReturn($newIdentity);
$um->findUserByOAuthService($service, $username)->willReturn($prevUser);
$pa->setValue($prevIdentity, $service.'Id', null)->shouldBeCalled();
$pa->setValue($prevIdentity, $service.'Token', null)->shouldBeCalled();
$um->updateUser($prevUser)->shouldBeCalled();
$pa->setValue($newIdentity, $service.'Id', $username)->shouldBeCalled();
$pa->setValue($newIdentity, $service.'Token', $token)->shouldBeCalled();
$um->updateUser($newUser)->shouldBeCalled();
$this->connect($newUser, $response);
}
}
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
/**
* @ORM\Entity
*/
class User extends BaseUser
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\OneToOne(targetEntity="OAuthIdentity", cascade={"all"}, mappedBy="user")
*/
private $oAuthIdentity;
public function isOAuthConnected()
{
return is_object($this->oAuthIdentity);
}
public function initializeOAuth()
{
$this->oAuthIdentity = new OAuthIdentity($this);
return $this->oAuthIdentity;
}
public function getOAuthIdentity()
{
return $this->oAuthIdentity;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment