Aaron Vegh
Aaron Vegh

Reputation: 5217

Ruby OAuth Nightmare: Using Contacts API

I've been spending the last few days banging my head against the wall on supporting the ability to add a contact to the Google Contacts API in my Rails 3 application. Despite many false starts, I've finally made some progress by employing the Ruby OAuth gem, and following the tutorial here: http://everburning.com/news/google-analytics-oauth-and-ruby-oh-my/

When I follow this in the console, I get further than I do in my Rails app. I can create an access token, authenticate against Google's service with the specific scope of the Contacts API, and apply the oauth_verifier token to get an access token. But when it comes time to push the data, I get this error:

response = at.post("https://www.google.com/m8/feeds/contacts/default/full", gdata)
 => #<Net::HTTPUnauthorized 401 Unknown authorization header readbody=true> 

Where does the "readbody=true" header come from, and how would I get rid of it?

But it's worse in the Rails app. I have one controller action ("googlecontacts") that creates the request token and leads the user to the authentication site with Google:

def googlecontacts

@card = Card.find_by_short_link(params[:id])

@consumer = OAuth::Consumer.new( 
  'anonymous', 
  'anonymous', 
  { 
    :site => 'https://www.google.com', 
    :request_token_path => '/accounts/OAuthGetRequestToken', 
    :access_token_path => '/accounts/OAuthGetAccessToken', 
    :authorize_path => '/accounts/OAuthAuthorizeToken', 
    :signature_method => 'HMAC-SHA1',
    :oauth_version => '1.0'
  })

@request_token = @consumer.get_request_token(
  {:oauth_callback => 'http://monkey.dev/cards/google_auth?redir='[email protected]_link}, 
  {:scope => "https://www.google.com/m8/feeds/"}
)

session[:request_token] = @request_token

redirect_to @request_token.authorize_url

end

This appears to work; I get a working request token object, and the user is forwarded to the Google service to authenticate. The callback URL ("google_auth") should take the oauth_verifier token to create an access token. Here's the beginning of the controller:

def google_auth

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

And here's where it craps out. The error on that last line is:

You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.[]

But the values that are in there -- the session[:request_token] and the params[:oauth_verifier] -- are present and accounted for in that action! I can't figure out what is nil here.

So I guess I need to figure out this second problem first, but bonus points for answering the first problem as well. :-)

Thanks for reading.

Aaron.

Upvotes: 3

Views: 2657

Answers (2)

Bob Aman
Bob Aman

Reputation: 33249

Unknown authorization header typically means that your signature didn't match what you sent. I do not recommend the oauth gem. It's full of bugs and weird issues and it doesn't properly escape certain parameters.

The Signet gem is the officially supported gem for accessing Google APIs in Ruby.

Here's how you'd implement this with Signet:

require 'signet/oauth_1/client'
require 'addressable/uri'
card = Card.find_by_short_link(params[:id])
callback = Addressable::URI.parse('http://monkey.dev/cards/google_auth')
callback.query_values = {'redir' => card.short_link}

client = Signet::OAuth1::Client.new(
  :temporary_credential_uri =>
    'https://www.google.com/accounts/OAuthGetRequestToken',
  :authorization_uri =>
    'https://www.google.com/accounts/OAuthAuthorizeToken',
  :token_credential_uri =>
    'https://www.google.com/accounts/OAuthGetAccessToken',
  :client_credential_key => 'anonymous',
  :client_credential_secret => 'anonymous',
  :callback => callback
)

session[:temporary_credential] = (
  client.fetch_temporary_credential!(:additional_parameters => {
    :scope => 'https://www.google.com/m8/feeds/'
  })
)
redirect_to(client.authorization_uri)

Upvotes: 0

Ashley Williams
Ashley Williams

Reputation: 6840

Try setting/getting the session data with a string not symbol, i.e. session["request_token"], not session[:request_token]. I know I've had that issue before in the past.

Upvotes: 0

Related Questions