Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto register users after login with facebook #116

Closed
dmontero opened this issue Aug 29, 2012 · 38 comments
Closed

Auto register users after login with facebook #116

dmontero opened this issue Aug 29, 2012 · 38 comments

Comments

@dmontero
Copy link

Hi! I'm new to HWIOauth, and I've been experimenting with this great bundle and FOSUserBundle for a few days. I had several problems with the routing, configuration, etc. But now I'm being able to connect a facebook account to a registered (and logged) user. If the user is not registered and I send him to http://mysite.dev/connect/facebook I get this error: No route found for "GET /connect"

¿What am I doing wrong? I want a new user to just log-in using his facebook account and save him in the user's list.

My config.yml file

imports:
    - { resource: parameters.ini }
    - { resource: security.yml }

hwi_oauth:
    # configuration of oauth resource owners to use
    resource_owners:
        facebook:
            type: facebook
            client_id:xxxxxxxxxxxxxxxxxxxxx
            client_secret:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
            scope: ""
        twitter:
            type: twitter
            client_id: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
            client_secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

    # name of the firewall the oauth bundle is active in
    firewall_name: secured_area

    # optional FOSUserBundle integration
    fosub:
        # try 30 times to check if a username is available (foo, foo1, foo2 etc)
        username_iterations: 30

        # mapping between resource owners (see below) and properties
        properties:
            facebook: facebookId

    # if you want to use 'connect' and do not use the FOSUB integration, configure these separately
    connect: ~
#        registration_form_handler: my_registration_form_handler
#        registration_form: my_registration_form
#        connect_provider: my_link_provider # can be the same as your user provider

    # optional HTTP Client configuration
    http_client:
        timeout:       5
        verify_peer:   false
        ignore_errors: true
        max_redirects: 5

framework:
    #esi:             ~
    #translator:      { fallback: %locale% }
    secret:          %secret%
    router:
        resource: "%kernel.root_dir%/config/routing.yml"
        strict_requirements: %kernel.debug%
    form:            true
    csrf_protection: true
    validation:      { enable_annotations: true }
    templating:      { engines: ['twig'] } #assets_version: SomeVersionScheme
    default_locale:  %locale%
    trust_proxy_headers: false # Whether or not the Request object should trust proxy headers (X_FORWARDED_FOR/HTTP_CLIENT_IP)
    translator:      { fallback: en }
    session:         ~

# Twig Configuration
twig:
    debug:            %kernel.debug%
    strict_variables: %kernel.debug%

# Assetic Configuration
assetic:
    debug:          %kernel.debug%
    use_controller: false
    bundles:        [ ]
    #java: /usr/bin/java
    filters:
        cssrewrite: ~
        #closure:
        #    jar: %kernel.root_dir%/Resources/java/compiler.jar
        #yui_css:
        #    jar: %kernel.root_dir%/Resources/java/yuicompressor-2.4.7.jar

# Doctrine Configuration
doctrine:
    dbal:
        driver:   %database_driver%
        host:     %database_host%
        port:     %database_port%
        dbname:   %database_name%
        user:     %database_user%
        password: %database_password%
        charset:  UTF8

    orm:
        auto_generate_proxy_classes: %kernel.debug%
        auto_mapping: true

# Swiftmailer Configuration
swiftmailer:
    transport: %mailer_transport%
    host:      %mailer_host%
    username:  %mailer_user%
    password:  %mailer_password%
    spool:     { type: memory }

# FosUser   
fos_user:
    db_driver: orm
    firewall_name: main
    user_class: Miramos\UserBundle\Entity\User

My security.yml

security:
    providers:
        fos_userbundle:
            id: fos_user.user_manager

    encoders:
        FOS\UserBundle\Model\UserInterface: sha512


    firewalls:
        secured_area:
            pattern:    ^/
            form_login:
                provider: fos_userbundle
                login_path: /connect/
                check_path: /login/login_check
            anonymous: true
            logout: true
            oauth:
                resource_owners:
                    twitter: "/login/check-twitter"
                    facebook: "/login/check-facebook"
                login_path:        /connect
                failure_path:      /connect

                # FOSUB integration
                oauth_user_provider:
                    service: hwi_oauth.user.provider.fosub_bridge

My routing.yml

miramos_user:
    resource: "@MiramosUserBundle/Resources/config/routing.yml"
    prefix:   /

facebook_login:
    pattern: /login/check-facebook

google_login:
    pattern: /login/check-google

custom_login:
    pattern: /login/check-custom

github_login:
    pattern: /login/check-github



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

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

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





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

Thanks in advance

@dr-fozzy
Copy link

Any advice on this?

@ruudk
Copy link
Contributor

ruudk commented Sep 11, 2012

I have the same problem. Could someone please provide an example how to integrate this correctly?

@dmontero You should change your Facebook and Twitter API secrets because they are exposed now!

@vwasteels
Copy link

