Auto-refresh OAuth 2 tokens & retry request on 401 response

This commit is contained in:
Steven Bazyl 2012-10-30 13:18:12 -07:00
parent 1cf7975319
commit 3d157007f6
4 changed files with 61 additions and 1 deletions

View File

@ -5,6 +5,8 @@
* :api_method in request can no longer be a string * :api_method in request can no longer be a string
* Deprecated ResumableUpload.send_* methods. * Deprecated ResumableUpload.send_* methods.
* Reduce memory utilization when uploading large files * 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. * Simplify internal request processing.
# 0.4.7 # 0.4.7

View File

@ -540,6 +540,15 @@ module Google
request.authorization = options[:authorization] || self.authorization unless options[:authenticated] == false request.authorization = options[:authorization] || self.authorization unless options[:authenticated] == false
result = request.send(connection) 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 return result
end end

View File

@ -14,6 +14,7 @@
require 'jwt' require 'jwt'
require 'signet/oauth_2/client' require 'signet/oauth_2/client'
require 'delegate'
module Google module Google
class APIClient class APIClient
@ -117,7 +118,21 @@ module Google
authorization.grant_type = 'urn:ietf:params:oauth:grant-type:jwt-bearer' authorization.grant_type = 'urn:ietf:params:oauth:grant-type:jwt-bearer'
authorization.extension_parameters = { :assertion => assertion } authorization.extension_parameters = { :assertion => assertion }
authorization.fetch_access_token!(options) 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 end
end end

View File

@ -52,5 +52,39 @@ describe Google::APIClient::JWTAsserter do
auth.access_token.should == "1/abcdef1234567890" auth.access_token.should == "1/abcdef1234567890"
conn.verify conn.verify
end 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 end