Adding HWIOAuthBundle to your Symfony2 project

When it comes the time to add OAuth support to your Symfony2 project, and allow your users to login using their accounts of Facebook, Google, Twitter, Linkedin, etc., it really makes sense to use some of those great third-party bundles that already solve this problem.

One of these bundles is the HWIOAuthBundle. This is a great bundle that allows you very easily to add support for authenticating users via OAuth1.0a or OAuth2 in Symfony2, handling more than 20 providers.

But as today, there is only a little problem… documentation is not as detailed as some may want. This is only my very small contribution after fighting to make it work.

The most simple working example

When installing and testing a new S2 bundle, almost always I do it the same way: testing the most simple example I can, and make it work, so I can start refactoring and adding complexity later. And this was not the exception. What follows is my own very simple example of how to get started with HWIOAuthBundle.

All bundles come with their respective documentation, with instructions on how to install, configure and use it. Why am I repeating it here? In my own experience, following the current version of the documentation was not as “pleasant” as I expected (You can read the official docs here and I encourage you to do so, because I’m not explaining each configuration option in detail here ).

So here it goes my example:

Setting up the bundle

First of all, you need to add the bundle to your composer.json file. Please remember to update your vendors after doing this:

1
2
<code lang="javascript[lines]">{
3
  "require": {
4
    "hwi/oauth-bundle": "*"
5
  }
6
}

Enabling the bundle

Then you must enable the bundle on your AppKernel.php file:

01
// app/AppKernel.php
02
 
03
 
04
 
05
public function registerBundles() {
06
    $bundles = array(
07
        // ...
08
        new HWI\Bundle\OAuthBundle\HWIOAuthBundle(),
09
    );
10
}

Importing the bundle routing

1
hwi_oauth_login:
2
    resource: "@HWIOAuthBundle/Resources/config/routing/login.xml"
3
    prefix:   /login

 

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

Configuring your resource owners

In this example, we are adding support for Linkedin. Add the following to your app/config/config.yml:

1
#HWI OAuthBundle
2
hwi_oauth:
3
    firewall_name: main
4
    resource_owners:
5
        linkedin:
6
            type: linkedin
7
            client_id: %linkedin_client_id%
8
            client_secret: %linkedin_client_secret%
9
            scope: r_fullprofile

The firewall_name parameter must match the name of the security.yml firewall. The parameters between %% are defined in the parameters.yml file, but you could add them directly here if you want to. Follow the instructions and links here to know how to work with Linkedin API.

Implementing a user provider

The most simple way you can do this is by creating a class that extends a default implementation of OAuthUserProvider class and configuring it as a service in your project:

1
namespace IB\UserBundle\Model;

 

use HWI\Bundle\OAuthBundle\Security\Core\User\OAuthUserProvider as BaseOAuthUserProvider;

 

class OAuthUserProvider extends BaseOAuthUserProvider { }

and in the service.xml file of the UserBundle in this example project:

1
<parameters>
2
        <parameter key="ib_user.oauth_user_provider.class">IB\UserBundle\Model\OAuthUserProvider
3
    </parameter></parameters>

 


            

 

 

Configuring the firewall

In your security.yml file you should add this:

01
02
security:
03
    ...
04
    providers:
05
        ...
06
        my_custom_hwi_provider:
07
             id: ib_user.oauth_user_provider
08
    ...
09
    firewalls:
10
        main:
11
            pattern: ^/
12
            anonymous: true
13
            oauth:
14
                resource_owners:
15
                    linkedin: "/login/check-linkedin"
16
                login_path: /login
17
                failure_path: /login
18
                oauth_user_provider:
19
                    service: ib_user.oauth_user_provider

 

access_control:
– { path: ^/login, role: IS_AUTHENTICATED_ANONYMOUSLY }
– { path: ^/connect, role: IS_AUTHENTICATED_ANONYMOUSLY }
– { path: ^/, role: ROLE_USER }