this works for me, the /login is defined by the FOSUserBundle :
fos_user_security_login ANY /login

you can check your routing rules : php app/console router:debug

firewalls:
    secured_area:
        pattern:    ^/
        anonymous: true
        logout: true
        form_login:
            provider: fos_userbundle
            login_path: /login
            check_path: /login_check
            csrf_provider: form.csrf_provider
        oauth:
            resource_owners:
                facebook: "/login/check-facebook"
            login_path: /login
            failure_path: /login
            oauth_user_provider:
                service: hwi_oauth.user.provider.fosub_bridge

@tobiassjosten
Copy link

I have a similar problem and even with the above configuration won't solve it. When someone tries registering by connecting with Facebook, they get the error "User '123456' not found."

Can this be made to work? Where is the user creation handled? I'm asking for the email scope, so preferably it would match existing users to the connectin Facebook user's email.

@Narretz
Copy link

Narretz commented Dec 11, 2012

A cleaner implementation could be to redirect the user to a registration form, which is prepopulated with basic info from Facebook.

@tobiassjosten
Copy link

Not sure it would be cleaner but it would certainly be a much worse user experience, rather than just clicking to be registered and logged in.

@yplam
Copy link

yplam commented Dec 14, 2012

I find a way to do this, but I don't know if is safe.

Create your own FOSUserProvider, override loadUserByOAuthUserResponse

public function loadUserByOAuthUserResponse(UserResponseInterface $response)
{
    $username = $response->getUsername();
    $user = $this->userManager->findUserBy(array($this->getProperty($response) => $username));

    if (null === $user) {
        //throw new AccountNotLinkedException(sprintf("User '%s' not found.", $username));
        // create new user here
        $user = $this->userManager->createUser()
        // ......
        // set user name, email ...
        // ......
        $this->userManager->updateUser($user);
    }

    return $user;
}

@tobiassjosten
Copy link

Thanks! That worked perfectly.

@asm89
Copy link
Contributor

asm89 commented Jan 8, 2013

Auto registering users after login might not be the best idea (IMO), but if you really want to do it look at the snippet above. :)

@asm89 asm89 closed this as completed Jan 8, 2013
@daFish
Copy link
Contributor

daFish commented Jan 15, 2013

@asm89 Want to elaborate any further why it is not a good idea? What should one do instead?

@aosmialowski
Copy link

@daFish in the above snippet, it may really hurt you if OAuth server does not provide user email address.

@daFish
Copy link
Contributor

daFish commented Jan 23, 2013

@osmialowski Yeah, after some more thought about the whole workflow, sending the user to the registration page with pre-filled fields seems to be much more robust than just trying to create the user.

@cryptiklemur
Copy link

@daFish how would you go about doing that?

@daFish
Copy link
Contributor

daFish commented Jun 19, 2013

@aequasi I haven't had time to try it out myself but you could do the following:

  • let the user login via a provider
  • after success redirect the user to fos_user_registration_register
  • listen to the FOSUserEvents::REGISTRATION_INITIALIZE-event from FOSUserBundle to pre-fill the user with the data from the OAuth-provider

@pentarim
Copy link

@daFish I would like to implement what you are suggesting, but where should this fos_user_registration_register redirect happen, in FOSUBUserProvider->loadUserByOAuthUserResponse ? What happens with tokens and with oauth provider data after redirect (should they be persisted in session?)

@daFish
Copy link
Contributor

daFish commented Jun 25, 2013

@pentarim That is exactly what I'd tried to do but ultimately failed. I used a RedirectResponse which was intercepted by ConnectController leading to an exception. Haven't had the time to work around this or find another way.
But while looking at the controller I saw a registrationAction which looks exactly what we need. Not sure how to use this. Maybe @asm89 or @stloyd can shed some light into the situation?

@Fraktl
Copy link

Fraktl commented Oct 31, 2013

@daFish Did you manage to redirect the user to a registration form after a succesful OAuth response? I'm facing the same problem. In my custom user provider I issue a redirect but the controllers expects a User object not a redirect response. Any ideas how you implemented this?

I sa the registrationAction in the ConnectController but I can't reach this...

@daFish
Copy link
Contributor

daFish commented Oct 31, 2013

@Fraktl Unfortunately not.

@asm89 @stloyd Is there any way to connect an already authenticated user? This seems supported but I can't find a way to do it. Maybe some hints would be great.

@devswaam
Copy link

i am also facing the same issue , can any one please help me how to solve it ?
Nothing above worked for me !

@tobiassjosten
Copy link

@devswaam Did you try to implement a custom user provider and have it look up existing users or create new ones on the fly? Else see @yplam's suggestion above — it worked perfectly for me.

@ryanotella
Copy link

