Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a separate url for signin via Facebook using FOSFacebookBundle in Symfony2

Tags:

php

symfony

I need to have a separate url for plain signin form (username/password) which already exist on /login, and some url like /login-via-facebook to login via FOSFacebookBundle oauth procedure.

Now I can't understand how to trigger oauth-facebook procedure by url, it works only if I try to access url which is listed in "access_control".

Thanks in advance!


@Matt, thank you very much for your the explanation! I tried to follow your answer, but still have a problem, I didn't mention that I already using FOSUserBundle,

my security.yml:

providers:
    chain_provider:
      providers: [fos_userbundle, fos_facebook]
    fos_userbundle:
        id: fos_user.user_manager
    fos_facebook:
        id: fos_facebook.auth 

firewalls:      
    public:
      pattern:   ^/
      fos_facebook:
        app_url: ""
        server_url: ""
        login_path: /login
        check_path: /login_check/facebook
        provider: fos_userbundle
      fos_userbundle:
        login_path: /login
        check_path: /login_check/form
        provider: fos_userbundle
      anonymous: true
      logout:    true`

so, at this point it throws an exception: InvalidConfigurationException: Unrecognized options "fos_userbundle" under "security.firewalls.public", and if I change fos_userbundle to form_login in public firewalls (but should I do this at all?), it throws an exception You must configure the check path to be handled by the firewall using form_login in your security firewall configuration.

like image 637
synthetic Avatar asked Nov 17 '11 10:11

synthetic


3 Answers

Did you check your security.yml for:

factories:
    - "%kernel.root_dir%/../vendor/bundles/FOS/FacebookBundle/Resources/config/security_factories.xml"

It's important because it will charge the missing options for the service.

like image 142
Jose Antonio Pio Gil Avatar answered Nov 04 '22 16:11

Jose Antonio Pio Gil


The trick is to have two separated entries in your firewall, one for the form login and one for the facebook login. But in my case, I have a single login url where the user can either log in using his credentials or click on a Facebook connect to authenticate via the Facebook OAuth2 API using the FOSFacebookBundle. Here a sample of my security.yml config file to make this work:

security:
  factories:
    - "%kernel.root_dir%/../vendor/bundles/FOS/FacebookBundle/Resources/config/security_factories.xml"

  providers:
    chain_provider:
      providers: [acme.form_provider, acme.facebook_provider]
    acme.form_provider:
      id: acme.user_provider.form
    acme.facebook_provider:
      id: acme.user_provider.facebook

  firewalls:
    dev:
      pattern:  ^/(_(profiler|wdt)|css|images|js)/
      security: false

    public:
      pattern:   ^/
      fos_facebook:
        app_url: "your_app_url"
        server_url: "your_server_url"
        login_path: /login
        check_path: /login_check/facebook
        provider: acme.facebook_provider
      form_login:
        login_path: /login
        check_path: /login_check/form
        provider: acme.form_provider
      anonymous: true
      logout:    true

  role_hierarchy:
    ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

And here a sample of my routing.yml file that is used to define security routes required to make this work:

    # This is defined to let the user log in. The controller for
    # route display the login form and a facebook connect button
_security_login:
    pattern:   /login
    defaults:  { _controller: AcmeAcmeBundle:Main:login }

# This is defined for the form login authentication, 
# no controller is associated with it
_security_check_form:
    pattern:   /login_check/form

# This is defined for facebook login authentication, 
# a controller is associated with it but does nothing
_security_check_facebook:
    pattern:   /login_check/facebook
            defaults:  { _controller: AcmeAcmeBundle:Main:loginCheckFacebook }

_security_logout:
    pattern:   /logout
    defaults:  { _controller: AcmeAcmeBundle:Main:logout }

Using the security.yml before, the security mechanism will check for user logged in using form prior to user logged in with facebook. This is because the order in which providers are defined in the chain_provider. On each request, the FOSFacebookBundle check if a facebook oauth2 cookie is present, if so, it tries to load your user. If it fails to find the cookie, the authentication process will try another provider, if it is defined after the facebook provider.

In your case, when the user tries to go to a protected url (in access_control), the facebook login page is shown and he authenticate, then he is redirected to your site, the cookie is found and the user is authenticated successfully by the FOSFacebookBundle. To manually trigger the authentication process, place a facebook connect button on your site, then, in javascript, redirects the user to another page of your site. This way, the cookie will be set by the connect button and the FOSFacebookBundle will authenticate him on the next request. What I do, is to redirect the user to the login_check path for facebook in javascript when the facebook connect button has been successfull. This way, the security mechanism will redirect him where it is stated in the security configuration.

I hope this will help you to achieve what you want. Do not hesite to ask further question if something is unclear.

@Follow-up #1

Indeed, you cannot put fos_userbundle under the node public in the firewall configuration because it is not a valid option. The string fos_userbundle is used to reference FOSUserBundle UserProvider class. I never used this bundle but reading the documentation, you should use form_login. You can look at the documentation of FOSUserBundle in Step #5 and below. The error you mentionning is odd because it tells you that the login_path is not handled by the firewall but it's the case since the firewall matchs anything that start with a / (^/). Not sure what is going wrong but you're going in the right direction. Use form_login and try to check why it's not working from there.

Regards,
Matt

like image 33
Matt Avatar answered Nov 04 '22 16:11

Matt


There's a simple example Bundle which implements both FOSUserBundle and FOSFacebookBundle if you'd like to look https://github.com/ollietb/OhFOSFacebookUserBundle

like image 1
Ollie Harridge Avatar answered Nov 04 '22 16:11

Ollie Harridge