Developing OAuth clients in Ruby

Posted by Pelle February 23rd, 2008 22 comments edit

On the request of many people here is a quick guide to developing OAuth Consumer Application (Consumer==Client in OAuth Speak) in Ruby.

I will be using Agree2 as the sample application here, so feel free to go Register and load up a irb session to follow along. You could also do the same with Twitter’s OAuth or any other OAuth server.

The general process is:

  1. Register your consumer application with the OAuth compliant service to receive your Consumer Credentials (This is only done once)
  2. You initiate the OAuth Token exchange process for a user by requesting a RequestToken from the Service
  3. You store the RequestToken in your database or in the users session object
  4. You redirect your user to the service providers authorize_url with the RequestToken’s key appended
  5. Your user is asked by the service provider to authorize your RequestToken
  6. Your user clicks yes and is redirected to your CallBack URL
  7. Your callback action exchanges the RequestToken for an AccessToken
  8. Now you can access your users data by performing http requests signed by your consumer credentials and the AccessToken.
  9. ????
  10. PROFIT!!!

Get your Consumer Credentials

Once you are logged in to Agree2 click the Manage OAuth Applications link in the footer:

All OAuth capable applications require you to register your own application first to get your consumer credentials:

Click Register your application

Enter the name of your application, the url of your application, the callback url and an optional support url.

The callback url is the url that Agree2 redirects to after a user has authorized a token for you. For now just enter a url like http://myapp.com/oauth_client/callback. Click register and hey presto:

These are your applications Consumer Credentials.

Hooking up your code

As we are nice guys here at Agree2 also provides actual sample Ruby code on the credentials screen. I will go through this step by step.

First of all you need to install the oauth gem (make sure you have at least 0.2.2):

sudo gem install oauth

Your code needs to require the gem and the consumer part of the library:

gem 'oauth'
require 'oauth/consumer'

Instantiate your Consumer object with your credentials:

@consumer=OAuth::Consumer.new "AVff2raXvhMUxFnif06g", 
                              "u0zg77R1bQqbzutAusJYmTxqeUpWVt7U2TjWlzbVZkA", 
                              {:site=>"https://agree2.com"}

Now request a token from Agree2. This method actually performs a signed http request to https://agree2.com/oauth/request_token :

@request_token=@consumer.get_request_token

Now you need to redirect the user to the authorize_url

If you’re in irb just output the url:

@request_token.authorize_url

In a real rails application you would perform a redirect:

redirect_to @request_token.authorize_url

The user will be taken to this screen to authorize the token:

I think we need to work a bit on the user interface for this. But it does work. The user authorizes the token. and the user is redirected to the callback url you specified earlier.

In your callback action you now need to exchange the request token for an AccessToken:

@access_token=@request_token.get_access_token

Now you are ready to do whatever you wanted to do:

# Request all your users agreements
@response=@access_token.get "/agreements.xml"

The access token object has all the normal http request methods and returns a standard ruby http response.

Our next step is to integrate this with ActiveResource. This is being worked on now. Once this is done I will update this tutorial.

If your company needs help getting your OAuth Strategy right or implementing OAuth in your application I’m available for consulting work [email protected].

Posted February 23rd, 2008 under:
Comments
aishwarya_aishwaryaa@yahoo.com
Ajit February 24th, 2008 destroy

This is all very informative! There is almost a step-by-step illustration to the whole process. Thanks. I shall follow the general procedure.

ajturner@highearthorbit.com
Andrew Turner February 27th, 2008 destroy

Your tutorial is missing some key steps.

It assumes the @request_token and @consumer stay around. This obviously isn’t true if you want some way to only requiring authorization once, but then repeatedly access resources – say, in a Rails app.

What are the steps to recreating a client after you have stored the Access Tokens?

humbroll@gmail.com
humbroll March 2nd, 2008 destroy

thanks for your tutorial~!
what a fantastic simple steps.
cheer up~!

Arjan van Bentem April 19th, 2008 destroy