Adding the check path to the routing.yml

You should add the corresponding login check route to your routing.yml, under the imported routes (/login and /connect):

1
linkedin_login:
2
    pattern: /login/check-linkedin

And that should be it. You should have now your simple example of HWIOAuthBundle working…

My own sad story…

As I said a few lines above, my initial experience with the bundle was not a happy one. That’s why I created this kind of tutorial of how to setup a minimal working example. These are the steps that I did, and how I tried to solve the problems as they appeared:

Problem 1:

I followed exactly what the docs instructed but got the error: No route found for “GET /login”.

The problem was that the docs only instruct you to import the /connect (redirect.xml) routes. In my example I added the /login (routes.xml) routes.

Problem 2:

Tried again with this little change… step forward it seemed, but I got a different error now: The browser reported a “too many redirections” problem. Reviewing the logs, I saw that the /login route was called over an over in a loop, because “no security token was found”.

So, a firewall problem… what I missed (and the official docs also) was to add the “anonymous: true” to the firewall definition in security.yml. Every time the page was redirected to /login, the firewall intercepted it as a protected URI, and it denied access because no anonymous users were allowed and the current user has no security context… so it redirects to the route configured to authenticate the user, that was… /login… auch!

Problem 3:

Testing again and… wow, it seems to work now. It showed a page with a link with the text “linkedin”. I clicked on it, thinking that I should be redirected to the Linkedin site asking for my permission, but instead… the same page appeared again, with the link “linkedin”. What’s happening? The logs reveal that, again, an infinite loop was taking place, but now on this new page… why? Well, now the link is pointing to the /connect/{service} and this route is inside the firewall, so… whenever I clicked on it, the access was denied, because I didn’t have permission yet to access that route, and it redirected me to the configured route to authenticate myself, which was /login… and this now automatically redirected me again to the page with the “linkedin” link.

Great, found the problem… so now I added the exception to the access_control list to admit the role “IS_AUTHENTICATED_ANONYMOUSLY” to the /connect path.

I guess that this could also be solved by using the same route pattern of /login when importing the redirect.xml, instead of using /connect, but I wanted to preserve as much as I could of the original instructions of the docs.

Problem 4:

Well, at this point it started to work as I expected. I try to enter a protected resource in my example site, it showed this ugly page with a link to go to “linkedin” to get authorization. I identified myself in Linkedin, and clicked the Authorize button… and it tries to comeback to my site, but it shows me a a beautiful Symfony2 ghost error saying: There is no user provider for user “HWI\Bundle\OAuthBundle\Security\Core\User\OAuthUser”…

OMG! This isn’t going to end soon, right? So now what? … I added a “my_custom_hwi_provider” user provider to the “providers” section of the security.yml (You can see it already there in my example).

Of course that to test this part, I had to prepare an “online” environment to be able to receive the control back from Linkedin. This is not going to work if you are testing it in your own developer machine without your server being able to receive requests from the outside world.

Final try…

Now we are talking… it seems to work. I’m now finally seeing my protected resource and I’m very happy to see this in the Symfony2 debug bar:

It's alive!

It’s alive!

Some final thoughts…

This is a great bundle, and it’s a work in progress. The developers are putting a lot of effort and time trying to make a good bundle for all of us… This is my way to try to help some beginner Symfony2 developers that might encounter some problems trying to make this work, and maybe don’t have the time to dig around to find the causes. And if you are like me, that usually copy and paste every example, well… you’ll have to be a little more careful this time.