The built-in FOSUBUserProvider actually does most of what people are describing. There are methods for connecting OAuth2 logins to existing users in the system. By default it creates the connection by comparing a specified field on the user table (i.e. "google_id", or "facebook_id"). But if those aren't already populated it won't work. Although it should prompt logged-in users to connect their accounts if configured to do so.

However, if you override the loadUserByOAuthUserResponse() method in the FOS User Provider Bridge (HWI\Bundle\OAuthBundle\Security\Core\User\FOSUBUserProvider) you can use the email address field to match existing users. Although Google, for one, doesn't recommend this.

In config.yml tell HWIOAuth2 to override the user provider:

parameters:
    hwi_oauth.user.provider.fosub_bridge.class: Acme\SecurityBundle\Security\Core\User\FOSConnectUserProvider

Overriding the loadUserByOAuthUserResponse() in your own user provider:

class FOSConnectUserProvider extends FOSUBUserProvider {
    /**
     * @param UserResponseInterface $response
     * @return \FOS\UserBundle\Model\UserInterface
     */
    public function loadUserByOAuthUserResponse(UserResponseInterface $response)
    {
        $username = $response->getUsername();

        $user = $this->userManager->findUserBy(array($this->getProperty($response) => $username));
        if (null === $user || null === $username) {

            /* We are going to trust the email from Google, even though they don't recommend it */
            $user = $this->userManager->findUserBy(array('username' => $response->getEmail()));

            if(null === $user) {
                throw new AccountNotLinkedException(sprintf("User matching ID '%s' was not found.", $username));
            }
        }

        return $user;
    }
}

@daFish
Copy link
Contributor

daFish commented Sep 23, 2014

@ryancastle Would you mind to add some documentation how to nicely connect already existing users. It might be already covered in some way in the docs but this isn't intuitive. This would certainly help many people.

@ryanotella
Copy link

Yes. When I'm finished my current implementation I was planning to fill in some of the missing steps in the docs.

@daFish
Copy link
Contributor

daFish commented Sep 23, 2014

@ryancastle 👍

@zazzou
Copy link

zazzou commented Sep 23, 2014

@aosmialowski
Copy link

@zazzou This gist should not be considered as a good practice.

@matheo
Copy link
Contributor

matheo commented Oct 10, 2014

@aosmialowski could you provide a better one?

@aosmialowski
Copy link

@matheo Yes, for this specific use case. And for this use case, the gists provided is not a good solution. I'd say it's terrible solution. Check out this blog post: http://www.osmialowski.co.uk/symfony-2-oauth-better-way-integrate-hwioauthbundle-fosuserbundle/.

@ryanotella
Copy link

@aosmialowski I suspect that findUserByUsernameAndEmail() method you're using there would allow people to break into someone else's account fairly easily, by forging their "real name" or nickname. I don't think Google has a terribly restrictive police around "real names" and other OAuth providers may be even more lax. Google explicitly says even the email address returned should not be trusted as a user identifier, although at least that will normally be validated somehow.

However, the default implementation that forces users to explicitly login and "connect" their accounts is not that nice either.

@aosmialowski
Copy link

@ryancastle but you are also wrong. The findUserByUsernameAndEmail() method is used only to check for uniqness of username and email address. Nothing more. Take a look at if conditional and the exception is thrown throw new AuthenticationException('Username or email has been already used.');.

At the begining of the process, the user is checked by ID returned from resource owner (and the resource owner name in my approach).

@ryanotella
Copy link

@aosmialowski Ah yes. You are right.

@leonardola
Copy link

@dappl
Copy link

dappl commented Jul 16, 2015

@aosmialowski You wrote on 22 Jan 2013 "@daFish in the above snippet, it may really hurt you if OAuth server does not provide user email address."

But in your example on github (https://github.com/aosmialowski/FOSUserBundle-HWIOAuthBundle-integration/blob/master/src/Owl/UserBundle/Security/User/Provider/OAuthUserProvider.php) you do it the same way?!

I also did it like that, but as you said, sometimes there is no email provided...
I don't know why, because my scope is on "public_profile, email, user_birthday".

So sending the user to a pre-filled sign up form is in my opinion the best way.

@aosmialowski
Copy link

@dappl In this snippet yes, but in real world application I have a listener which checks for user email address. If there's no email supplied user gets redirected to the "Add email" form. Without providing email, user can do nothing on the website (in this particular example).

Pre-filled sign up form? It's against the idea of form-less registration via 3rd party services IMHO.

@milokmet
Copy link

@aosmialowski Can you provide and example how to correctly redirect from OAuthUserProvider to "Add email" form? I would like to ask users for phone number, so I need to redirect them to a form before creating user. Thank you.

@aosmialowski
Copy link

@milokmet Depending on your use-case you can throw a custom exception, then create a listener that would handle thrown exception and redirect user to proper form.

@glennthehuman
Copy link

From OAuthUserProvider, how can I send a registration confirmation email and then redirect to "/register/check-email" ?

@glennthehuman
Copy link

#1334

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests