Skip to content

Commit

Permalink
Auto-refresh OAuth 2 tokens & retry request on 401 response
Browse files Browse the repository at this point in the history
  • Loading branch information
sqrrrl committed Oct 30, 2012
1 parent 1cf7975 commit 3d15700
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* :api_method in request can no longer be a string
* Deprecated ResumableUpload.send_* methods.
* Reduce memory utilization when uploading large files
* Automatic refresh of OAuth 2 credentials & retry of request when 401 errors
are returned
* Simplify internal request processing.

# 0.4.7
Expand Down
9 changes: 9 additions & 0 deletions lib/google/api_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,15 @@ def execute(*params)
request.authorization = options[:authorization] || self.authorization unless options[:authenticated] == false

result = request.send(connection)
if result.status == 401 && authorization.respond_to?(:refresh_token)
begin
authorization.fetch_access_token!
result = request.send(connection)
rescue Signet::AuthorizationError
# Ignore since we want the original error
end
end

return result
end

Expand Down
17 changes: 16 additions & 1 deletion lib/google/api_client/auth/jwt_asserter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

require 'jwt'
require 'signet/oauth_2/client'
require 'delegate'

module Google
class APIClient
Expand Down Expand Up @@ -117,7 +118,21 @@ def authorize(person = nil, options={})
authorization.grant_type = 'urn:ietf:params:oauth:grant-type:jwt-bearer'
authorization.extension_parameters = { :assertion => assertion }
authorization.fetch_access_token!(options)
return authorization
return JWTAuthorization.new(authorization, self, person)
end
end

class JWTAuthorization < DelegateClass(Signet::OAuth2::Client)
def initialize(authorization, asserter, person = nil)
@asserter = asserter
@person = person
super(authorization)
end

def fetch_access_token!(options={})
new_authorization = @asserter.authorize(@person, options)
__setobj__(new_authorization)
self
end
end
end
Expand Down
34 changes: 34 additions & 0 deletions spec/google/api_client/service_account_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,39 @@
auth.access_token.should == "1/abcdef1234567890"
conn.verify
end

it 'should be refreshable' do
conn = stub_connection do |stub|
stub.post('/o/oauth2/token') do |env|
params = Addressable::URI.form_unencode(env[:body])
JWT.decode(params.assoc("assertion").last, @key.public_key)
params.assoc("grant_type").should == ['grant_type','urn:ietf:params:oauth:grant-type:jwt-bearer']
[200, {}, '{
"access_token" : "1/abcdef1234567890",
"token_type" : "Bearer",
"expires_in" : 3600
}']
end
stub.post('/o/oauth2/token') do |env|
params = Addressable::URI.form_unencode(env[:body])
JWT.decode(params.assoc("assertion").last, @key.public_key)
params.assoc("grant_type").should == ['grant_type','urn:ietf:params:oauth:grant-type:jwt-bearer']
[200, {}, '{
"access_token" : "1/0987654321fedcba",
"token_type" : "Bearer",
"expires_in" : 3600
}']
end
end
asserter = Google::APIClient::JWTAsserter.new('client1', 'scope1 scope2', @key)
auth = asserter.authorize(nil, { :connection => conn })
auth.should_not == nil?
auth.access_token.should == "1/abcdef1234567890"

auth.fetch_access_token!(:connection => conn)
auth.access_token.should == "1/0987654321fedcba"

conn.verify
end
end

0 comments on commit 3d15700

Please sign in to comment.