Skip to content

Instantly share code, notes, and snippets.

@danvbe
Last active April 21, 2023 15:39
Show Gist options
  • Save danvbe/4476697 to your computer and use it in GitHub Desktop.
Save danvbe/4476697 to your computer and use it in GitHub Desktop.
A way to integrate FosUserBundle and HWIOAuthBundle

I have managed to install this… and make it work. I implemented it for Facebook and Google, but you can extend it. My solution it is mostly as described in #116, with a bit of more code presented. The key aspects that lack in the #116 presentation (IMO) are:

  • the registration as service of your custom FOSUBUserProvider (with the necessary parameters)
  • set the service for oauth_user_provider in the security.yml with your custom created service

Here are the steps:

  1. Routing. In routing.yml I have added all the routes for both bundles.
  2. Configuration. I have set the config.yml mostly as it is presented in the HWIOAuthBundle.
  3. Security. I have set the security.yml mostly as it is presented in the HWIOAuthBundle (though my routes are using /login pattern, not /connect). Also, the oauth_user_provider is set for my custom service.
  4. User. My own User entity, extended from FosUser.
  5. UserProvider. My user provider, registered as service, extended from FOSUBUserProvider. This is the one that actually does the User registration in YOUR database with data from PROVIDERS (Facebook, Google, etc.) and in responsible for connecting already logged in users with accounts from PROVIDERS. It does this by overvriting 2 functions (connect(UserInterface $user, UserResponseInterface $response) and loadUserByOAuthUserResponse(UserResponseInterface $response)). See code below.
  6. Custom service. My user provider is registered as service.

Using this code, when:

  1. No user is authenticated on my site: by accessing http://my_app_web_root/login/facebook or http://my_app_web_root/login/google, a user is created in my database (with data as it is saved in the custom FOSUBUserProvider) and it is automatically login-ed to my site.
  2. A user is authenticated on my site: by accessing http://my_app_web_root/login/facebook or http://my_app_web_root/login/google, the current user is updated with data from the provider (account linking).

I think this is the behavior everybody was expecting :).

