I was able to set up the non-secure authentication after reading Tom Hunter's blog post about Google Contacts API authentication. If you use non-secure authentication Google shows a warning to the user about insecure authentication etc. which may not be a good thing. But getting the secure version to work was a pain in the $%&& to say the least. Anyway, after getting some pointers in Google groups, things got rolling.
- Register your app with Google. For secure authentication, you also need to provide an X.509 certificate to Google, which can be uploaded from the same link.
- To generate this certificate read the instructions given by Google and use the openssl command: # Generate the RSA keys and certificate
openssl req -x509 -nodes -days 365 -newkey rsa:1024 -sha1 -subj \
'/C=US/ST=CA/L=Mountain View/CN=www.example.com' -keyout \
myrsakey.pem -out /tmp/myrsacert.pem - The above command will generate 2 files, one in your current directory (myrsaky.pem, which is the private key) and the other in /tmp (myrsacert.pem) which is the certificate to upload. Remember to input your company or website information in the command instead of example.com's. Also remember to upload the 'certificate' and not the 'private_key' file.
- In the code below, callback_url is the link you provide on your Manage Domains form to Google, along with the certificate.
- When someone clicks 'Invite contacts', they get directed to the url of the init action. From there, they are shown the Google login page if they are not logged in to their Google account. After that they are shown the page on which they can choose to authorize you to import their gmail contacts.
- If you place the private key file in config folder of the app, then you can refer to it as shown in the method sign_data.
- Here's the code: (this is my rough draft of the code, so it may not be the most efficient piece of code on earth, but it works for me for now)
class GauthController (greater_than) ApplicationController
require 'uri'
require 'net/http'
require 'net/https'
require 'rexml/document'
def init
callback_url = "http://guitarati.com/gauth/authsub"
next_param = URI.escape(callback_url, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
scope_param = "https%3A%2F%2Fwww.google.com%2Fm8%2Ffeeds%2F"
secure_param = "1"
session_param = "1"
root_url = "https://www.google.com/accounts/AuthSubRequest"
query_string = "?scope=#{scope_param}&session=#{session_param}&secure=#{secure_param}&next=#{next_param}"
redirect_to root_url + query_string
end
def authsub
token = params[:token] # received the single-use authsub token, exchange for authsub session token
url = "https://www.google.com/accounts/AuthSubSessionToken"
headers = set_headers(url, token)
http = Net::HTTP.new('www.google.com', 443)
http.use_ssl = true
path = '/accounts/AuthSubSessionToken'
resp, data = http.get(path, headers)
if resp.code == "200"
token = ''
data.split.each do str
if not (str =~ /Token=/).nil?
token = str.gsub(/Token=/, '')
end
end
redirect_to "http://guitarati.com/gauth/import?token=#{token}"
else
redirect_to "http://guitarati.com/"
end
end
def import
token = params[:token]
#http = Net::HTTP.new('www.google.com', 80)
http = Net::HTTP.new('www.google.com', 443)
http.use_ssl = true
#path = "/m8/feeds/contacts/default/base?max-results=10000"
path = "/m8/feeds/contacts/default/base"
url = "https://www.google.com" + path
headers = set_headers(url, token)
resp, data = http.get(path, headers)
xml = REXML::Document.new(data)
contacts = []
xml.elements.each('//entry') do entry
person = {}
person['name'] = entry.elements['title'].text
gd_email = entry.elements['gd:email']
person['email'] = gd_email.attributes['address'] if gd_email
contacts << text =""> "#{contacts.inspect}"
end
private
require 'base64'
require 'openssl'
include OpenSSL
include PKey
include Digest
def sign_data(data)
private_key = OpenSSL::PKey::RSA.new(File.read("config/myrsaky.pem"))
sig = private_key.sign(OpenSSL::Digest::SHA1.new, data)
return Base64.b64encode(sig).gsub(/\n/, '')
end
def set_headers(url, token)
time = Time.now.to_i.to_s
nonce = OpenSSL::BN.rand_range(2**64)
data = "GET #{url} #{time} #{nonce}"
sig = sign_data(data)
return {'Authorization' => "AuthSub token=\"#{token}\" sigalg=\"rsa-sha1\" data=\"#{data}\" sig=\"#{sig}\""}
end
end

2 comments:
Glad that you put this up, I have spent hours,days on this but cudn't get it going. Glad that you got it running.
However, even with this code (also u had missed "end"), I am not able to get this working. I get "403" as resp.code :( Very unfortunate for me.
Can you pass a helping hand please?
how to export the contact list to the import.html.erb ? thanks
Post a Comment