I’ve spent the past 24 hours searching, reading and hacking my way to a very simple implementation of a Ruby on Rails based two-legged OAuth provider. I now have a working solution and I thought I’d share seeing as I was unable to find a solid resource on this for a Rails site.
A quick disclaimer: this hasn’t been tested extensively, though I am pretty confident that this will get you off the ground for a basic set of GET/POST/PUT/DELETE requests sending along form data (Content-Type: application/x-www-form-urlencoded for PUT/POST requests).
A Bit About OAuth
Now, I’m not going to dive into the low-level details of how two-legged works, but I feel it is important to know what OAuth is trying to accomplish and what the two-legged version is doing. OAuth is an open standard that was developed so that websites with APIs (ie Twitter) could allow users to securely grant third party websites access to their data. OAuth accomplishes this without ever giving the user’s password to the third party. Thus, you can authorize a specific client (TweetDeck) access to a specific user’s account information. The entire process is accomplished with a sequence of access/authorization tokens that are issued to the third party only when the end user explicitly grants access to the party. This is what is happening when you authorize a website access to your Twitter account data. So, traditional OAuth (aka three-legged OAuth) was created to securely grant third parties access to user data. Two-legged OAuth, while similar to three-legged, does not control access to specific user data. This means that you can control access on a per-client basis, but the protocol doesn’t give you user data access control.
As I said, the entire process is build upon the exchange of access keys. Two-legged OAuth requires two keys need to be known by both the client and the server. The first key, the public key, is transported publicly in the HTTP request and used as a client identifier. The second key, called the secret key, is known by both the client and server but is never transported in the HTTP request. The client uses this secret key to hash the request (which is then added to the request as a parameter) before sending it to the server. Upon receiving the request, the API looks up the client’s secret key based on the public key provided and then hashes the request with that secret key. If the hashes match, the client is authenticated and the request is allowed. The key here is that only the server and client know the secret key, ensuring that only the client can sign requests that the server will accept.
Note that this method only gives you a secure method to authenticate clients, the data in the request is still transported in plain text.
The Code
We’re going to use the Ruby OAuth gem to streamline this process. The gem provides the basic request hashing functionality, so all we’ll have to do is integrate this with Rails. To install this, simply insert one line into your Gemfile and run a bundle install:
gem 'oauth'
To get this working our Rails app needs to store API public key/secret key pairs so that we can authenticate our clients. We’ll store the key pairs in our database, allowing a simple lookup method and the ability to store meta data about our API clients if needed. Using this setup we could implement throttling and custom access controls, but these topics are beyond the scope of this post. Lets first write our database migration to get the table setup; I’m going to keep this incredibly simple for the purposes of this post.
class Client < ActiveRecord::Migration def change create_table :client do |t| t.string :api_key t.string :secret t.timestamps end end end
Great, run that migration to get the table into your database and we’ll move on to performing the authentication. In order to accomplish this we’re going to add a protected method to the ApplicationController and run before processing each request using a before_filter
require 'rack' require 'rack/request' require 'oauth' class ApplicationController < ActionController::Base before_filter :run_oauth_check # ... protected def run_oauth_check render :json => { :error => "Invalid request" }, :status => 400 unless params[:oauth_consumer_key] client = Client.find_by_api_key params[:oauth_consumer_key] render :json => { :error => "Invalid credentials" }, :status => 401 unless client != nil begin signature = OAuth::Signature.build(::Rack::Request.new(env)) do |rp| [nil, client.secret] end render :json => { :error => "Invalid credentials" }, :status => 401 unless signature.verify rescue Auth::Signature::UnknownSignatureMethod => e render :json => { :error => "Unknown signature method" }, :status =>; 400 end end end
Let’s take a look at what is happening here. First, we check to make sure that oauth_consumer_key is specified in the request. This is a parameter required by the OAuth spec that contains our client’s public API key. Next, we pull the client’s data from our database so we can verify the signature with their secret key. If no client is found with the provided API key, we reject the request. Once we’ve done that, all we have to do is use the OAuth gem to build the signature. This is done by first instantiating a new Rack request object and providing it to the signature builder. Finally, we have to account for the situation where the client didn’t provide their signature method in the request with a rescue block (the OAuth spec has more information about the different request signing methods – basically they’re different hash functions).
That’s it! Requests signed with incorrect credentials will now be rejected, and authorized requests will be allowed through. The important takeaway from this example is the signature verification process. I’ve included everything else as an example of its use, but so long as you can build the Rack request as we did above, you can implement this code in many different contexts.
Philip Southam has a great post on signing the requests in both Python and Ruby here. The post also contains a tutorial for this same setup in Python.
Thank you, thank you, thank you for finally making this easy to do. I needed a few changes to get it working with Rails 3 but they were minimal and this has been a light at the end of a long confusing tunnel.
Phil, can you help me out? I’m trying to implement it.
If you can, add my facebook.com/ricardonacif
Thanks!
I am genuinely grateful to the owner of tbis siote whho has
shared this enormous piece of writing at here.
Simply desire to say your article is as amazing. The clarity for your post is simply great and that
i can assume you’re knowledgeable on this subject. Well with your
permission allow me to take hold of your RSS feed to stay up
to date with coming near near post. Thanks one million and please keep up the
rewarding work.
Very good blog! Do you have any tips and hints for
aspiring writers? I’m hoping to start my own website soon but
I’m a little lost on everything. Would you advise starting with a free
platform like WordPress or go for a paid option? There are so many options out there that I’m totally
confused .. Any suggestions? Many thanks!
Nice blog right here! Also your site lots up very fast!
What web host are you the usage of? Can I get your affiliate link on your host?
I want my website loaded up as fast as yours lol
Hi, this weekend is good in support of me, for the reason that
this time i am reading this enormous informative paragraph
here at my house.
Thank you a bunch for sharing this with all people you
really know what you are talking approximately!
Bookmarked. Kindly also consult with my site =). We can have a hyperlink alternate contract between us
@Ricardo – I don’t have Facebook, but hopefully this gist will help you out: https://gist.github.com/4661334
Hello there! Perfect content! I really enjoy strategy
that you explained Simple Two-Legged OAuth Provider in Rails.
One more awesome material by great researcher.
Thank you for everyone web log! Number of washes alluring to check out the
public posts.
Kind of have got a skill level! I have to become nearly by
going to re-writing since you, on the other hand it’s rather than these cup
of tea .
When this happens Normally visit this website writing
service review (Jesus) by which I’m see really good comparisons and judge trustworthy publishingspecialist
always i used to read smaller posts which as well clear
their motive, and that is also happening with this article which
I am reading at this place.
This web site truly has all of the info I wanted concerning this subject and didn’t know who to ask.