<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -78,34 +78,34 @@ module Contacts
     #   (default: false)
     def self.authentication_url(target, options = {})
       params = authentication_url_options.merge(options)
-      key = params.delete(:key)
-      params[:secure] = true if !params[:secure] &amp;&amp; key
+      if key = params.delete(:key)
+        params[:secure] = true
+        set_private_key(key)
+      end
       params[:next] = target
       query = query_string(params)
-      url = &quot;https://#{DOMAIN}#{AuthSubPath}Request?#{query}&quot;
-      sig = generate_sig(url, key) if key
-      key ? &quot;#{url}&amp;#{query_string(:sig =&gt; sig)}&quot; : url
+      &quot;https://#{DOMAIN}#{AuthSubPath}Request?#{query}&quot;
     end
 
-    # Generates the signature for a secure AuthSub request. +key+ may be an IO, String or
+    # Sets the private key 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
+    def self.set_private_key(key)
+      case key
       when OpenSSL::PKey::RSA
-        key
+        @@pkey = key
       when File
-        OpenSSL::PKey::RSA.new(key.read)
+        @@pkey = OpenSSL::PKey::RSA.new(key.read)
       when String
-        OpenSSL::PKey::RSA.new(key)
+        @@pkey = OpenSSL::PKey::RSA.new(key)
       else
         raise &quot;Private Key in wrong format. Require IO, String or OpenSSL::PKey::RSA, you gave me #{key.class}&quot;
       end
-      timestamp = Time.now.to_i
-      nonce     = OpenSSL::BN.rand_range(2**64)
-      data      = &quot;GET #{url} #{timestamp} #{nonce}&quot;
-      digest    = OpenSSL::Digest::SHA1.new(data).hexdigest
-      sig       = [pkey.private_encrypt(digest)].pack(&quot;m&quot;)  #Base64 encode
+    end
+
+    # Unsets the private key. Only used for test teardowns.
+    def self.unset_private_key
+      @@pkey = nil
     end
 
     # Makes an HTTPS request to exchange the given token with a session one. Session
@@ -115,13 +115,14 @@ module Contacts
     # body.
     def self.session_token(token)
       response = http_start do |google|
-        google.get(AuthSubPath + 'SessionToken', authorization_header(token))
+        uri = AuthSubPath + 'SessionToken'
+        google.get(uri, authorization_header(token, false, uri))
       end
 
       pair = response.body.split(/\n/).detect { |p| p.index('Token=') == 0 }
       pair.split('=').last if pair
     end
-    
+
     # Alternative to AuthSub: using email and password.
     def self.client_login(email, password)
       response = http_start do |google|
@@ -142,6 +143,7 @@ module Contacts
     def initialize(token, user_id = 'default', client = false)
       @user    = user_id.to_s
       @token   = token.to_s
