googleauth 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -46,7 +46,7 @@ module Google
46
46
  #
47
47
  # cf [Application Default Credentials](http://goo.gl/mkAHpZ)
48
48
  class ServiceAccountCredentials < Signet::OAuth2::Client
49
- TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v3/token'
49
+ TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v4/token'.freeze
50
50
  extend CredentialsLoader
51
51
 
52
52
  # Creates a ServiceAccountCredentials.
@@ -73,8 +73,8 @@ module Google
73
73
  # JSON key.
74
74
  def self.read_json_key(json_key_io)
75
75
  json_key = MultiJson.load(json_key_io.read)
76
- fail 'missing client_email' unless json_key.key?('client_email')
77
- fail 'missing private_key' unless json_key.key?('private_key')
76
+ raise 'missing client_email' unless json_key.key?('client_email')
77
+ raise 'missing private_key' unless json_key.key?('private_key')
78
78
  [json_key['private_key'], json_key['client_email']]
79
79
  end
80
80
 
@@ -119,8 +119,8 @@ module Google
119
119
  class ServiceAccountJwtHeaderCredentials
120
120
  JWT_AUD_URI_KEY = :jwt_aud_uri
121
121
  AUTH_METADATA_KEY = Signet::OAuth2::AUTH_METADATA_KEY
122
- TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v3/token'
123
- SIGNING_ALGORITHM = 'RS256'
122
+ TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v4/token'.freeze
123
+ SIGNING_ALGORITHM = 'RS256'.freeze
124
124
  EXPIRY = 60
125
125
  extend CredentialsLoader
126
126
 
@@ -139,8 +139,8 @@ module Google
139
139
  # JSON key.
140
140
  def self.read_json_key(json_key_io)
141
141
  json_key = MultiJson.load(json_key_io.read)
142
- fail 'missing client_email' unless json_key.key?('client_email')
143
- fail 'missing private_key' unless json_key.key?('private_key')
142
+ raise 'missing client_email' unless json_key.key?('client_email')
143
+ raise 'missing private_key' unless json_key.key?('private_key')
144
144
  [json_key['private_key'], json_key['client_email']]
145
145
  end
146
146
 
@@ -64,7 +64,7 @@ module Signet
64
64
  @refresh_listeners << block
65
65
  end
66
66
 
67
- alias_method :orig_fetch_access_token!, :fetch_access_token!
67
+ alias orig_fetch_access_token! fetch_access_token!
68
68
  def fetch_access_token!(options = {})
69
69
  info = orig_fetch_access_token!(options)
70
70
  notify_refresh_listeners
@@ -77,6 +77,27 @@ module Signet
77
77
  block.call(self)
78
78
  end
79
79
  end
80
+
81
+ def retry_with_error(max_retry_count = 5)
82
+ retry_count = 0
83
+
84
+ begin
85
+ yield
86
+ rescue => e
87
+ if e.is_a?(Signet::AuthorizationError) || e.is_a?(Signet::ParseError)
88
+ raise e
89
+ end
90
+
91
+ if retry_count < max_retry_count
92
+ retry_count += 1
93
+ sleep retry_count * 0.3
94
+ retry
95
+ else
96
+ msg = "Unexpected error: #{e.inspect}"
97
+ raise(Signet::AuthorizationError, msg)
98
+ end
99
+ end
100
+ end
80
101
  end
81
102
  end
82
103
  end
@@ -37,7 +37,7 @@ module Google
37
37
  # are stored as JSON using the supplied key, prefixed with
38
38
  # `g-user-token:`
39
39
  class RedisTokenStore < Google::Auth::TokenStore
40
- DEFAULT_KEY_PREFIX = 'g-user-token:'
40
+ DEFAULT_KEY_PREFIX = 'g-user-token:'.freeze
41
41
 
42
42
  # Create a new store with the supplied redis client.
43
43
  #
@@ -51,12 +51,12 @@ module Google
51
51
  def initialize(options = {})
52
52
  redis = options.delete(:redis)
53
53
  prefix = options.delete(:prefix)
54
- case redis
55
- when Redis
56
- @redis = redis
57
- else
58
- @redis = Redis.new(options)
59
- end
54
+ @redis = case redis
55
+ when Redis
56
+ redis
57
+ else
58
+ Redis.new(options)
59
+ end
60
60
  @prefix = prefix || DEFAULT_KEY_PREFIX
