Skip to content

Commit

Permalink
Support signing the Gmail AuthSub using the :key parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
kamal committed Mar 17, 2009
1 parent c8d99af commit 2ceaadb
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 1 deletion.
27 changes: 26 additions & 1 deletion lib/contacts/google.rb
Expand Up @@ -78,9 +78,34 @@ def self.client_login_options
# (default: false)
def self.authentication_url(target, options = {})
params = authentication_url_options.merge(options)
key = params.delete(:key)
params[:secure] = true if !params[:secure] && key
params[:next] = target
query = query_string(params)
"https://#{DOMAIN}#{AuthSubPath}Request?#{query}"
url = "https://#{DOMAIN}#{AuthSubPath}Request?#{query}"
sig = generate_sig(url, key) if key
key ? "#{url}&#{query_string(:sig => sig)}" : url
end

# Generates the signature for a secure AuthSub request. +key+ may be an IO, String or
# OpenSSL::PKey::RSA.
# Stolen from http://github.com/stuart/google-authsub/lib/googleauthsub.rb
def self.generate_sig(url, key)
pkey = case key
when OpenSSL::PKey::RSA
key
when File
OpenSSL::PKey::RSA.new(key.read)
when String
OpenSSL::PKey::RSA.new(key)
else
raise "Private Key in wrong format. Require IO, String or OpenSSL::PKey::RSA, you gave me #{key.class}"
end
timestamp = Time.now.to_i
nonce = OpenSSL::BN.rand_range(2**64)
data = "GET #{url} #{timestamp} #{nonce}"
digest = OpenSSL::Digest::SHA1.new(data).hexdigest
sig = [pkey.private_encrypt(digest)].pack("m") #Base64 encode
end

# Makes an HTTPS request to exchange the given token with a session one. Session
Expand Down
37 changes: 37 additions & 0 deletions spec/gmail/auth_spec.rb
Expand Up @@ -28,6 +28,43 @@
pairs.should include('session=1')
end

it 'should imply secure=true when the key parameter is set' do
rsa = mock('OpenSSL::PKey::RSA', :private_encrypt => 'signature')
digest = mock('OpenSSL::Digest::SHA1', :hexdigest => 'digest')
OpenSSL::Digest::SHA1.expects(:new).returns(digest).at_least_once
OpenSSL::PKey::RSA.expects(:new).with('secret-key').returns(rsa).at_least_once

pairs = parse_authentication_url(nil, :key => 'secret-key').query.split('&')

pairs.should include('secure=1')
pairs.should include('sig=c2lnbmF0dXJl%0A') # ['signature'].pack('m')
pairs.should_not include('key=secret-key')
end

it 'should accept File or IO key parameter' do
rsa = mock('OpenSSL::PKey::RSA', :private_encrypt => 'signature')
digest = mock('OpenSSL::Digest::SHA1', :hexdigest => 'digest')
OpenSSL::Digest::SHA1.expects(:new).returns(digest).at_least_once
OpenSSL::PKey::RSA.expects(:new).with(File.open(File.join(File.dirname(__FILE__), 'myrsakey.pem')).read).returns(rsa).at_least_once

pairs = parse_authentication_url(nil, :key => File.open(File.join(File.dirname(__FILE__), 'myrsakey.pem'))).query.split('&')

pairs.should include('secure=1')
pairs.should include('sig=c2lnbmF0dXJl%0A') # ['signature'].pack('m')
pairs.should_not include('key=secret-key')
end

it 'should accept OpenSSL::Pkey::RSA key parameter' do
digest = mock('OpenSSL::Digest::SHA1', :hexdigest => 'digest')
OpenSSL::Digest::SHA1.expects(:new).returns(digest).at_least_once

pairs = parse_authentication_url(nil, :key => OpenSSL::PKey::RSA.new(File.open(File.join(File.dirname(__FILE__), 'myrsakey.pem')).read)).query.split('&')

pairs.should include('secure=1')
pairs.should include('sig=nhzzbfqHUOhN6iE%2BkyHQabRvtfc3pbxKQt4hHqlNtBZVliswTFfVIISFPo5Z%0Ads6YVeAKdqAAZuVFUwMDihA83ihIf8spWN%2BrKpeLxAhrUCM69oihD7csdedG%0AbN9TCToIp4q9tJj2o4SsAgxs3dK55Nc1vhCOlVB7mIbxM%2B8YGL4%3D%0A') # based on 'digest' data
pairs.should_not include('key=secret-key')
end

it 'skips parameters that have nil value' do
query = parse_authentication_url(nil, :secure => nil).query
query.should_not include('next')
Expand Down
15 changes: 15 additions & 0 deletions spec/gmail/myrsakey.pem
@@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDU1ZgHaDgXHIprpl+gV7OTkIpGwXIRA0abAKLGbRoEQx24rKoQ
4zcmn7+b+yx+hu0nNduZC9yOf1dBjB6o4PY8q8tF4OkT93UEcVfSZgU4cWZtcR+y
55ZY6Gnc25PmaH5P6FaFVdrIhGnSiFEa6kwizvUdgkaLNjYVM33XVTJDlQIDAQAB
AoGABVoZqJYGUw50rKBSZ0XNTjikQYM7yxG6BMvPTA4SSWkmpi4xWJteF6qMtu9p
/wSFkibYjtCtiyfIme1cGAMQLgqh8AQ5wngb9yDS1wHW66mO1IZcMhJstMQjIHGq
7ce27mGRLEQmsJeuaK0crZGdn5duQqitWLS+QEx1Y1cJb0ECQQD2wzvsPzpa86ay
RkjgZVg6tA6n51Mja9dm/EA/jVhU9o+PLY+F3h/SxWXlGtE8LQmcFlfQTk643EQ9
ESy7GMM7AkEA3M04OLyxjdX9UCks1Xr7eqi58OAS7pu4Dd867sOIsd3LdJGvZKdq
z59l4IQponDuKUMbm5mTJ8PoJz/1uG0HbwJAa6nPEUqc+WXpS0sjLNTK5AH/iv6A
5al9t+9DUPconRlelYe5YR0DIYEC7iz/MZQs5y9QbRBdhIaGN2aw74jkVQJAQjkq
y5mMh0XPG+O9lqm0Ey3X1u7dvsLliQOS4Vmz/eTPyL6JJF8yIeNk4tnAwv+r7qQ5
+4ksIZkYX+1G/XF18QJBAMOzzA98REQ+NlYNHXzou0HIY+H1jCh4WwFPF7HxaUiO
dtKsVwWxi4CS9/TTND17ZvpcMLuYqscskOUx1mQb13Y=
-----END RSA PRIVATE KEY-----

0 comments on commit 2ceaadb

Please sign in to comment.