27 thoughts on “Adding HWIOAuthBundle to your Symfony2 project

  1. cordoval

    you started well with a lot of detail but toward the end you lost detail and that was the most important part, you should have kept showing images, etc. Screen shots are a lot of help and stick to step 1, 2, 3 pattern. Also i bet you customize templates, that part was also important.

    Reply
    1. Diego Post author

      Thanks for your comment!
      Are you asking for more detail in the part of the article where I explain the problems that I had?
      The example (the first part) is almost as complete as the test site that I built. The only thing that I left out of it is a “page” that’s inside the protected URL pattern of the main firewall. I didn’t include it here because it’s just the default example that symfony2 provides you when you use the generate:bundle command, so I didn’t customize anything else that’s not shown already in the post… When you access this protected page using the classical /hello/{yourname}, the HWIOAuthBundle authenticates you using Linkedin, and then you get redirected to that page.

      Reply
  2. Ahmed

    Have you ever met this error “SSL certificate problem, verify that the CA cert is OK. Details:
    error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed ”

    while coming back from google, been pulling my hair for hours

    Reply
    1. Diego Post author

      I’m sorry Ahmed, I’ve never had that problem… Maybe some specific requirement with the Google API? I tested this example using Linkedin, and it comes back well.

      Reply
    2. Ahmed AbdelSamad

      Hey there ahmed, i had a similar problem with facebook a while ago
      Facebook::$CURL_OPTS[CURLOPT_SSL_VERIFYPEER] = false;
      Facebook::$CURL_OPTS[CURLOPT_SSL_VERIFYHOST] = 2;

      it was solved by tweaking the curl options you might want to try and do something like that
      I hope it helps

      Reply
  3. Drazen

    Hi Diego!
    Great post, it really helped me. Thank you.
    I Also had problem with SSL certificate trying to connect on facebook.
    I manage to pass it by switching CURLOPT_SSL_VERIFYPEER to FALSE
    in vendor/kriswallsmith/buzz/lib/Buzz/Client/AbstractCurl.php

    Reply
  4. Manoj

    i am get error..
    Unrecognized options “oauth” under “security.firewalls.main”

    here my security.tml

    security:
    encoders:
    Symfony\Component\Security\Core\User\User: plaintext

    role_hierarchy:
    ROLE_ADMIN: ROLE_USER
    ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    providers:
    in_memory:
    memory:
    users:
    user: { password: userpass, roles: [ ‘ROLE_USER’ ] }
    admin: { password: adminpass, roles: [ ‘ROLE_ADMIN’ ] }
    my_custom_hwi_provider:
    id: user.oauth_user_provider

    firewalls:
    main:
    pattern: ^/
    oauth:
    resource_owners:
    linkedin: “/login/check-linkedin”
    login_path: /login
    failure_path: /login
    oauth_user_provider:
    service: user.oauth_user_provider

    #anonymous: ~
    #http_basic:
    # realm: “Secured Demo Area”

    access_control:
    – { path: ^/demo/secured/hello/admin/, roles: ROLE_ADMIN }
    #- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }
    – { path: ^/login, role: IS_AUTHENTICATED_ANONYMOUSLY }

    – { path: ^/connect, role: IS_AUTHENTICATED_ANONYMOUSLY }

    – { path: ^/, role: ROLE_USER }

    in this code what kind of change needed?

    Reply
  5. Michael

    You made my day! After trying out every help document, I was nearly giving up, really. But now I found your great post and I had the exact same problems… Honestly, the official docs at github for this are crappy.

    Your solution really helped me out, but one possible improvement with your solution. You can leave out the creation of the service class. Just use “HWI\Bundle\OAuthBundle\Security\Core\User\OAuthUserProvider” as the class name when registering the service in the config.xml or config.yml, this also works like a charm! (instead of the “IB\UserBundle\Model\OAuthUserProvider” you used)

    Reply
  6. Tyler

    Thanks, Diego! The documentation for this bundle needs a little work. Your post was the only thing that I found to contain start-to-finish steps. Great stuff!

    Reply
  7. Acony

    There is no user provider for user “HWI\Bundle\OAuthBundle\Security\Core\User\OAuthUser”…

    How did u solve this error ? My user is added to database regulary but no way to avoid this error and to finally get authenticated.

    Reply
    1. Florian

      As said in “Problem 4”, you need to create your own service, register it in providers and set it as the firewall user provider.

      Reply
  8. Florian

    Very useful, thanks !
    I actually had exactly the same problems (from 1 to 4) than you.. That saved me lots of time !
    Maybe we should contribute to improve the documention ?

    Reply
  9. joe

    Thank you very much(especially for the problems part) I ran into the same problems with the same sequence, I had already got to problem 2 when I found myself here. I’m gonna read the article again and again and debug my project :)

    Reply
  10. bvvgopal

    Hi,
    I’m trying to use this on my template , but I’m getting this error: No resource owner with name ‘check-facebook’, when click on that link.

    Below are the files.

    security.yml

    security:
    encoders:
    Phoenix\CoreBundle\Entity\User\User:
    algorithm: sha1
    encode_as_base64: false
    iterations: 1
    providers:
    users:
    id: phoenix.core.manager.user
    fos_userbundle:
    id: fos_user.user_provider.username_email
    firewalls:
    backend:
    pattern: ^/admin
    anonymous: ~
    form_login:
    login_path: /admin/login
    check_path: /admin/login_check
    always_use_default_target_path: true
    default_target_path: /admin
    logout:
    path: /admin/logout
    target: /admin/login?logout=true
    frontend:
    pattern: ^/
    anonymous: ~
    form_login:
    csrf_provider: form.csrf_provider
    failure_path: /user/login
    login_path: /user/login
    check_path: /user/login_check
    success_handler: phoenix.frontend.security.authentication.success_handler
    failure_handler: phoenix.frontend.security.authentication.failure_handler
    always_use_default_target_path: false
    logout:
    path: /user/logout
    target: /
    remember_me:
    key: “%secret%”
    lifetime: 31536000
    path: /
    domain: ~
    secured_area:
    pattern: ^/
    anonymous: true
    oauth:
    resource_owners:
    facebook: “/login/check-facebook”
    login_path: /login
    failure_path: /login
    check_path: /login_check
    default_target_path: /

    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
    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:
    #Backendbundle
    – { path: ^/admin/(login|login_check)$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    – { path: ^/admin, roles: ROLE_ADMIN }
    #Frontbundle
    – { path: ^/login/$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    – { path: ^/connect, role: IS_AUTHENTICATED_ANONYMOUSLY }
    – { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }

    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_redirect:
    resource: “@HWIOAuthBundle/Resources/config/routing/redirect.xml”
    prefix: /login

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

    facebook_login:
    pattern: /login/check-facebook

    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: secured_area
    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_client_id%
    client_secret: %facebook_client_secret%
    scope: email
    infos_url: “https://graph.facebook.com/me?fields=username,name,email,picture.type(large)”
    paths:
    email: email
    profilepicture: picture.data.url
    options:
    display: popup #dialog is optimized for popup window
    services:
    hwi_oauth.user.provider.entity:
    class: HWI\Bundle\OAuthBundle\Security\Core\User\OAuthUserProvider

    Reply
  11. Konstantin

    Thank you very much! Original docs still incomplete!

    One thing I’ve done differently. It appears the bundle has user provider service for OAuthUserProvider class, so I don’t need to create new, just configure:

    security:
    providers:
    oauth:
    id: hwi_oauth.user.provider
    firewalls:
    secured_area:
    oauth:
    oauth_user_provider:
    service: hwi_oauth.user.provider

    Reply
  12. Ismail

    After login facebook I get this exception
    Class ‘HWI\Bundle\OAuthBundle\Security\Core\User\OAuthUser’ should have a method ‘setFacebookId’

    in hwi/oauth-bundle/HWI/Bundle/OAuthBundle/Security/Core/User/FOSUBUserProvider.php at line 94

    Reply
  13. Michael

    I want to upload the profile picture to amazon s3. For this I need the service. But I cannot inject a service_container into the FOSUBUserProvider.php. How is it possible to access the service_container from this file?

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *