OnSwipe redirect code

Thursday, May 29, 2014

My response to request for feedback from AAP (Aam Aadmi Party)

Today I got an email asking me for feedback on AAP (Aam Admi Party). The first question was

What would make you volunteer in future? What stopped you from volunteering this time? *
Give us as much detail as you'd like

I took the sub-text literally and a little seriously and wrote a really detailed answer. I thought I might as well put it up here and share with the world (or the one or two who actually read my blog.. :P ). Anyways here it goes :

Not interested in volunteering for AAP.

AAP as a party has acted in the most irresponsible, selfish and foolhardy manner ever since it formed the government in Delhi. People of Delhi whole heartedly supported AAP with the hope that they would get some real stable governance for the next 5 years. Instead what they got was utter chaos.

I expected changes like corruption free governance, no undue fiscal burden on the state, public works development, fulfillment of election promises for once, but all I got to see was a couple of filmy style sacking of corrupt officers, some agitation and finally a resignation just after 49 days. If that was not irresponsible then what is?

And what do you do after resigning? You forget Delhi altogether and try to become the prime minister of India. If this isn't selfish then what is it?

Prime minister of India, no less. Are you kidding me? You could not run the state of Delhi for two months and you expect me to hand over the reins of the country to you for the next 5 years?

Alright lets assume that you genuinely felt that contesting the Lok Sabha elections was a key thing and something that you had do. What did you do? Again like the typical filmy style you go out like an underdog hero and challenge the most popular leader, in a place which is not even your home turf. If this is not foolhardiness then what is it? How much would it have mattered whom you win against. Wouldn't you be able to serve the people better being an MP from somewhere in Delhi (your home turf) instead of creating all this drama of challenging Mr. Modi from Varanasi. Look at how Dr. Jayaprakash Narayan (http://en.wikipedia.org/wiki/Jayaprakash_Narayan_(Lok_Satta) ) has done it. His party was never in power anywhere, but that never deterred him from becoming one of the best performing MLAs. I believe you wanted to show that you are not afraid of facing anyone in elections and you have the peoples' mandate to contest and win against Mr. Modi. But who cares about that Mr. Kejriwal? Sure you might have put up a good fight, but what does it account to in the end? All that I see is that you were trying to be a bollywood hero in the Indian politics, who wants to the take on the big bad guy (again as declared by yourself). You now lost your chance to voice the opinion of the people you represent in the LokSabha.

Then comes the ultimate flip-flop you did with the Delhi assembly. Until LokSabha election results were announced you kept hounding the Lt. Governor to dissolve the assembly. You even approached the Supreme Court for this. Then on one fine Tuesday you go and meet the Lt. Governor and make an announcement to the public that you have requested the Lt. Governor to NOT dissolve the assembly but give you another chance and that you will ask people about forming the government again and then decide. And the next day (on Wednesday) you make an yet another U-turn announcing that you have asked the Lt. Governor to dissolve the assembly and you will now ask people about contesting again in the elections.

How do you think this reflects on you? You accuse every politician out there of corruption, double standards and every other accusation possible and you yourself act in such an irresponsible and opaque manner, saying contradictory things on a daily basis and on top of it claim to be "THE ONLY HONEST POLITICAL PARTY". How can I believe anything you say after you have done so much of circus?

All this drama eroded complete faith in the capability of AAP to form and run a government in any form, let alone provide better governance than the existing political parties.

Please note that I have talked only about your own actions and what I feel about those. I have not mentioned any of the other accusations made about AAP or Mr. Kejriwal, like Mr. Kejriwal coming to Varanasi by train saying he has Rs. 500/- in his pocket and going back by flight, or the episode around Mr. Kejriwal's official residence as CM of Delhi, or the incident of Mr. Somnath Bharti and certain people of African origin or the mayraid images and posts circulated on the social network which, of course, you totally accuse as baseless and a defamation propaganda by the IT cell of BJP. I am not even making the comparison between Mr. Modi's Gujarat model and your achievements, because you say that its all an eyewash. So be it. That's why I have mentioned only the actions of AAP and it's leader Mr. Kejriwal.

Now a few times AAP has come out and said that it is a new political party with inexperienced people and hence they could have made some mistakes. It's good that you acknowledge this. But with this statement you can't ask people to hand over the country's administration to you. If you are new and inexperienced, then start with the basics. Gain that experience that you think is needed. I don't have to tell you that you need to start from the grass-root levels. Making noise in urban places, parading urban youth with the "Aam Aadmi" cap is one thing and working at village panchayat level, addressing rural problems is entirely different.

You do not have to be a MLA or MP to provide good governance. Those are not the only places where good politicians are needed. Those are not the only places where big time scams and plundering of public funds is done. Gram panchayat, taluk panchayat and Zilla Parishad are equally in need of good politicians and bureaucrats. If one were to accumulate the amount of money siphoned off in the name of NREGA through out the country then I am sure it will beat any of the other big scams that the previous government(s) have been accused of. The amount of pilfering that happens is insane. It easily runs into crores for each panchayat, every year. And I know this first hand.

Start at those places. Let people across the state and country know you, not just by your agitations, but by real work that you do, by some real meaningful change that you bring about. It's ok if you are unable to bring in a strong legislation like Jan Lokpal. You don't have to throw your hands up in the air in despair saying you were not given enough support. There are other innumerable things that you can and should do and those will have a big positive impact on people even without the Jan Lokpal. If you tell me that there is absolutely no machinery to fight corruption in our system currently then you are absolutely wrong. You do not have to put every single corrupt bureaucrat in jail or suspend them. It's sufficient if you make an example out of one or some of them in a few departments on a regular basis. The rest will automatically shy away from corruption. And you have enough ways to enforce such a thing.

Also, corruption is not the only problem plaguing our country. There are numerous other things that require attention. Being in a position of power allows you to address these other issues while you carefully maneuver the juggernaut that our system is. It doesn't have to be a head on collision. It's not a "Last man standing" kind of battle. I have personally seen government officials doing this in a very intelligent manner at various levels and I have seen it from very close quarters. It is possible. It is doable. Above all it brings out the desired positive changes and makes the lives of people better. It provides them what they really crave for. It's going to be difficult no doubt, but our constitution and our legal system provide so many options to deal with this juggernaut. This same government infrastructure which the corrupt are mis-utilizing can be turned around as a weapon against them.

No matter which legislation you bring in, we will not be a corruption free state overnight. It's going to be a long road and you need to be patient about it. We as a country are definitely in a much better position than we were a few decades back. So we can get better. Its just that we have a huge inertia owing to our big mass (aka population). Change is difficult. One is easily tempted about the idea of bringing about a revolution and making overnight changes. Personally, I feel the cost involved for such a revolution is very high and more importantly the desired result is not guaranteed. In comparison the slow and steady here will indeed be the winner.

You ask me what would make me a AAP volunteer in future. I say look beyond cities. Look at the vast majority of semi-urban and rural places. Go there. Find like minded people in those places. I am sure they are there. Empower them with all that you have. You have top class lawyers with you who can carry out a PIL right up in the supreme court. You have contacts in the press. You can mobilize man power. You can draw the attention of the nation and beyond. You know how to effectively use the RTI. You run NGOs which, despite the allegations of being funded by the CIA through various organizations, can do a lot of good. Do all of that. Become a party of the rural India. Become a household name as the best choice in panchayat politics and then I will probably become an AAP volunteer. Well technically you will not need me at that point and thats a fine contradiction to have. :).

I say probably because apart from the stuff that I have said above I am not really a fan of AAP party principles, which according to me are very close to communist ideas. So that's altogether a different matter and a commentary on that will have meaning only after the above mentioned things are addressed.

Oh and one more thing which I almost forgot. Please groom local leaders everywhere. Or rope in existing local leaders whose thought process aligns with yours. Personally a tie-up with Loksatta would have been really good. Nevertheless, do not be a typical "high command controlled" party. Be true to your vision and words and be truly federal even in your party structure and operation.

P.S : I might have come across as a staunch BJP supporter through out, which at the moment is true. In the recently concluded elections BJP under the leadership of Mr. Narendra Modi has been the best option for the country according to me and I am positive about India's future under the current government. Ideologically though, I think Loksatta is more appealing to me than AAP.

Sunday, October 27, 2013

Invalid credentails : OmniAuth + oAuth2 + Rails 4 encrypted cookie store + simultaneous requests

OmniAuth is a very well know gem in the Ruby/Rails world. Almost every Rails application out there is probably using it to authenticate with one of the various mechanisms it supports. OmniAuth is just awesome.!

I have been using OmniAuth for about 3 years now in various Rails projects and it has worked very well, although I have had to monkey patch it once or twice to allow me to exploit some of the Facebook features (like authenticated referrals). But all in all, it just works as advertised and you will have an authentication system in pretty much no time at all.

Yesterday, however, I was having quite a bit of hard time getting OmniAuth to do a simple "Login with Facebook" oAuth2 authorization. It was something that had worked seamlessly on innumerable previous occasions. But yesterday it just kept failing repeatedly, succeeding only once in a while. And it always failed with the same obscure error "Invalid Credentials" during the callback phase. (OmniAuth operates in three phases : Setup, Request and Callback. Apart from OmniAuth wiki on github, this is a good place to read about it : http://www.slideshare.net/mbleigh/omniauth-from-the-ground-up). The fact that the error message was not so helpful made this whole process a lot more frustrating. After some hunting on the inter-webs I found that the culprit could be a bad "state" parameter.

Wait, what is this "state" parameter?

Background : oAuth-2 CSRF protection

oAuth2 specifies the uses of a non-guessable secure random string to be used as a "state" parameter to prevent CSRF attacks on oAuth. More details here : http://tools.ietf.org/html/rfc6749#section-10.12. This came out almost a year back and many oAuth providers implement it already, including Facebook. OmniAuth also implemented it last year. Although not written with the best grammar, this article will tell you why this "state" parameter is needed and what happens without it.

To sum it up, oAuth client (our web application) creates a random string, stores it in an accessible place and also sends it to the oAuth provider (Ex : Facebook) as "state" during the request phase. Facebook will keep it, authenticate the end user, ask for permissions and when granted sends a callback to our web application by redirecting the user back to our website "with the state" as a query parameter. The client (our web application) compares the "state" sent by the provider and the one it had stored previously and proceeds only if they match. If they don't then there is no proof that the callback that our web application received is actually from the provider. It could be from some other attacker trying to trick our web application into thinking (s)he is someone else.

In case of OmniAuth oAuth2, this state parameter is stored as a property in the session with the key 'omniauth.state' during the request phase. The result of the request phase is a redirect to the provider's URL. The new session with the "state" stored in it will be set on the client's browser when it receives this redirect (302) response for the request /auth/:provider (This is the default OmniAuth route to initiate the request phase). After the provider (Facebook) authenticates the user and user authorizes our application, the provider makes a callback to our application by redirecting the user back to our web application at the callback URL /auth/:provider/callback along with the "state" as a query parameter. When this callback URL is requested by the browser, the previously stored session cookie containing the 'omniauth.state' property is also sent to our web application.

OmniAuth checks both of these and proceeds only if they match. If they don't match it raises the above mentioned "Invalid Credentials" error. (Yeah, I know, not really a helpful error message..!).

Ok, that is good to hear, but why will there be a mismatch?

A mismatch is possible only if the session cookie stored on the user's browser is changed such that the 'omniauth.state' property is removed from it or altered after the request phase has set it. This can happen if a second request to our web application was initiated while the request phase of oAuth was running and it completed after the request phase completed but before the callback phase started. Sounds complex? The diagram below illustrates it.

The diagram makes it clear as to when and how the 'omniauth.state' gets removed from the session leading to the error. However, apart from the timeline requirements (i.e. when requests start and end), there is another essential criteria for this error to occur :
The response of the "other simultaneous request" must set a new session cookie, overriding the existing one. If it does not explicitly specify a session cookie in the response headers, the client's browser will retain the existing cookie and 'omniauth.state' will be preserved in the session.
Now, from what I have observed, Rails (or one of Rack middlewares) has this nifty feature of not serializing the session and setting the session cookie in the response headers, if the session has not changed in the course of processing a request. So, in our case, if the intermediate simultaneous request does not make any changes to the session, Rails will not explicitly set the session cookie, there by preventing the loss of 'omniauth.state' property in the session.

Ok, then why will the session cookie change and lose the 'omniauth.state' property?

One obvious thing is that the "other simultaneous request" might change the session - either add or remove or edit any of the properties. There however is another player involved.

This is where the "Encrypted Cookie Store" of Rails 4 comes into picture. Prior to Rails 4, Rails did not encrypt its session cookie. It merely signed it and verified the signature when it had to de-serialize the session from the request cookie. Read how Rails 3 handles cookies for a detailed breakdown. Rails 4 goes one step ahead and encrypts the session data with AES-256 (along with the old signing mechanism. More details on that coming up in a new post). The implementation used is AES-256-CBC from OpenSSL. I am not a Cryptography expert, but the way I understand it, the property of AES is that it results in a different cipher text every time you run the encryption for the same message plain text. Or it could also be because the Rails encryption scheme initializes the encryptor with a random initialization vector every time it encrypts a session (Implementation here). Either ways, the session cookie contents are always new for every request even when the actual session object or session contents remain unchanged. As a result Rails will always set the session cookie in the response header for every request and result in the browser updating that cookie in its cookie store.

In our case, this results in the session being clobbered at the end of the "other simultaneous request" and we end up losing the 'omniauth.state' property and oAuth fails.

Umm.. ok, but when and how does this happen in real world, if at all it can?!

All these requirements/constraints described above, especially the timing constraints makes one wonder if this can really happen in the real world. Well, for starters it happened to me (hence this blog post..!!). I also tried to think of scenarios other than mine where this would happen. Here are a couple that I could think of :

Scenario - 1) FB Login is in a popup window and the "Simultaneous request" is a XHR - Ex : an analytics or tracking request.

Here is the flow :
  1. User clicks on a "Login with FB" button on your website.
  2. You popup the FB Login page in a new popup window. Request phase is initiated. But there is a small window of time before the redirect response for '/auth/facebook' is received and 'omniauth.state' is set.
  3. During that small window of time, in the main window, you send an XHR to your web app to, may be, track the click on the "Login with FB" button. You might do this to just track usage or for some A/B testing or to build a funnel, etc. This request sends the session without the 'omniauth.state'.
  4. While the XHR is in progress, the redirect from the request phase is complete and the session with 'omniauth.state' is set. The user now sees FB Login page loading and proceeds to login once it is loaded.
  5. While the user is logging in to FB and approving our app, the XHR has completed and has come back with a session without 'omniauth.state'. This is stored by the browser now.
  6. Once user logs in and approves your app, the callback state starts. But the session sent to your web app is now missing the 'omniauth.state'. 
  7. oAuth fails.
How big a deal is this scenario?

If you are indeed making a XHR in the background, then this scenario needs to be taken care of. Since the "other simultaneous" request is automatically triggered every time, it is very likely that session will get clobbered.

How to solve this?

You can either first send the XHR and then in the response handler of that XHR, you can open the FB Login page in the popup. Also have a timeout just to make sure you don't wait for too long (or forever) before you receive a response for that XHR.

Alternatively, if you can push your tracking events in a queue stored in a cookie, you can do that and then open the FB Login page. Once the FB Login completes, you can pull that event out of the queue and send it. As a backup have a code that runs on every new page load to look for pending events from the queue in the cookie and send those events.

With HTML5 in place, its probably better to use the localstorage for the queue than the cookie. But again that needs user's permission. Your call.

Scenario - 2) FB Login is in the same window/tab but User has the website opened in two tabs.

Here is the flow :
  1. User has your website opened in a browser tab - Tab-1
  2. User opens a link on your website in a second tab - Tab-2 (Ctrl + Click or 'Open in a new tab' menu item). This request sends the session without 'omniauth.state'.
  3. While that Tab-2 is loading, user clicks on "Login with FB" in Tab-1 initiating the request phase.
  4.  If the request loading in Tab-2 is a little time consuming the redirect of the request phase of oAuth in Tab-1 completes before request in Tab-2, setting the session with 'omniauth.state'. After that FB Login page is shown and user proceeds to login and authorize.
  5. While the user is logging in, the request in Tab-2 completes, but with a session that is missing 'omniauth.state'.
  6. After logging in to FB, the callback phase is initiated with a redirect to your web app, but with a session that doesn't have 'omniauth.state'. 
  7. oAuth fails. 
How big a deal is this scenario?

Not a big deal actually. In your web app, in the oAuth failure handle, you can just redirect the user back to /auth/facebook, redoing the whole process again and guess what - this time it will succeed and that too without the user having to do anything because the user is already logged in to FB and has also authorized your app. But just to be on the safer side, you would want to be careful about this loop going infinite (i.e. You start FB auth, it fails and the failure handler restarts the FB auth). Setting a cookie (different from the session cookie) with the attempt count should be good. If the attempt count crosses a certain limit, send the user back to homepage or show up an error page or show a lolcats video, c'mon be creative.

Ok, those are two scenarios that I could think of. I am not sure if there are more.

Can OmniAuth change something to solve this?

I believe so. If OmniAuth uses a different signed and/or encrypted cookie to store the state value instead of the session cookie none of this session clobbering would result in loss of the state value. OmniAuth is a Rack based app and relies on the Session middleware. I am not entirely sure, but it can probably use the Cookie middleware instead. Just set its own '_oa_state' cookie and use that during callback for verification.

Will you send a pull request making this change?

I am not sure. I first will hit the OmniAuth mailing list and find out what the wise folks there have to say about this. If it makes sense and nobody in the awesome Ruby community provides an instant patch, I will try and send a patch myself.

Ok, so that was the awesome ride through oAuth workings inside the OmniAuth gem. In the course I got to know quite a bit of Rails and also Ruby internals. Looking forward to writing posts about those too. Okay, okay.. fine. I will try and keep those posts short and not make them this long..!!

Till then, happy oAuthing. :-/ !

P.S : Security experts, excuse me if I have used "authentication" and "authorization" in the wrong places. I guess I have used it interchangeably as web applications typically do both with oAuth2.