Updating Ruby Consumers and Providers to OAuth 1.0a
In a previous post, I did a quick run-through of the changes that were introduced in OAuth 1.0a. As promised, here's a rough guide to updating Ruby Consumers and Providers to 1.0a. Don't mind the pseudo code.
Updating Ruby OAuth Consumers to 1.0a
In order for things to work properly, you'll need to use a version of the OAuth gem that's at least 0.3.4.1 (0.3.5 was released on 6/3/09). To install it and check the version number:
sudo gem install oauth
oauth --version
Authorization code that once looked like this:
request_token = consumer.get_request_token
puts "Please visit the following URL to authorize this application:"
puts request_token.authorize_url(:oauth_callback => callback_url)
# wait for input
gets
access_token = request_token.get_access_token
Should now look like this:
request_token = consumer.get_request_token(:oauth_callback => callback_url)
puts "Please visit the following URL to authorize this application:"
puts request_token.authorize_url
# wait for input
puts "What's the value of `oauth_verifier`?"
oauth_verifier = gets.chomp
# `oauth_verifier` is extracted from the expanded callback URL or was displayed to the user
access_token = request_token.get_access_token(:oauth_verifier => oauth_verifier)
You can detect whether a Service Provider supports 1.0a:
request_token = consumer.get_request_token
puts "OAuth 1.0a detected" if request_token.callback_confirmed?
Updating Ruby OAuth Providers to 1.0a
As with the updates to consumer applications, you'll need to be running at least 0.3.4.1. For the sake of simplicity (and additional laziness on my part), I'll assume OAuth verification is baked into your Rails app rather than as Rack middleware (where it probably belongs).
You'll need to add a pair of columns to your oauth_request_tokens table (or
whatever it's called): callback and verifier.
Accepting oauth_callback During the Request Token Phase
The first step to supporting OAuth 1.0a is to accept oauth_token parameters when issuing Request Tokens. To do this, you'll need to make the OAuth::RequestProxy::ActionControllerRequest available to methods that run later in a request's lifecycle:
def verify_oauth_signature
valid = OAuth::Signature.verify(request) do |request_proxy|
# make the request proxy available outside this block
@_request_proxy = request_proxy
# proceed normally...
end
end
# accessor for the request proxy
def oauth_request_proxy
@_request_proxy
end
Once this is set up, you'll need to modify your request_token method to associate the oauth_callback parameter with your Request token and set oauth_callback_confirmed to true:
def request_token
request_token = new_request_token
# request_proxy provides unified interface to params + headers
request_token.callback = request_proxy.oauth_callback
request_token.save
render :text => "oauth_token=#{request_token.token}&" \
"oauth_token_secret=#{request_token.secret}&" \
"oauth_callback_confirmed=true"
end
Generating An oauth_verifier During the Authorization Phase
In addition to validating the Request Token during the authorization phase,
you'll want to generate an oauth_verifier value and return it to the
application via the pre-registered callback url (or display it to the user and
instruct them to enter it into their application).
def authorize
# display the authorization page
render and return unless request.post?
# validate the token
request_token = find_request_token(params[:oauth_token])
request_token.validated = true
# generate a verification code
request_token.verifier = generate_verifier
request_token.save
if request_token.callback?
# this was previously params[:oauth_callback]
redirect_to request_token.callback + "?oauth_verifier=#{request_token.verifier}"
else
@verifier = request_token.verifier
render # display the verification code to the user
end
end
Verifying Access Token Exchanges
When exchanging a Request Token for an Access Token, you need to confirm that the verification code provided by the consumer matches the one on file.
def access_token
# this is a correctly signed request: oauth_token has already been loaded
# compare the verifier from the request proxy to the one we generated
if oauth_token.verifier == request_proxy.oauth_verifier
# exchange the request token for an access token
access_token = oauth_token.exchange
render :text => "oauth_token=#{access_token.token}&" \
"oauth_token_secret=#{access_token.secret}"
else
raise OAuth::InvalidVerifier
end
end
Summary
Obviously, there are cleaner ways to do this, but they're presumably very specific to individual codebases. If you have questions, check out the oauth-ruby mailing list. Otherwise, patches can be submitted against https://github.com/mojodna/oauth/tree/mergeable.
Good luck!