#app/config/config.yml
hwi_oauth:
#this is my custom user provider, created from FOSUBUserProvider - will manage the
#automatic user registration on your site, with data from the provider (facebook. google, etc.)
#and also, the connecting part (get the token and the user_id)
connect:
account_connector: my_user_provider
# name of the firewall in which this bundle is active, this setting MUST be set
firewall_name: main
fosub:
username_iterations: 30
properties:
# these properties will be used/redefined later in the custom FOSUBUserProvider service.
facebook: facebook_id
google: google_id
resource_owners:
facebook:
type: facebook
client_id: "%facebook_app_id%"
client_secret: "%facebook_app_secret%"
scope: ""
google:
type: google
client_id: "%google_app_id%"
client_secret: "%google_app_secret%"
scope: "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"
# here you will add one (or more) configurations for resource owners
<?php
namespace danvbe\UserBundle\Security\Core\User;
use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
use HWI\Bundle\OAuthBundle\Security\Core\User\FOSUBUserProvider as BaseClass;
use Symfony\Component\Security\Core\User\UserInterface;
class FOSUBUserProvider extends BaseClass
{
/**
* {@inheritDoc}
*/
public function connect(UserInterface $user, UserResponseInterface $response)
{
$property = $this->getProperty($response);
$username = $response->getUsername();
//on connect - get the access token and the user ID
$service = $response->getResourceOwner()->getName();
$setter = 'set'.ucfirst($service);
$setter_id = $setter.'Id';
$setter_token = $setter.'AccessToken';
//we "disconnect" previously connected users
if (null !== $previousUser = $this->userManager->findUserBy(array($property => $username))) {
$previousUser->$setter_id(null);
$previousUser->$setter_token(null);
$this->userManager->updateUser($previousUser);
}
//we connect current user
$user->$setter_id($username);
$user->$setter_token($response->getAccessToken());
$this->userManager->updateUser($user);
}
/**
* {@inheritdoc}
*/
public function loadUserByOAuthUserResponse(UserResponseInterface $response)
{
$username = $response->getUsername();
$user = $this->userManager->findUserBy(array($this->getProperty($response) => $username));
//when the user is registrating
if (null === $user) {
$service = $response->getResourceOwner()->getName();
$setter = 'set'.ucfirst($service);
$setter_id = $setter.'Id';
$setter_token = $setter.'AccessToken';
// create new user here
$user = $this->userManager->createUser();
$user->$setter_id($username);
$user->$setter_token($response->getAccessToken());
//I have set all requested data with the user's username
//modify here with relevant data
$user->setUsername($username);
$user->setEmail($username);
$user->setPassword($username);
$user->setEnabled(true);
$this->userManager->updateUser($user);
return $user;
}
//if user exists - go with the HWIOAuth way
$user = parent::loadUserByOAuthUserResponse($response);
$serviceName = $response->getResourceOwner()->getName();
$setter = 'set' . ucfirst($serviceName) . 'AccessToken';
//update access token
$user->$setter($response->getAccessToken());
return $user;
}
}
#app/config/routing.yml
#FosUserBundle Routes
fos_user_security:
resource: "@FOSUserBundle/Resources/config/routing/security.xml"
fos_user_profile:
resource: "@FOSUserBundle/Resources/config/routing/profile.xml"
prefix: /profile
fos_user_register:
resource: "@FOSUserBundle/Resources/config/routing/registration.xml"
prefix: /register
fos_user_resetting:
resource: "@FOSUserBundle/Resources/config/routing/resetting.xml"
prefix: /resetting
fos_user_change_password:
resource: "@FOSUserBundle/Resources/config/routing/change_password.xml"
prefix: /profile
#HWIOAuthBundle routes
hwi_oauth_security:
resource: "@HWIOAuthBundle/Resources/config/routing/login.xml"
prefix: /login
hwi_oauth_connect:
resource: "@HWIOAuthBundle/Resources/config/routing/connect.xml"
prefix: /login
hwi_oauth_redirect:
resource: "@HWIOAuthBundle/Resources/config/routing/redirect.xml"
prefix: /login
facebook_login:
pattern: /login/check-facebook
google_login:
pattern: /login/check-google
security:
encoders:
FOS\UserBundle\Model\UserInterface: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_USER
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
login_path: /login
check_path: /login_check
oauth:
resource_owners:
facebook: "/login/check-facebook"
google: "/login/check-google"
login_path: /login
failure_path: /login
oauth_user_provider:
#this is my custom user provider, created from FOSUBUserProvider - will manage the
#automatic user registration on your site, with data from the provider (facebook. google, etc.)
service: my_user_provider
logout: true
anonymous: true
login:
pattern: ^/login$
security: false
remember_me:
key: "%secret%"
lifetime: 31536000 # 365 days in seconds
path: /
domain: ~ # Defaults to the current domain from $_SERVER
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 }
#danvbe/UserBundle/Resources/services.yml
parameters:
my_user_provider.class: danvbe\UserBundle\Security\Core\User\FOSUBUserProvider
services:
my_user_provider:
class: "%my_user_provider.class%"
#this is the place where the properties are passed to the UserProvider - see config.yml
arguments: [@fos_user.user_manager,{facebook: facebook_id, google: google_id}]
<?php
namespace danvbe\UserBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @ORM\Entity(repositoryClass="danvbe\UserBundle\Repository\UserRepository")
* @ORM\Table(name="lcl_user")
*/
class User extends BaseUser
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/** @ORM\Column(name="facebook_id", type="string", length=255, nullable=true) */
protected $facebook_id;
/** @ORM\Column(name="facebook_access_token", type="string", length=255, nullable=true) */
protected $facebook_access_token;
/** @ORM\Column(name="google_id", type="string", length=255, nullable=true) */
protected $google_id;
/** @ORM\Column(name="google_access_token", type="string", length=255, nullable=true) */
protected $google_access_token;
//YOU CAN ADD MORE CODE HERE !
}
@caparkin
Copy link

Hi @danvbe

I have implemented your solution in Symfony 2.8.2. For some reason, I see the the popup window calls the facebook api, but always ends up on a FOSUserbundle login form (assuming it's defaults to this)

How could I go about solving this?

@alitvinenko
Copy link

@waynobweno, i have added this code

paths:
    email: email

in Facebook resource_owners.

My code:

hwi_oauth:
    connect:
        account_connector: footgears.oauth_user_provider
    firewall_name: main
    target_path_parameter: _destination
    fosub:
        username_iterations: 5
        properties:
            vkontakte: vkontakteId
            facebook: facebookId
    resource_owners:
        vkontakte:
            type: vkontakte
            client_id: %vk_client_id%
            client_secret: %vk_client_secret%
            scope: email
            user_response_class: Footgears\MainBundle\Security\VKUserResponse
        facebook:
            type: facebook
            client_id: %fb_client_id%
            client_secret: %fb_client_secret%
            scope: email
            paths:
                email: email

@axelbrethes
Copy link

The Bundle is ready on my application but when i register a member with facebook, all of columns in my user database [ firstname, lastname, email, etc ] are save with the facebookID's member. when i can update this with his facebook's email, firstname, lastname ?

Thanks

@siba1994
Copy link

i got an error like
ParameterNotFoundException in ParameterBag.php line 84:
You have requested a non-existent parameter "mailer_encryption".
what to do????
thanks

@RafalJaworski
Copy link

I got error on $form = $this->createForm('form'); . Did you get that?

@PhilETaylor
Copy link

@waynobweno did you get sorted? I have made great progress in Symfony3 getting this to work, I can now login/register with Facebook :) - if you still need help I can spend time putting some notes together?

