3

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='+@card.short_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.

Aaron Vegh
  • 5,217
  • 7
  • 48
  • 75
  • Bah, I found the problem. I was using code from a previous developer, and he'd tucked away a redefinition of the Marshal method in environment.rb. Removed that and everything cleared through. The hours wasted! – Aaron Vegh Oct 11 '10 at 15:04
  • Just a note: "readbody=true" is not a header. It's actually part of the Ruby implementation of the response object. That was never sent over the wire by either party and is entirely unrelated to the issue you were having. – Bob Aman Nov 22 '10 at 21:40

2 Answers2

0

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.

Ashley Williams
  • 6,770
  • 4
  • 35
  • 42
  • Sorry, that doesn't help. The session values are clearly seen in the action (a call to "logger.debug "REQUEST: " + @request_token.inspect" yields the right data), and the same with the params value. My suspicion is falling on the call to get_access_token because I can't locate anything wrong with the line! But I don't know how to troubleshoot the code in the gem... – Aaron Vegh Oct 11 '10 at 14:20
  • And what about in `google_auth`, does `session[:request_token]` have the correct data there too? If so, post the entire stack trace. – Ashley Williams Oct 11 '10 at 14:28
0

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)
Bob Aman
  • 32,839
  • 9
  • 71
  • 95