<p>@Andrew, get authorised tokens once (using Ruby or some <a href=“http://term.ie/oauth/example/client.php”>online test client</a>) and ensure the server keeps the tokens authorised for a long time. Next:</p>
<pre>
@consumer= (just like above)

accesstoken = OAuth::AccessToken.new(consumer, ‘access token’, ‘access token secret’)
</pre>

Arjan van Bentem June 22nd, 2008 destroy

To set another User-Agent or Accept in the HTTP headers, use something like:

response=access_token.get “/agreements.xml”, {"User-Agent" => “my user agent”}

anthonyeden@gmail.com
Anthony Eden May 27th, 2009 destroy

Since it’s not completely obvious:

Once you have the access token you can get the token string and the token secret (respectively) as follows:

@access_token.token @access_token.secret

You would need to save these two strings and use them to reconstruct the access token later:

access_token = OAuth::AccessToken.new(consumer, ‘token’, ‘secret’)

Hope this helps.

teefan82@yahoo.com
Teefan June 23rd, 2009 destroy

Hi,

Re-constructing a new access token as access_token = OAuth:AccessToken.new(consumer, ‘token’, ‘secret’) does not work for Yahoo OAuth. I keeps getting signature_invalid error. Are there something wrong with the encoding of the oauth_signature parameter?

Thanks,
Teefan

StanG September 17th, 2009 destroy

I am trying to make a client (in python, but it’s not the problem).

I can’t really understand how the user will interact (or will be provided option) if it’s CLI (or even if its GUI – how to design to ""let the server show the option"" – any API call?)?

c_ciocan at yahoo dot com
claude October 22nd, 2009 destroy

i’m trying to test all of this within irb, I can generate the request token and i go and approve the request in the browser, but when i try to access the tokeni get this:
@
access_token=request_token.get_access_token
path: /oauth/access_token
OAuth::Unauthorized: 401 Unauthorized
from /Library/Ruby/Gems/1.8/gems/oauth-0.3.6/lib/oauth/consumer.rb:201:in `token_request’
from /Library/Ruby/Gems/1.8/gems/oauth-0.3.6/lib/oauth/tokens/request_token.rb:18:in `get_access_token’
from (irb):23
@

on the server the request goes through, but then returns the 401

@
Processing OauthController#access_token (for 64.183.112.84 at 2009-10-22 15:58:36) [POST]
Parameters: {"oauth_nonce"=>"uHk3HvbmD1N7XnWGoAZF3M58OalRIQJTmcuW2AVdQ", “action”=>"access_token", “oauth_timestamp”=>"1256252307", “oauth_signature_method”=>"HMAC-SHA1", “controller”=>"oauth", “oauth_token”=>"GpfsPinMUSoBYgzx38ZJ", “oauth_consumer_key”=>"CrOSkI3UJjYXQzvxwukC", “oauth_signature”=>"0iSPV6D+Yjq1U03yeNWEC7s2frw=", “oauth_version”=>"1.0"}
[[4;35;1mOauthToken Columns (4.0ms)[[0m [[0mSHOW FIELDS FROM `oauth_tokens`[[0m
[[4;36;1mOauthToken Load (0.0ms)[[0m [[0;1mSELECT * FROM `oauth_tokens` WHERE (`oauth_tokens`.`token` = ‘GpfsPinMUSoBYgzx38ZJ’) LIMIT 1[[0m
[[4;35;1mRequestToken Columns (4.0ms)[[0m [[0mSHOW FIELDS FROM `oauth_tokens`[[0m
[[4;36;1mClientApplication Columns (0.0ms)[[0m [[0;1mSHOW FIELDS FROM `client_applications`[[0m
[[4;35;1mClientApplication Load (0.0ms)[[0m [[0mSELECT * FROM `client_applications` WHERE (`client_applications`.`id` = 1) ^[[0m
^[[4;36;1mUser Columns (4.0ms)
[[0m [[0;1mSHOW FIELDS FROM `users`[[0m
[[4;35;1mUser Load (0.0ms)[[0m [[0mSELECT * FROM `users` WHERE (`users`.`id` = 2) ^[[0m
^[[4;36;1mOauthNonce Columns (0.0ms)
[[0m [[0;1mSHOW FIELDS FROM `oauth_nonces`[[0m
[[4;35;1mSQL (0.0ms)[[0m [[0mBEGIN[[0m
[[4;36;1mOauthNonce Load (0.0ms)[[0m [[0;1mSELECT `oauth_nonces`.id FROM `oauth_nonces` WHERE (`oauth_nonces`.`nonce` = BINARY ‘uHk3HvbmD1N7XnWGoAZF3M58OalRIQJTmcuW2AVdQ’ AND `oauth_nonces`.timestamp = 1256252307) LIMIT 1[[0m
[[4;35;1mOauthNonce Create (0.0ms)[[0m [[0mINSERT INTO `oauth_nonces` (`created_at`, `timestamp`, `updated_at`, `nonce`) VALUES[[0m
[[4;36;1mSQL (28.0ms)[[0m [[0;1mCOMMIT[[0m
Completed in 112ms (View: 0, DB: 40) | 401 Unauthorized [http://claude.circlestreet.com/oauth/access_token]
@

Any ideas?

nilesh@betterlabs.net
Nilesh November 10th, 2009 destroy

@claude

Replace your

@access_token = @request_token.get_access_token

with

@access_token = @request_token.get_access_token(:oauth_verifier => params[:oauth_verifier])

Earlier it worked without :oauth_verifier, but is required now.

dibyajyoti@iviztechnosolutions.com
dg January 6th, 2010 destroy

While attempting to register my app on OAuth service provider I’m getting the following error:

ActiveRecord::StatementInvalid in Oauth clientsController#create

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘key FROM `client_applications` WHERE (`client_applications`.key = ’NHpO78hhm’ at line 1: SELECT key FROM `client_applications` WHERE (`client_applications`.key = ‘NHpO78hhmZzJWESLAMGw’)

I think this is due to the key attribute in client_applications table. We need to access this attribute as quoted `key` but for some reason ActiveRecord seems to behave strangely.

harryche888@gmail.com
harry May 10th, 2010 destroy

Hi,

I tried the sample code with MySpace OAuth API, but I keep getting “OAuth::Unauthorized (401 Unauthorized)” errors.

Here is code:

def test_oauth

consumer_key = “http://www.myspace.com/533816209”
consumer_secret = “03edd8f05e33428285582e4ae5be116149f9ff9e680e4227a19975847600259c”
#consumer_key = “ea873b9d77994e4293b05d097149f88d”
#consumer_secret = “a9607a38c1304744bdba00432cb3112912b71a74c6cd4244a89b66e2f87664f0”

@consumer = OAuth::Consumer.new(consumer_key, consumer_secret,
:http_method => :get,
:site=>"http://api.myspace.com",
:request_token_path => OAUTH_REQUEST_TOKEN_URL,
:access_token_path => OAUTH_ACCESS_TOKEN_URL,
:authorize_path => OAUTH_AUTHORIZATION_URL)

#return render(:text => @consumer.inspect)

@request_token = @consumer.get_request_token

session[:request_token] = @request_token

redirect_to @request_token.authorize_url

end

It errors on the line where it tries to get request token. My error log shows:

OAuth::Unauthorized (401 Unauthorized):
/usr/local/lib/ruby/gems/1.8/gems/oauth-0.4.0/lib/oauth/consumer.rb:215:in `token_request’
/usr/local/lib/ruby/gems/1.8/gems/oauth-0.4.0/lib/oauth/consumer.rb:136:in `get_request_token’
/app/controllers/my_space_controller.rb:37:in `test_oauth’
/usr/local/lib/ruby/gems/1.8/gems/actionpack-2.1.0/lib/action_controller/base.rb:1162:in `send’
/usr/local/lib/ruby/gems/1.8/gems/actionpack-2.1.0/lib/action_controller/base.rb:1162:in `perform_action_

Any ideas?

Thanks,
Harry

awaage@youffiliate.com
Andy May 12th, 2010 destroy

Thanks for this tutorial! I wrote an article about the pros and cons for using OAuth in our application, and why we chose not to use OAuth for the time being.

http://blog.youffiliate.com/2010/05/pros-and-cons-of-openid-authentication-oauth/

rajalakshmivelu@gmail.com
saranya October 11th, 2010 destroy

HI

when i follow in this line
access_token=request_token.get_access_token

I came across with the following error
401 Unauthorized errror

Anybody plz provide me the solution

tupagar@gmail.com
Tushar November 30th, 2010 destroy

Thanks Nilesh…
for getting access token, following code should be used:

@access_token = @request_token.get_access_token(:oauth_verifier => params[:oauth_verifier])

vibhor.mahajan@gmail.com
Vibhor Mahajan May 9th, 2011 destroy

3. Making an API call to receive the OAuth 1.0a tokens for the REST API.

jacky.jihao@gmail.com
Hao September 3rd, 2011 destroy

Thanks for this tutorial!
It really helped me understand the oauth gem.

alex@stinky.com
Alex Chaffee November 6th, 2011 destroy

.../oauth-0.4.4/lib/oauth/client/helper.rb:64:in `amend_user_agent_header': uninitialized constant OAuth::VERSION (NameError)

to avoid the above, use

require 'oauth'

instead of

require 'oauth/consumer'

hendrik.stein@hhv.de
hendrik stein December 1st, 2011 destroy

Hey Pelle! Thanks for your tutorial – works great for twitter! But when I try to use it against api.discogs.com I always get a “406 Not Acceptable” when i call “get_request_token”

I think this is because the discogs-api only accepts calls including “Accept-Encoding: gzip” in request-header

http://www.discogs.com/developers/oauth.html

http://www.discogs.com/developers/accessing.html#required-headers

Is there a possibiltiy to change the Header before or during the call?! I tried several thing – but without success…

mail@sebastian-engel.de
Juuro March 16th, 2012 destroy

When I use this:

@access_token = @request_token.get_access_token(:oauth_verifier => params[:oauth_verifier])

I get this error:

oauth.rb:13:in `

‘: undefined local variable or method `params’ for main:Object (NameError)

What can I do?

pooja@wazabegroup.com
pooja May 14th, 2012 destroy

hi,
I am doing the justin.tv api integration in asp.net, but i am not able to get access_token.
even i have other parameters like consumer key , secret, request_token . but when i try 2 get access_token, it raises error. There is no solution in asp.net in google. Kindly suggest the sol.

Thanx,
Pooja

pooja@wazabegroup.com
pooja May 14th, 2012 destroy

hi,

I got the following error.
When try to get access_Token.

Request for uri: http://api.justin.tv/oauth/access_token failed. status code: Unauthorized headers: Connection: close status: 401 Unauthorized x-runtime: 0.021571 x-ua-compatible: IE=Edge,chrome=1 x-geo: IN Content-Length: 1 Cache-Control: max-age=0, private, must-revalidate Content-Type: text/html; charset=utf-8 Date: Mon, 14 May 2012 04:49:14 GMT Set-Cookie: language=en; domain=.justin.tv; path=/; expires=Fri, 14-May-2032 04:49:14 GMT Server: nginx Via: Twice 0.2 web6:3345 body: