Tag Archives: Symfony2

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.