Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails, OmniAuth, google_oauth2, google-api-client, Moments.insert... 401 unauthorized... why?

I haven't been able to find an answer to this online -- aside from using the Google+ Sign In button, which I don't want to use at this point because I don't want to get into Javascript if I don't have to.

I have a Ruby on Rails application (ruby v1.9.3, rails v3.2.13) in which I've hooked up OmniAuth and I'm using the google_oauth2 gem to integrate with Google+.

My simple goal is to allow a user to authenticate with Google+, grant access to my Google API project, and then be able to post a moment to the Google+ user's vault using the google-api-client gem.

I have already setup my Google API Project, created the OAuth 2.0 for Web Applications, and enabled Google+ API service.

I have OmniAuth setup with the following provider and I've added the request_visible_actions option to allow me to post (I think this is correct but haven't seen this used from any code examples I've looked at online...):

provider :google_oauth2, CLIENT_ID, CLIENT_SECRET, {
    access_type: 'offline',
    scope: 'userinfo.email,userinfo.profile,plus.me,https://www.googleapis.com/auth/plus.login',
    request_visible_actions: 'http://schemas.google.com/AddActivity',
    redirect_uri: 'http://localhost/auth/google_oauth2/callback'
}

When I redirect my user to /auth/google_oauth2, it sends the user to Google+ to authorize my app and when the user approves, it returns to my callback where I can access the request.env["omniauth.auth"] and it has all the information I would expect, including tokens, email address, etc. I'm storing the access_token from auth["credentials"]["token"].

So far so good, right?

When I try to post the moment using the following code, I encounter an exception indicating a 401 unauthorized error.

client = Google::APIClient.new

client.authorization.access_token = self.access_token

plus = client.discovered_api('plus', 'v1')

moment = {
    :type => 'http://schemas.google.com/AddActivity',
    :target => { :id => Time.now.to_i.to_s,
               :description => message,
               :name => message
    }
}

# post a moment/activity to the vault/profile
req_opts = { :api_method => plus.moments.insert,
             :parameters => { :collection => 'vault', :userId => 'me', },
             :body_object => moment
}

response = client.execute!(req_opts).body

I have also tried replacing

client.authorization.access_token = self.access_token

with

credentials = Hash.new
credentials[:access_token] = self.access_token
credentials[:refresh_token] = self.refresh_token
credentials[:expires_at] = self.expires_at
client.authorization.update_token!(credentials)

But no luck.

I think the issue either has to do with:

  1. OmniAuth not issuing the request_visible_actions to Google correctly
  2. Me not setting the token in the Google::APIClient object properly

I've gotten this far using the following resources, but I'm officially stuck:

  • http://blog.baugues.com/google-calendar-api-oauth2-and-ruby-on-rails
  • https://developers.google.com/+/api/latest/moments/insert

Any ideas would be appreciated!

like image 633
sadiq Avatar asked May 09 '13 02:05

sadiq


1 Answers

Here is my working code from web app using 'omniauth-google-oauth2' along with 'google-api-client'. This sample code uses calendar API, but I guess it will work for you.

require 'google/api_client'

class Calendar
  def initialize(user)
    @user = user
  end

  def events
    result = api_client.execute(:api_method => calendar.events.list,
                            :parameters => {'calendarId' => 'primary'},
                            :authorization => user_credentials)

    result.data
  end

  private

  def api_client
    @client ||= begin
      client = Google::APIClient.new(application_name: 'xxx', application_version: '0.0.1')
      client.authorization.client_id = ENV["GOOGLE_KEY"]
      client.authorization.client_secret = ENV["GOOGLE_SECRET"]
      client.authorization.scope = 'https://www.googleapis.com/auth/calendar'
      client
    end
  end

  def calendar
    @calendar ||= api_client.discovered_api('calendar', 'v3')
  end

  def user_credentials
    auth = api_client.authorization.dup
    # @user.credentials is an OmniAuth::AuthHash  cerated from request.env['omniauth.auth']['credentials']
    auth.update_token!(access_token: @user.credentials.token) 
    auth
  end
end
like image 175
cthulhu Avatar answered Oct 07 '22 00:10

cthulhu