Reputation: 620
I have project on SF4.3 and i don't use FOSUserBundle. How to setup HWIOAuthBundle? Configuration that i have now:
security.yaml
main:
anonymous: ~
oauth:
resource_owners:
facebook: "/login/check-facebook"
google: "/login/check-google"
#my_custom_provider: "/login/check-custom"
#my_github: "/login/check-github"
login_path: /login
use_forward: false
failure_path: /login
provider: users
oauth_user_provider:
service: my.oauth_aware.user_provider.service
hwi_oauth.yaml
hwi_oauth:
# list of names of the firewalls in which this bundle is active, this setting MUST be set
firewall_names: [main]
# https://github.com/hwi/HWIOAuthBundle/blob/master/Resources/doc/2-configuring_resource_owners.md
resource_owners:
facebook:
type: facebook
client_id: '%env(FB_ID)%'
client_secret: '%env(FB_SECRET)%'
scope: "email"
options:
display: popup
csrf: true
google:
type: google
client_id: '%env(G_ID)%'
client_secret: '%env(G_SECRET)%'
scope: "email"
and in security.yaml
my.oauth_aware.user_provider.service:
class: HWI\Bundle\OAuthBundle\Security\Core\User\FOSUBUserProvider
arguments:
- '@fos_user.user_manager'
- ['@fos_user.user_manager', google: google ]
if i don't use FOSUser for users provider in security.yaml
has to be different, how to configure provider for my User
?
Upvotes: 2
Views: 3361
Reputation: 3697
If you want to use your custom User class (eg App\Entity\User) the solution is to make your own User Provider. If you want users to be able to authenticate with a traditional login form and also with HWIOAuthBundle then you could start to make your own User provider as described here. Of course you have to add some code inside the two methods before it works (see example below) and do not forget to setup the providers section in security.yaml like so:
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
id: App\Security\UserProvider
Then if you can authenticate with your traditional login form through your newly created User Provider and it all works well you can start to integrate HWIOAuthBundle. Start to direct the oauth_user_provider setting to your own User Provider class. Your main firewall could look like this:
firewalls:
main:
anonymous: true
oauth:
resource_owners:
facebook: "/login/check-facebook"
oauth_user_provider:
service: App\Security\UserProvider # HERE YOU GO!
login_path: /login
use_forward: false
failure_path: /login
form_login:
login_path: /login
guard:
authenticators:
- App\Security\LoginFormAuthenticator
logout:
path: app_logout
By the way your App\Security\UserProvider class should be auto-wired in the service container. If not you have to add the service manually to your service.yaml.
If you now try to login with a resource owner (eg Facebook or Google) from HWIOAuthBundle then you will get an error because your User Provider class must implement HWI's OAuthAwareUserProviderInterface. A quick look into that interface source file will learn that you have to add one method to your User Provider: loadUserByOAuthUserResponse(UserResponseInterface $response). So let your User Provider class implement HWI OAuthAwareUserProviderInterface and add the method to the class. It is pretty straightforward.
Here comes the full User Provider that i wrote for this test case:
<?php
namespace App\Security;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Doctrine\ORM\EntityManagerInterface;
use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
use HWI\Bundle\OAuthBundle\Security\Core\User\OAuthAwareUserProviderInterface;
use App\Entity\User;
class UserProvider implements UserProviderInterface, OAuthAwareUserProviderInterface
{
private $em;
private $property = 'email';
public function __construct(EntityManagerInterface $em) {
$this->em = $em;
}
/**
* @return UserInterface
*/
public function loadUserByUsername($username)
{
$repository = $this->em->getRepository(User::class);
if (null !== $this->property) {
$user = $repository->findOneBy([$this->property => $username]);
} else {
if (!$repository instanceof UserLoaderInterface) {
throw new \InvalidArgumentException(sprintf('You must either make the "%s" entity Doctrine Repository ("%s") implement "Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface" or set the "property" option in the corresponding entity provider configuration.', $this->classOrAlias, \get_class($repository)));
}
$user = $repository->loadUserByUsername($username);
}
if (null === $user) {
throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username));
}
return $user;
}
/**
* @return UserInterface
*/
public function refreshUser(UserInterface $user)
{
if (!$user instanceof User) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', User::class));
}
$repository = $this->em->getRepository(User::class);
if ($repository instanceof UserProviderInterface) {
$refreshedUser = $repository->refreshUser($user);
} else {
$refreshedUser = $repository->find($user->getId());
if (null === $refreshedUser) {
throw new UsernameNotFoundException(sprintf('User with id %s not found', json_encode($user->getId())));
}
}
return $refreshedUser;
}
/**
* @return UserInterface
*/
public function loadUserByOAuthUserResponse(UserResponseInterface $response)
{
return $this->loadUserByUsername($response->getEmail());
}
/**
* Tells Symfony to use this provider for this User class.
*/
public function supportsClass($class)
{
return User::class === $class;
}
}
This User Provider is still not ready. For example it will throw an exception if a unknown facebook/google/... user tries to login. You have to extend this example to your needs and thoroughly test it!
Upvotes: 1
Reputation: 620
Ok, i made my own provider:
class OAuthUserProvider extends BaseClass {
public $entityManager;
public $userRepository;
public function __construct(
UserManagerInterface $userManager,
array $properties,
UserRepository $userRepository,
EntityManagerInterface $entityManager
) {
parent::__construct($userManager, $properties);
$this->userRepository = $userRepository;
$this->entityManager = $entityManager;
}
/**
* {@inheritdoc}
* @throws \Doctrine\ORM\NonUniqueResultException
*/
public function loadUserByOAuthUserResponse(UserResponseInterface $response)
{
$socialID = $response->getUsername();
$user = $this->userRepository->findByGoogleId($socialID);
$email = $response->getEmail();
//check if the user already has the corresponding social account
if (null === $user) {
//check if the user has a normal account
$user = $this->userRepository->findUserByEmail($email);
if (null === $user || !$user instanceof UserInterface) {
//if the user does not have a normal account, set it up:
$user = new User();
$user->setEmail($email);
$user->setPlainPassword(md5(uniqid('', true)));
$user->setActive(true);
}
//then set its corresponding social id
$service = $response->getResourceOwner()->getName();
switch ($service) {
case 'google':
$user->setGoogleID($socialID);
break;
case 'facebook':
$user->setFacebookID($socialID);
break;
}
$em = $this->entityManager;
$em->persist($user);
$em->flush();
//$this->userManager->updateUser($user);
} else {
//and then login the user
$checker = new UserChecker();
$checker->checkPreAuth($user);
}
return $user;
}
}
in my services.yaml
:
app.provider.oauth:
class: App\Security\Providers\OAuthUserProvider
arguments: ['@fos_user.user_manager',{google: googleID, facebook: facebook}]
Upvotes: 3