61
61
  end
62
62
 
@@ -44,7 +44,7 @@ module Google
44
44
  # @return [String]
45
45
  # The loaded token data.
46
46
  def load(_id)
47
- fail 'Not implemented'
47
+ raise 'Not implemented'
48
48
  end
49
49
 
50
50
  # Put the token data into storage for the given ID.
@@ -54,7 +54,7 @@ module Google
54
54
  # @param [String] token
55
55
  # The token data to store.
56
56
  def store(_id, _token)
57
- fail 'Not implemented'
57
+ raise 'Not implemented'
58
58
  end
59
59
 
60
60
  # Remove the token data from storage for the given ID.
@@ -62,7 +62,7 @@ module Google
62
62
  # @param [String] id
63
63
  # ID of the token data to delete
64
64
  def delete(_id)
65
- fail 'Not implemented'
65
+ raise 'Not implemented'
66
66
  end
67
67
  end
68
68
  end
@@ -53,13 +53,13 @@ module Google
53
53
  # ...
54
54
  class UserAuthorizer
55
55
  MISMATCHED_CLIENT_ID_ERROR =
56
- 'Token client ID of %s does not match configured client id %s'
57
- NIL_CLIENT_ID_ERROR = 'Client id can not be nil.'
58
- NIL_SCOPE_ERROR = 'Scope can not be nil.'
59
- NIL_USER_ID_ERROR = 'User ID can not be nil.'
60
- NIL_TOKEN_STORE_ERROR = 'Can not call method if token store is nil'
56
+ 'Token client ID of %s does not match configured client id %s'.freeze
57
+ NIL_CLIENT_ID_ERROR = 'Client id can not be nil.'.freeze
58
+ NIL_SCOPE_ERROR = 'Scope can not be nil.'.freeze
59
+ NIL_USER_ID_ERROR = 'User ID can not be nil.'.freeze
60
+ NIL_TOKEN_STORE_ERROR = 'Can not call method if token store is nil'.freeze
61
61
  MISSING_ABSOLUTE_URL_ERROR =
62
- 'Absolute base url required for relative callback url "%s"'
62
+ 'Absolute base url required for relative callback url "%s"'.freeze
63
63
 
64
64
  # Initialize the authorizer
65
65
  #
@@ -73,8 +73,8 @@ module Google
73
73
  # URL (either absolute or relative) of the auth callback.
74
74
  # Defaults to '/oauth2callback'
75
75
  def initialize(client_id, scope, token_store, callback_uri = nil)
76
- fail NIL_CLIENT_ID_ERROR if client_id.nil?
77
- fail NIL_SCOPE_ERROR if scope.nil?
76
+ raise NIL_CLIENT_ID_ERROR if client_id.nil?
77
+ raise NIL_SCOPE_ERROR if scope.nil?
78
78
 
79
79
  @client_id = client_id
80
80
  @scope = Array(scope)
@@ -102,7 +102,8 @@ module Google
102
102
  credentials = UserRefreshCredentials.new(
103
103
  client_id: @client_id.id,
104
104
  client_secret: @client_id.secret,
105
- scope: scope)
105
+ scope: scope
106
+ )
106
107
  redirect_uri = redirect_uri_for(options[:base_url])
107
108
  url = credentials.authorization_uri(access_type: 'offline',
108
109
  redirect_uri: redirect_uri,
@@ -123,17 +124,13 @@ module Google
123
124
  # @return [Google::Auth::UserRefreshCredentials]
124
125
  # Stored credentials, nil if none present
125
126
  def get_credentials(user_id, scope = nil)
126
- fail NIL_USER_ID_ERROR if user_id.nil?
127
- fail NIL_TOKEN_STORE_ERROR if @token_store.nil?
128
-
129
- scope ||= @scope
130
- saved_token = @token_store.load(user_id)
127
+ saved_token = stored_token(user_id)
131
128
  return nil if saved_token.nil?
132
129
  data = MultiJson.load(saved_token)
133
130
 
134
131
  if data.fetch('client_id', @client_id.id) != @client_id.id
135
- fail sprintf(MISMATCHED_CLIENT_ID_ERROR,
136
- data['client_id'], @client_id.id)
132
+ raise sprintf(MISMATCHED_CLIENT_ID_ERROR,
133
+ data['client_id'], @client_id.id)
137
134
  end
138
135
 
139
136
  credentials = UserRefreshCredentials.new(
@@ -142,10 +139,11 @@ module Google
142
139
  scope: data['scope'] || @scope,
143
140
  access_token: data['access_token'],
144
141
  refresh_token: data['refresh_token'],
145
- expires_at: data.fetch('expiration_time_millis', 0) / 1000)
142
+ expires_at: data.fetch('expiration_time_millis', 0) / 1000
143
+ )
144
+ scope ||= @scope
146
145
  if credentials.includes_scope?(scope)
