After a few hours of trial and error and finally getting authentication against Google’s API working in Ruby I think it’s time for a blog post 😉
I had a simple (at least I thought it was simple) requirement: Reading the people in a user’s Google+ circles with Ruby. Because I use OmniAuth in my application, the user is already authenticated and I even have his access token stored in the database. However, it took me a few hours to find out how to use this token to access Google’s API.
Reading people from a Google+ user’s circles
Reading the people from a user’s circle is quite easy. Simply use plus.people.list
. You can try it with the Google APIs Explorer. However, you need to make sure, your app requests the right permissions. This is the corresponding line from my Rails initializer omniauth.rb
(I needed to add the scope plus.login
and change the access type to offline
):
provider :google_oauth2, "CLIENTID", "SECRET", scope: 'profile,email,plus.login', image_aspect_ratio: 'square', image_size: 48, access_type: 'offline', name: 'google'
The Ruby code using google-api-ruby-client looks like this:
plus = Google::Apis::PlusV1::PlusService.new
friends = plus.list_people("me", "visible").items
The items you get from Google+ look like this:
#<Google::Apis::PlusV1::Person:0x4307fd0
@display_name="The Name",
@etag="\some tag\"",
@id="1234",
@image=#<Google::Apis::PlusV1::Person::Image:0x43057f8
@url="https://lh6.googleusercontent.com/.../photo.jpg?sz=50">,
@kind="plus#person",
@object_type="person",
@url="https://plus.google.com/1234">
Using an existing access token for authentication
Google’s documentation states that you need to use Signet to authenticate against the Google API. So I thought I’d give it a try and started coding. But as it turns out, Signet is a complex beast and I didn’t want to re-implement the whole OAuth authentication process, because OmniAuth already does that for me. I just wanted to use my existing token for authentication!
Long story short: I found the solution in file http_command.rb in method apply_request_options()
:
if options.authorization.respond_to?(:apply!)
options.authorization.apply!(req.header)
elsif options.authorization.is_a?(String)
req.header[:authorization] = sprintf('Bearer %s', options.authorization)
end
You can simply set the attribute authorization
of your PlusService
to a string (instead of a Signet
object) and it will be set as the Bearer
in the HTTP request to Google’s API. So, I simply had to add this line to my calling code and I was done:
plus.authorization = access_token
The final code
I still can’t believe that the final solution is so simple! 🙂
Gemfile
:
gem 'google-api-client', '~> 0.9'
omniauth.rb
:
provider :google_oauth2, "CLIENTID", "SECRET", scope: 'profile,email,plus.login', image_aspect_ratio: 'square', image_size: 48, access_type: 'offline', name: 'google'
friend_reader.rb
:
require 'google/apis/plus_v1'
plus = Google::Apis::PlusV1::PlusService.new
plus.authorization = access_token
friends = plus.list_people("me", "visible").items
this just saved me a ton of time!! thanks!