Category Archives: Dev Tutorials

Programming tutorials of different languages and technologies.

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.

How to create a dynamic list of checkboxes in Android, part 1

A brief introduction…

The first app that I developed with Android was very simple and the screen that I started with, had a search function. The user could search food stores near a city, selecting the desired type of food. This screen was fairly simple, with these input elements: a city spinner, a group of checkboxes for the food types, and a search button. As simple as it seems, when you are a total newbie to the technology, is difficult to get started.

So my first decision was to tackle only the layout and the user interface at first, with no worries about the source of the data or the search function in the beginning. This lead me to quickly create these two string arrays in strings.xml, just to test the UI of the activity.

01
<string-array name="cities">
02
    <item>Mar del Plata</item>
03
    <item>Buenos Aires</item>
04
</string-array>
05
<string-array name="foodTypes">
06
    <item>Pasta</item>
07
    <item>Pie</item>
08
    <item>Pizza</item>
09
    <item>Grill</item>
10
    <item>Sea Food</item>
11
    <item>Salads</item>
12
    <item>Desserts</item>
13
    <item>Quick Meals</item>
14
</string-array>

Great! Now to the code that would make these items into the UI elements… The spinner was quite straightforward, the layout element in the layout.xml file is (I’m using RelativeLayout, but i’m showing only the spinner):

1
<Spinner android:id="@+id/cities_spinner"
2
    android:layout_width="fill_parent"
3
    android:layout_height="wrap_content" 
4
    android:layout_below="@id/cities_label"
5
    />

And inside the onCreate method of my activity:

1
//create spinner with cities
2
Spinner spinner = (Spinner) findViewById(R.id.cities_spinner);
3
// Create an ArrayAdapter using the string array and a default spinner layout
4
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
5
    R.array.cities, android.R.layout.simple_spinner_item);
6
// Specify the layout to use when the list of choices appears
7
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
8
spinner.setAdapter(adapter);

What’s the logic behind all this code?

First, you need to get the spinner that’s defined in your activity layout file, and to do that in execution time, you could use the findViewById method.

Then, you need a way to load all your string array items into the spinner, and you do exactly that, using an adapter. An adapter is a mediator between the UI element (the spinner in this case), and a source of data (the string array in this example), and is responsible for creating a UI representation of each element of the source data (creating a View object for each one). In this example I used an ArrayAdapter, that is an Android predefined subclass that helps with source data inside arrays. The interesting part here, is that you specify the way you want the elements to display in the spinner, using setDropDownViewResource method from the Adapter. The parameter passed to this method it’s an Android predefined element to show a text as a spinner item.

At last, you set this adapter to your spinner, an is done! Your spinner now shows all the items in the string array that was defined in the strings.xml file.

And now, let’s tackle the checkboxes

Ok, so lets go down to business… this post was about a dynamic list of checkboxes, or not?

I’m sorry for that introduction into spinners, but being this app my first one, I thought it couldn’t be so much complicated to tackle the checkboxes part. It should be quite similar to the city spinner. And it was… at the end. But I got stuck trying to solve this. There are a lot of questions and examples drifting around this issue on the net, and I got confused. So I included that part as an introduction, and as a way to compare the little differences between the spinner and the checkboxes examples.

Remember that my task here was pretty simple, showing a string array as a list of checkboxes that the user could check and uncheck. And here is how I got that running. For the layout, I defined a GridView element, like this:

1
<GridView android:id="@+id/foodtype_list"
2
        android:layout_width="fill_parent"
3
        android:layout_height="wrap_content"
4
        android:numColumns="auto_fit"
5
        android:choiceMode="multipleChoice"
6
        android:layout_below="@id/foodtype_label"        
7
        ></GridView>

And inside the onCreate method of my activity, just under the spinner code, I wrote this:

1
//create checkboxes from string array for the food types
2
String[] foodTypes = getResources().getStringArray(R.array.foodTypes); 
3
ArrayAdapter<String> adapter2 = new ArrayAdapter<String>(this,
4
    android.R.layout.simple_list_item_multiple_choice, foodTypes);
5
GridView gridView = (GridView) findViewById(R.id.foodtype_list);
6
gridView.setAdapter(adapter2);
7
for (int i = 0; i < adapter2.getCount(); i++) {
8
    gridView.setItemChecked(i, true);
9
}

Yes, I know, it’s basically the same thing as with the spinner. But that’s the point here. You create an adapter from the string array, and define it to use another Android predefined element as layout template. In this case, I did this in the constructor directly.

The for block is just to always set all the checkboxes into checked state.

This is not a finished activity, just the first draft of one screen. I’ll be posting updates on this example to show how to evolve this simple and “hardcoded” activity into one that loads the cities and food types from storage, and can get the selections made by the user.