147
- monitor_credentials(user_id, credentials)
148
- return credentials
146
+ return monitor_credentials(user_id, credentials)
149
147
  end
150
148
  nil
151
149
  end
@@ -174,7 +172,8 @@ module Google
174
172
  client_id: @client_id.id,
175
173
  client_secret: @client_id.secret,
176
174
  redirect_uri: redirect_uri_for(base_url),
177
- scope: scope)
175
+ scope: scope
176
+ )
178
177
  credentials.code = code
179
178
  credentials.fetch_access_token!({})
180
179
  monitor_credentials(user_id, credentials)
@@ -234,13 +233,26 @@ module Google
234
233
  access_token: credentials.access_token,
235
234
  refresh_token: credentials.refresh_token,
236
235
  scope: credentials.scope,
237
- expiration_time_millis: (credentials.expires_at.to_i) * 1000)
236
+ expiration_time_millis: credentials.expires_at.to_i * 1000
237
+ )
238
238
  @token_store.store(user_id, json)
239
239
  credentials
240
240
  end
241
241
 
242
242
  private
243
243
 
244
+ # @private Fetch stored token with given user_id
245
+ #
246
+ # @param [String] user_id
247
+ # Unique ID of the user for loading/storing credentials.
248
+ # @return [String] The saved token from @token_store
249
+ def stored_token(user_id)
250
+ raise NIL_USER_ID_ERROR if user_id.nil?
251
+ raise NIL_TOKEN_STORE_ERROR if @token_store.nil?
252
+
253
+ @token_store.load(user_id)
254
+ end
255
+
244
256
  # Begin watching a credential for refreshes so the access token can be
245
257
  # saved.
246
258
  #
@@ -263,9 +275,9 @@ module Google
263
275
  # Redirect URI
264
276
  def redirect_uri_for(base_url)
265
277
  return @callback_uri unless URI(@callback_uri).scheme.nil?
266
- fail sprintf(
267
- MISSING_ABSOLUTE_URL_ERROR,
268
- @callback_uri) if base_url.nil? || URI(base_url).scheme.nil?
278
+ if base_url.nil? || URI(base_url).scheme.nil?
279
+ raise sprintf(ISSING_ABSOLUTE_URL_ERROR, @callback_uri)
280
+ end
269
281
  URI.join(base_url, @callback_uri).to_s
270
282
  end
271
283
  end
@@ -46,9 +46,9 @@ module Google
46
46
  #
47
47
  # cf [Application Default Credentials](http://goo.gl/mkAHpZ)
48
48
  class UserRefreshCredentials < Signet::OAuth2::Client
49
- TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v3/token'
50
- AUTHORIZATION_URI = 'https://accounts.google.com/o/oauth2/auth'
51
- REVOKE_TOKEN_URI = 'https://accounts.google.com/o/oauth2/revoke'
49
+ TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v4/token'.freeze
50
+ AUTHORIZATION_URI = 'https://accounts.google.com/o/oauth2/auth'.freeze
51
+ REVOKE_TOKEN_URI = 'https://accounts.google.com/o/oauth2/revoke'.freeze
52
52
  extend CredentialsLoader
53
53
 
54
54
  # Create a UserRefreshCredentials.
@@ -77,7 +77,7 @@ module Google
77
77
  json_key = MultiJson.load(json_key_io.read)
78
78
  wanted = %w(client_id client_secret refresh_token)
79
79
  wanted.each do |key|
80
- fail "the json is missing the #{key} field" unless json_key.key?(key)
80
+ raise "the json is missing the #{key} field" unless json_key.key?(key)
81
81
  end
82
82
  json_key
83
83
  end
@@ -92,15 +92,18 @@ module Google
92
92
  # Revokes the credential
93
93
  def revoke!(options = {})
94
94
  c = options[:connection] || Faraday.default_connection
95
- resp = c.get(REVOKE_TOKEN_URI, token: refresh_token || access_token)
96
- case resp.status
97
- when 200
98
- self.access_token = nil
99
- self.refresh_token = nil
100
- self.expires_at = 0
101
- else
102
- fail(Signet::AuthorizationError,
103
- "Unexpected error code #{resp.status}")
95
+
96
+ retry_with_error do
97
+ resp = c.get(REVOKE_TOKEN_URI, token: refresh_token || access_token)
98
+ case resp.status
99
+ when 200
100
+ self.access_token = nil
101
+ self.refresh_token = nil
102
+ self.expires_at = 0
103
+ else
104
+ raise(Signet::AuthorizationError,
105
+ "Unexpected error code #{resp.status}")
106
+ end
104
107
  end
105
108
  end
106
109
 
@@ -31,6 +31,6 @@ module Google
31
31
  # Module Auth provides classes that provide Google-specific authorization
32
32
  # used to access Google APIs.
33
33
  module Auth
34
- VERSION = '0.5.1'
34
+ VERSION = '0.5.2'.freeze
35
35
  end
36
36
  end
@@ -66,20 +66,21 @@ module Google
66
66
  # @see {Google::Auth::ControllerHelpers}
67
67
  # @note Requires sessions are enabled
68
68
  class WebUserAuthorizer < Google::Auth::UserAuthorizer
69
- STATE_PARAM = 'state'
70
- AUTH_CODE_KEY = 'code'
71
- ERROR_CODE_KEY = 'error'
72
- SESSION_ID_KEY = 'session_id'
73
- CALLBACK_STATE_KEY = 'g-auth-callback'
74
- CURRENT_URI_KEY = 'current_uri'
75
- XSRF_KEY = 'g-xsrf-token'
76
- SCOPE_KEY = 'scope'
69
+ STATE_PARAM = 'state'.freeze
70
+ AUTH_CODE_KEY = 'code'.freeze
71
+ ERROR_CODE_KEY = 'error'.freeze
72
+ SESSION_ID_KEY = 'session_id'.freeze
73
+ CALLBACK_STATE_KEY = 'g-auth-callback'.freeze
74
+ CURRENT_URI_KEY = 'current_uri'.freeze
75
+ XSRF_KEY = 'g-xsrf-token'.freeze
76
+ SCOPE_KEY = 'scope'.freeze
77
77
 
78
- NIL_REQUEST_ERROR = 'Request is required.'
79
- NIL_SESSION_ERROR = 'Sessions must be enabled'
80
- MISSING_AUTH_CODE_ERROR = 'Missing authorization code in request'
81
- AUTHORIZATION_ERROR = 'Authorization error: %s'
82
- INVALID_STATE_TOKEN_ERROR = 'State token does not match expected value'
78
+ NIL_REQUEST_ERROR = 'Request is required.'.freeze
79
+ NIL_SESSION_ERROR = 'Sessions must be enabled'.freeze
80
+ MISSING_AUTH_CODE_ERROR = 'Missing authorization code in request'.freeze
81
+ AUTHORIZATION_ERROR = 'Authorization error: %s'.freeze
82
+ INVALID_STATE_TOKEN_ERROR =
83
+ 'State token does not match expected value'.freeze
83
84
 
84
85
  class << self
85
86
  attr_accessor :default
@@ -128,13 +129,15 @@ module Google
128
129
  # credentials & next URL to redirect to
129
130
  def handle_auth_callback(user_id, request)
130
131
  callback_state, redirect_uri = WebUserAuthorizer.extract_callback_state(
131
- request)
132
+ request
133
+ )
132
134
  WebUserAuthorizer.validate_callback_state(callback_state, request)
133
135
  credentials = get_and_store_credentials_from_code(
134
136
  user_id: user_id,
135
137
  code: callback_state[AUTH_CODE_KEY],
136
138
  scope: callback_state[SCOPE_KEY],
137
- base_url: request.url)
139
+ base_url: request.url
140
+ )
138
141
  [credentials, redirect_uri]
139
142
  end
140
143
 
@@ -156,14 +159,15 @@ module Google
156
159
  def get_authorization_url(options = {})
157
160
  options = options.dup
158
161
  request = options[:request]
159
- fail NIL_REQUEST_ERROR if request.nil?
160
- fail NIL_SESSION_ERROR if request.session.nil?
162
+ raise NIL_REQUEST_ERROR if request.nil?
163
+ raise NIL_SESSION_ERROR if request.session.nil?
161
164
 
162
165
  redirect_to = options[:redirect_to] || request.url
163
166
  request.session[XSRF_KEY] = SecureRandom.base64
164
167
  options[:state] = MultiJson.dump(
165
168
  SESSION_ID_KEY => request.session[XSRF_KEY],
166
- CURRENT_URI_KEY => redirect_to)
169
+ CURRENT_URI_KEY => redirect_to
170
+ )
167
171
  options[:base_url] = request.url
168
172
  super(options)
169
173
  end
@@ -193,7 +197,8 @@ module Google
193
197
  user_id: user_id,
194
198
  code: callback_state[AUTH_CODE_KEY],
195
199
  scope: callback_state[SCOPE_KEY],
196
- base_url: request.url)
200
+ base_url: request.url
201
+ )
197
202
  else
198
203
  super(user_id, scope)
199
204
  end
@@ -204,7 +209,7 @@ module Google
204
209
  redirect_uri = state[CURRENT_URI_KEY]
205
210
  callback_state = {
206
211
  AUTH_CODE_KEY => request[AUTH_CODE_KEY],
207
- ERROR_CODE_KEY => request[ERROR_CODE_KEY],
212
+ ERROR_CODE_KEY => request[ERROR_CODE_KEY],
208
213
  SESSION_ID_KEY => state[SESSION_ID_KEY],
209
214
  SCOPE_KEY => request[SCOPE_KEY]
210
215
  }
@@ -223,12 +228,12 @@ module Google
223
228
  # Current request
224
229
  def self.validate_callback_state(state, request)
225
230
  if state[AUTH_CODE_KEY].nil?
226
- fail Signet::AuthorizationError, MISSING_AUTH_CODE_ERROR
231
+ raise Signet::AuthorizationError, MISSING_AUTH_CODE_ERROR
227
232
  elsif state[ERROR_CODE_KEY]
228
- fail Signet::AuthorizationError,
229
- sprintf(AUTHORIZATION_ERROR, state[ERROR_CODE_KEY])
233
+ raise Signet::AuthorizationError,
234
+ sprintf(AUTHORIZATION_ERROR, state[ERROR_CODE_KEY])
230
235
  elsif request.session[XSRF_KEY] != state[SESSION_ID_KEY]
231
- fail Signet::AuthorizationError, INVALID_STATE_TOKEN_ERROR
236
+ raise Signet::AuthorizationError, INVALID_STATE_TOKEN_ERROR
232
237
  end
233
238
  end
234
239
 
@@ -254,7 +259,7 @@ module Google
254
259
  #
255
260
  # @see {Google::Auth::WebUserAuthorizer}
256
261
  class CallbackApp
257
- LOCATION_HEADER = 'Location'
262
+ LOCATION_HEADER = 'Location'.freeze
258
263
  REDIR_STATUS = 302
259
264
  ERROR_STATUS = 500
260
265
 
@@ -62,7 +62,8 @@ shared_examples 'apply/apply! are OK' do
62
62
  @client.on_refresh(&b)
63
63
  @client.fetch_access_token!
64
64
  end.to yield_with_args(have_attributes(
65
- access_token: '1/abcdef1234567890'))
65
+ access_token: '1/abcdef1234567890'
66
+ ))
66
67
  expect(stub).to have_been_requested
67
68
  end
68
69
  end
@@ -131,10 +132,10 @@ shared_examples 'apply/apply! are OK' do
131
132
  end
132
133
 
133
134
  it 'should fetch a new token if the current one is expired' do
134
- token_1 = '1/abcdef1234567890'
135
- token_2 = '2/abcdef1234567891'
135
+ token1 = '1/abcdef1234567890'
136
+ token2 = '2/abcdef1234567891'
136
137
 
137
- [token_1, token_2].each do |t|
138
+ [token1, token2].each do |t|
138
139
  make_auth_stubs access_token: t
139
140
  md = { foo: 'bar' }
140
141
  got = @client.apply(md)