+      @client  = client
       @headers = {
         'Accept-Encoding' =&gt; 'gzip',
         'User-Agent' =&gt; Identifier + ' (gzip)'
@@ -154,7 +156,9 @@ module Contacts
         path = FeedsPath + CGI.escape(@user)
         google_params = translate_parameters(params)
         query = self.class.query_string(google_params)
-        google.get(&quot;#{path}/#{@projection}?#{query}&quot;, @headers)
+        uri = &quot;#{path}/#{@projection}?#{query}&quot;
+        headers = @headers.update(self.class.authorization_header(@token, @client, uri))
+        google.get(uri, headers)
       end
     end
 
@@ -284,9 +288,23 @@ module Contacts
         end
       end
       
-      def self.authorization_header(token, client = false)
-        type = client ? 'GoogleLogin auth' : 'AuthSub token'
-        { 'Authorization' =&gt; %(#{type}=&quot;#{token}&quot;) }
+      def self.secure?
+        defined?(@@pkey) &amp;&amp; !@@pkey.nil?
+      end
+
+      def self.authorization_header(token, client = false, uri = nil)
+        if client
+          { 'Authorization' =&gt; %(GoogleLogin auth=&quot;#{token}&quot;) }
+        elsif secure?
+          timestamp = Time.now.to_i
+          nonce = OpenSSL::BN.rand_range(2**64)
+          data = &quot;GET http://#{DOMAIN}#{uri} #{timestamp} #{nonce}&quot;
+          sig = @@pkey.sign(OpenSSL::Digest::SHA1.new, data)
+          sig = [sig].pack(&quot;m&quot;).gsub(/\n/, &quot;&quot;) #Base64 encode
+          { 'Authorization' =&gt; %(AuthSub token=&quot;#{token}&quot; sigalg=&quot;rsa-sha1&quot; data=&quot;#{data}&quot; sig=&quot;#{sig}&quot;) }
+        else
+          { 'Authorization' =&gt; %(AuthSub token=&quot;#{token}&quot;) }
+        end
       end
       
       def self.http_start(ssl = true)</diff>
      <filename>lib/contacts/google.rb</filename>
    </modified>
    <modified>
      <diff>@@ -29,40 +29,29 @@ describe Contacts::Google, '.authentication_url' do
   end
 
   it 'should imply secure=true when the key parameter is set' do
-    rsa = mock('OpenSSL::PKey::RSA', :private_encrypt =&gt; 'signature')
-    digest = mock('OpenSSL::Digest::SHA1', :hexdigest =&gt; '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 =&gt; File.open(File.join(File.dirname(__FILE__), 'myrsakey.pem'))).query.split('&amp;')
+
+    pairs.should include('secure=1')
+  end
 
-    pairs = parse_authentication_url(nil, :key =&gt; 'secret-key').query.split('&amp;')
+  it 'should accept String key parameter' do
+    key = File.open(File.join(File.dirname(__FILE__), 'myrsakey.pem')).read
+    pairs = parse_authentication_url(nil, :key =&gt; key).query.split('&amp;')
 
     pairs.should include('secure=1')
-    pairs.should include('sig=c2lnbmF0dXJl%0A') # ['signature'].pack('m')
-    pairs.should_not include('key=secret-key')
+    pairs.should_not include(&quot;key=#{key}&quot;)
   end
 
   it 'should accept File or IO key parameter' do
-    rsa = mock('OpenSSL::PKey::RSA', :private_encrypt =&gt; 'signature')
-    digest = mock('OpenSSL::Digest::SHA1', :hexdigest =&gt; '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 =&gt; File.open(File.join(File.dirname(__FILE__), 'myrsakey.pem'))).query.split('&amp;')
 
     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 =&gt; 'digest')
-    OpenSSL::Digest::SHA1.expects(:new).returns(digest).at_least_once
-
     pairs = parse_authentication_url(nil, :key =&gt; OpenSSL::PKey::RSA.new(File.open(File.join(File.dirname(__FILE__), 'myrsakey.pem')).read)).query.split('&amp;')
 
     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</diff>
      <filename>spec/gmail/auth_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -13,6 +13,7 @@ describe Contacts::Google do
   
   after :each do
     FakeWeb.clean_registry
+    Contacts::Google.unset_private_key
   end
 
   describe 'fetches contacts feed via HTTP GET' do
@@ -41,6 +42,29 @@ describe Contacts::Google do
       response = @gmail.get({})
       response.body.should == 'full results'
     end
+
+    it 'with signed requests' do
+      # freeze time and nonce for consistent results
+      time = mock('Time.now')
+      time.expects(:to_i).with().returns(1237384927).at_least_once
+      Time.expects(:now).returns(time).at_least_once
+      OpenSSL::BN.expects(:rand_range).with(2**64).returns(11138977546881557915).at_least_once
+
+      Contacts::Google.set_private_key(File.open(File.join(File.dirname(__FILE__), 'myrsakey.pem')))
+      @gmail = Contacts::Google.new('dummytoken')
+
+      FakeWeb::register_uri(:get, 'www.google.com/m8/feeds/contacts/default/thin',
+        :string =&gt; 'thin results',
+        :verify =&gt; lambda { |req|
+          req['Authorization'].should == %(AuthSub token=&quot;dummytoken&quot; sigalg=&quot;rsa-sha1&quot; data=&quot;GET http://www.google.com/m8/feeds/contacts/default/thin? 1237384927 11138977546881557915&quot; sig=&quot;EAO7okp0W+0kAOOFxaAfexym0tWEgKogHpjSs7AX8GZYBWJvyJg6+SFHMAeGUEpAXIyAxCoF+RYzvEt2ONVXEJKaMw4zo/qAFc/RC4dLkFbzwe4r5srFMAGrgbWgqQEeUBeY2koc0vfs2dfOP19rJgACtksm3HLMKpl8M9sUF8c=&quot;)
+          req['Accept-Encoding'].should == 'gzip'
+          req['User-Agent'].should == &quot;Ruby Contacts v#{Contacts::VERSION::STRING} (gzip)&quot;
+        }
+      )
+
+      response = @gmail.get({})
+      response.body.should == 'thin results'
+    end
   end
 
   it 'handles a normal response body' do</diff>
      <filename>spec/gmail/fetching_spec.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>2ceaadbcc8e18cd94759399c357ef0db051ba513</id>
    </parent>
  </parents>
  <author>
    <name>Kamal Fariz Mahyuddin</name>
    <login>kamal</login>
    <email>kamal.fariz@gmail.com</email>
  </author>
  <url>http://github.com/kamal/contacts/commit/b3e9585fe1688f946eeb5fb8fe028bd2f63e374b</url>
  <id>b3e9585fe1688f946eeb5fb8fe028bd2f63e374b</id>
  <committed-date>2009-03-18T07:12:06-07:00</committed-date>
  <authored-date>2009-03-18T06:53:33-07:00</authored-date>
  <message>Fix signing the AuthSub request. It's set in the headers, instead of in the params.</message>
  <tree>5df43083bf88819a084e93ee915856c30c1c96fe</tree>
  <committer>
    <name>Kamal Fariz Mahyuddin</name>
    <login>kamal</login>
    <email>kamal.fariz@gmail.com</email>
  </committer>
</commit>