@PhilETaylor
Copy link

I spoke too soon - having trouble connecting services to existing and logged in users... get a

Could not load type "form"

Exception... just cannot reverse engineer deep enough to work out why :(

@gabma
Copy link

gabma commented Aug 18, 2016

@quantizer @danvbe
If you use the $username (which returns the ID, right?) and set it as the password, you could suppose that anyone could log in on a users account if he has his email address, and finds his Facebook account Id ... which is not really complicated to figure out these days.

Wouldn’t it be more efficient to set the default password with the token?

@de-itsnotme
Copy link

Works fine on localhost but when I upload it to live server, I get the following error:

Fatal error: Class 'HWI\Bundle\OAuthBundle\HWIOAuthBundle' not found in /home/xyz/symfony3/app/AppKernel.php on line 21

AppKernel.php has such entry:

$bundles = [
            ...,
            ...,
            new HWI\Bundle\OAuthBundle\HWIOAuthBundle(),
        ];

What could be the problem? I'm using Symfony 3.1.1 on Windows for localhost.
PS: I have no access to the terminal as it is a shared hosting thus clear:cache is not possible. Alternatives?

@yamennassif
Copy link

@yamennassif
Copy link

@de-itsnotme did you composer require hwi/oauth-bundle on server ?

@grekpg
Copy link

grekpg commented Nov 10, 2016

@hariharasudhan94
Copy link

hariharasudhan94 commented Nov 18, 2016

hi i am new to hwiauthbundle i cant figure out why we use connect()

@jeromeheitor
Copy link

Very Nice and Clear Tutorial.
Thank You, you saved time to a lot of people !

@de-itsnotme
Copy link

Thanks @yamennassif, I don't know the reason but cache clear did solve the issue back then.

@varnitsaini
Copy link

varnitsaini commented Feb 28, 2017

@northern, i know its bit late but did you get the answer to redirect based on context of login after successful login? i am facing the same issue and i dont want to use default_target_path. thanks in advance!

@phpdeveloper1
Copy link

phpdeveloper1 commented Mar 8, 2017

I have implemented hwio auth bundle for facebook.But after facebook login I am redirected to failure_path instead of login_path.
My security.yml is as follows :

main:
            pattern: ^/
            form_login:
                provider: fos_userbundle
                csrf_provider: form.csrf_provider
                check_path: /login_check
                login_path: /login
                #use_forward: false
                #use_referer: false
                default_target_path: /artist
                
            oauth:
                resource_owners:
                    facebook:           "/login/check-facebook"
                login_path:        /login
                failure_path:      /login
                default_target_path: /artist
                oauth_user_provider:
                    service: hwi_oauth.user.provider.fosub_bridge
            logout:                target: /
            anonymous:    true
            switch_user:  { role: ROLE_ADMIN }
            context: primary_auth

My config.yml is as follows:

hwi_oauth:
    
    connect:
        account_connector: hwi_oauth.user.provider.fosub_bridge
          
    firewall_names: [main]
    fosub:
        username_iterations: 30
        properties:
            facebook: facebookId
    resource_owners:
        facebook:
            type:                facebook
            client_id:           "%fb_app_id%"
            client_secret:       "%fb_app_secret%"

@ernestocuzcueta
Copy link

Hi
Thanks for this good bundle. I'm using it from a year ago. But now I need modify the way to work, I only need connect to facebook by example only in the login action, not in the others requests.
Is possible only connect to the social network in the login, validate and later not connect more?

Thanks for your time
Regards Ernesto

@ducho
Copy link

ducho commented Apr 26, 2017

Hi.
Today I have same problem in Symfony3 as @speelgoedkoper. :(
Unrecognized field: facebook_id
500 Internal Server Error - ORMException
Configuration is same as @danvbe wrote....
Please, any suggestions ?

@ansien
Copy link

ansien commented May 11, 2017

Getting the error: No oauth code in the request.

hwi/HWIOAuthBundle#1217

Any ideas on how to fix this?

@iroegbu
Copy link

iroegbu commented May 17, 2017

I had problems using:

...
facebook_login:
    pattern: /login/check-facebook
...

Fixed by changing pattern to path
version: Symfony 3.2.8

@AdrienRosi
Copy link

Hi
I have this exception using custom provider (no problem width the hwi_oauth.user.provider.fosub_bridge) :

No property defined for entity for resource owner 'google'.

version: Symfony 3.3

@MatrixOfDeath
Copy link

Hey thanks for the documentation on this, I'm using Symfony3.3 and i'm meeting an issue, before implementing this SSO with google and facebook. I was already using FosUserbundle and it worked perfectly, to acces the login, my route is /login.
Now even if i still see my route, and if i renamed HwiOauth routes too /connect /connect/check etc... I can't seem to access my old working route to login normally (in that template I wanted to add 2 button with google and facebook).

@chinhnm
Copy link

chinhnm commented Nov 24, 2017

@ducho i have same problem like you. did you fix this issue?

@MarcinGladkowski
Copy link

Which url i have to add to google Api;

I have error like this: Error: redirect_uri_mismatch

@Whiax
Copy link

Whiax commented Feb 4, 2018

I didn't find a way to fix this "No oauth code in the request" following https://gist.github.com/danvbe/4476697
Does-it still work ? (Symfony 3)

#config

...

hwi_oauth:
    connect:
        account_connector: my_user_provider
    firewall_name: main
    fosub:
        username_iterations: 30
        properties:
            google: google_id
    resource_owners:
        google:
            type:                google
            client_id:           X.apps.googleusercontent.com
            client_secret:       X
            scope:              "email profile"

#security

firewalls:
    dev:
      pattern: ^/(_(profiler|wdt)|css|images|js)/
      security: false
    main:
      pattern:      ^/
      anonymous:    true
      provider:     main
      form_login:
        login_path: fos_user_security_login
        check_path: fos_user_security_check
      logout:
        path:       fos_user_security_logout
        target:     fos_user_security_login
      remember_me:
        secret:     "%secret%"
      oauth:
        resource_owners:
            google: "/login/google"
        login_path:        /login
        failure_path:      /login
        oauth_user_provider:
            service: my_user_provider

  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 }

#routing

hwi_oauth_security:
    resource: "@HWIOAuthBundle/Resources/config/routing/login.xml"
    prefix: /login

hwi_oauth_connect:
    resource: "@HWIOAuthBundle/Resources/config/routing/connect.xml"
    prefix: /login

hwi_oauth_redirect:
    resource: "@HWIOAuthBundle/Resources/config/routing/redirect.xml"
    prefix: /login

google_login:
    path: /login/google

#services

    my_user_provider:
        class: MainBundle\Security\Core\User\FOSUBUserProvider
        arguments: ['@fos_user.user_manager',{google: google_id}]`

@sharkman79
Copy link

I have a question where is located the file FOSUBUserProvider.php may be i haven't understood some question.

@MaxDeveloper0408
Copy link

my_user_provider:
    class: App\Security\Core\User\FOSUBUserProvider
    arguments: [ "@fos_user.user_manager",{ office365: office365_id } ]

I see this error now.
You have requested a non-existent service "fos_user.registration.form.factory".

@simonspts
Copy link

simonspts commented Jan 13, 2023

The Bundle is ready on my application but when i register a member with facebook, all of columns in my user database [ firstname, lastname, email, etc ] are save with the facebookID's member. when i can update this with his facebook's email, firstname, lastname ?

Thanks

Add this
`public function loadUserByOAuthUserResponse(UserResponseInterface $response)
{
$username = $response->getUsername();
$email = $response->getEmail();
$firstname = $response->getFirstName();
$lastname = $response->getLastName();
$user = $this->userManager->findUserBy(array($this->getProperty($response) => $username));
//when the user is registrating
if (null === $user) {
$service = $response->getResourceOwner()->getName();
$setter = 'set'.ucfirst($service);
$setter_id = $setter.'Id';
$setter_token = $setter.'AccessToken';
// create new user here
$user = $this->userManager->createUser();
$user->$setter_id($username);
$user->$setter_token($response->getAccessToken());
//I have set all requested data with the user's username
//modify here with relevant data
$user->setUsername($username);
$user->setEmail($email);
$user->setFirstname($firstname);
$user->setLastname($lastname);
$user->setPassword($username);
$user->setEnabled(true);
$this->userManager->updateUser($user);
return $user;
}

    //if user exists - go with the HWIOAuth way
    $user = parent::loadUserByOAuthUserResponse($response);

    $serviceName = $response->getResourceOwner()->getName();
    $setter = 'set' . ucfirst($serviceName) . 'AccessToken';

    //update access token
    $user->$setter($response->getAccessToken());

    return $user;
}`

Dont'f forget to add scope for facebook into hwi_oauth.yaml
scope: "public_profile"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment