Skip to content

Commit 2ceaadb

Browse files
committed
Support signing the Gmail AuthSub using the :key parameter
1 parent c8d99af commit 2ceaadb

File tree

3 files changed

+78
-1
lines changed

3 files changed

+78
-1
lines changed

lib/contacts/google.rb

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,34 @@ def self.client_login_options
7878
# (default: false)
7979
def self.authentication_url(target, options = {})
8080
params = authentication_url_options.merge(options)
81+
key = params.delete(:key)
82+
params[:secure] = true if !params[:secure] && key
8183
params[:next] = target
8284
query = query_string(params)
83-
"https://#{DOMAIN}#{AuthSubPath}Request?#{query}"
85+
url = "https://#{DOMAIN}#{AuthSubPath}Request?#{query}"
86+
sig = generate_sig(url, key) if key
87+
key ? "#{url}&#{query_string(:sig => sig)}" : url
88+
end
89+
90+
# Generates the signature for a secure AuthSub request. +key+ may be an IO, String or
91+
# OpenSSL::PKey::RSA.
92+
# Stolen from http://github.com/stuart/google-authsub/lib/googleauthsub.rb
93+
def self.generate_sig(url, key)
94+
pkey = case key
95+
when OpenSSL::PKey::RSA
96+
key
97+
when File
98+
OpenSSL::PKey::RSA.new(key.read)
99+
when String
100+
OpenSSL::PKey::RSA.new(key)
101+
else
102+
raise "Private Key in wrong format. Require IO, String or OpenSSL::PKey::RSA, you gave me #{key.class}"
103+
end
104+
timestamp = Time.now.to_i
105+
nonce = OpenSSL::BN.rand_range(2**64)
106+
data = "GET #{url} #{timestamp} #{nonce}"
107+
digest = OpenSSL::Digest::SHA1.new(data).hexdigest
108+
sig = [pkey.private_encrypt(digest)].pack("m") #Base64 encode
84109
end
85110

86111
# Makes an HTTPS request to exchange the given token with a session one. Session

spec/gmail/auth_spec.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,43 @@
2828
pairs.should include('session=1')
2929
end
3030

31+
it 'should imply secure=true when the key parameter is set' do
32+
rsa = mock('OpenSSL::PKey::RSA', :private_encrypt => 'signature')
33+
digest = mock('OpenSSL::Digest::SHA1', :hexdigest => 'digest')
34+
OpenSSL::Digest::SHA1.expects(:new).returns(digest).at_least_once
35+
OpenSSL::PKey::RSA.expects(:new).with('secret-key').returns(rsa).at_least_once
36+
37+
pairs = parse_authentication_url(nil, :key => 'secret-key').query.split('&')
38+
39+
pairs.should include('secure=1')
40+
pairs.should include('sig=c2lnbmF0dXJl%0A') # ['signature'].pack('m')
41+
pairs.should_not include('key=secret-key')
42+
end
43+
44+
it 'should accept File or IO key parameter' do
45+
rsa = mock('OpenSSL::PKey::RSA', :private_encrypt => 'signature')
46+
digest = mock('OpenSSL::Digest::SHA1', :hexdigest => 'digest')
47+
OpenSSL::Digest::SHA1.expects(:new).returns(digest).at_least_once
48+
OpenSSL::PKey::RSA.expects(:new).with(File.open(File.join(File.dirname(__FILE__), 'myrsakey.pem')).read).returns(rsa).at_least_once
49+
50+
pairs = parse_authentication_url(nil, :key => File.open(File.join(File.dirname(__FILE__), 'myrsakey.pem'))).query.split('&')
51+
52+
pairs.should include('secure=1')
53+
pairs.should include('sig=c2lnbmF0dXJl%0A') # ['signature'].pack('m')
54+
pairs.should_not include('key=secret-key')
55+
end
56+
57+
it 'should accept OpenSSL::Pkey::RSA key parameter' do
58+
digest = mock('OpenSSL::Digest::SHA1', :hexdigest => 'digest')
59+
OpenSSL::Digest::SHA1.expects(:new).returns(digest).at_least_once
60+
61+
pairs = parse_authentication_url(nil, :key => OpenSSL::PKey::RSA.new(File.open(File.join(File.dirname(__FILE__), 'myrsakey.pem')).read)).query.split('&')
62+
63+
pairs.should include('secure=1')
64+
pairs.should include('sig=nhzzbfqHUOhN6iE%2BkyHQabRvtfc3pbxKQt4hHqlNtBZVliswTFfVIISFPo5Z%0Ads6YVeAKdqAAZuVFUwMDihA83ihIf8spWN%2BrKpeLxAhrUCM69oihD7csdedG%0AbN9TCToIp4q9tJj2o4SsAgxs3dK55Nc1vhCOlVB7mIbxM%2B8YGL4%3D%0A') # based on 'digest' data
65+
pairs.should_not include('key=secret-key')
66+
end
67+
3168
it 'skips parameters that have nil value' do
3269
query = parse_authentication_url(nil, :secure => nil).query
3370
query.should_not include('next')

spec/gmail/myrsakey.pem

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
MIICXAIBAAKBgQDU1ZgHaDgXHIprpl+gV7OTkIpGwXIRA0abAKLGbRoEQx24rKoQ
3+
4zcmn7+b+yx+hu0nNduZC9yOf1dBjB6o4PY8q8tF4OkT93UEcVfSZgU4cWZtcR+y
4+
55ZY6Gnc25PmaH5P6FaFVdrIhGnSiFEa6kwizvUdgkaLNjYVM33XVTJDlQIDAQAB
5+
AoGABVoZqJYGUw50rKBSZ0XNTjikQYM7yxG6BMvPTA4SSWkmpi4xWJteF6qMtu9p
6+
/wSFkibYjtCtiyfIme1cGAMQLgqh8AQ5wngb9yDS1wHW66mO1IZcMhJstMQjIHGq
7+
7ce27mGRLEQmsJeuaK0crZGdn5duQqitWLS+QEx1Y1cJb0ECQQD2wzvsPzpa86ay
8+
RkjgZVg6tA6n51Mja9dm/EA/jVhU9o+PLY+F3h/SxWXlGtE8LQmcFlfQTk643EQ9
9+
ESy7GMM7AkEA3M04OLyxjdX9UCks1Xr7eqi58OAS7pu4Dd867sOIsd3LdJGvZKdq
10+
z59l4IQponDuKUMbm5mTJ8PoJz/1uG0HbwJAa6nPEUqc+WXpS0sjLNTK5AH/iv6A
11+
5al9t+9DUPconRlelYe5YR0DIYEC7iz/MZQs5y9QbRBdhIaGN2aw74jkVQJAQjkq
12+
y5mMh0XPG+O9lqm0Ey3X1u7dvsLliQOS4Vmz/eTPyL6JJF8yIeNk4tnAwv+r7qQ5
13+
+4ksIZkYX+1G/XF18QJBAMOzzA98REQ+NlYNHXzou0HIY+H1jCh4WwFPF7HxaUiO
14+
dtKsVwWxi4CS9/TTND17ZvpcMLuYqscskOUx1mQb13Y=
15+
-----END RSA PRIVATE KEY-----

0 commit comments

Comments
 (0)