googleauth 1.8.0 → 1.15.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +117 -0
- data/Credentials.md +106 -0
- data/Errors.md +152 -0
- data/README.md +49 -1
- data/lib/googleauth/api_key.rb +164 -0
- data/lib/googleauth/application_default.rb +6 -8
- data/lib/googleauth/base_client.rb +21 -4
- data/lib/googleauth/bearer_token.rb +162 -0
- data/lib/googleauth/client_id.rb +9 -6
- data/lib/googleauth/compute_engine.rb +231 -49
- data/lib/googleauth/credentials.rb +187 -58
- data/lib/googleauth/credentials_loader.rb +11 -20
- data/lib/googleauth/default_credentials.rb +29 -8
- data/lib/googleauth/errors.rb +117 -0
- data/lib/googleauth/external_account/aws_credentials.rb +85 -18
- data/lib/googleauth/external_account/base_credentials.rb +67 -6
- data/lib/googleauth/external_account/external_account_utils.rb +15 -4
- data/lib/googleauth/external_account/identity_pool_credentials.rb +40 -15
- data/lib/googleauth/external_account/pluggable_credentials.rb +34 -19
- data/lib/googleauth/external_account.rb +32 -7
- data/lib/googleauth/helpers/connection.rb +7 -1
- data/lib/googleauth/iam.rb +19 -3
- data/lib/googleauth/id_tokens/errors.rb +13 -7
- data/lib/googleauth/id_tokens/key_sources.rb +13 -7
- data/lib/googleauth/id_tokens/verifier.rb +2 -3
- data/lib/googleauth/id_tokens.rb +4 -6
- data/lib/googleauth/impersonated_service_account.rb +329 -0
- data/lib/googleauth/json_key_reader.rb +13 -3
- data/lib/googleauth/oauth2/sts_client.rb +9 -4
- data/lib/googleauth/scope_util.rb +1 -1
- data/lib/googleauth/service_account.rb +84 -104
- data/lib/googleauth/service_account_jwt_header.rb +187 -0
- data/lib/googleauth/signet.rb +169 -4
- data/lib/googleauth/token_store.rb +3 -3
- data/lib/googleauth/user_authorizer.rb +89 -11
- data/lib/googleauth/user_refresh.rb +72 -9
- data/lib/googleauth/version.rb +1 -1
- data/lib/googleauth/web_user_authorizer.rb +65 -17
- data/lib/googleauth.rb +8 -0
- metadata +45 -13
data/lib/googleauth/signet.rb
CHANGED
@@ -12,8 +12,11 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
+
require "base64"
|
16
|
+
require "json"
|
15
17
|
require "signet/oauth_2/client"
|
16
18
|
require "googleauth/base_client"
|
19
|
+
require "googleauth/errors"
|
17
20
|
|
18
21
|
module Signet
|
19
22
|
# OAuth2 supports OAuth2 authentication.
|
@@ -25,6 +28,33 @@ module Signet
|
|
25
28
|
class Client
|
26
29
|
include Google::Auth::BaseClient
|
27
30
|
|
31
|
+
alias update_token_signet_base update_token!
|
32
|
+
|
33
|
+
def update_token! options = {}
|
34
|
+
options = deep_hash_normalize options
|
35
|
+
id_token_expires_at = expires_at_from_id_token options[:id_token]
|
36
|
+
options[:expires_at] = id_token_expires_at if id_token_expires_at
|
37
|
+
update_token_signet_base options
|
38
|
+
self.universe_domain = options[:universe_domain] if options.key? :universe_domain
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
alias update_signet_base update!
|
43
|
+
def update! options = {}
|
44
|
+
# Normalize all keys to symbols to allow indifferent access.
|
45
|
+
options = deep_hash_normalize options
|
46
|
+
|
47
|
+
# This `update!` method "overide" adds the `@logger`` update and
|
48
|
+
# the `universe_domain` update.
|
49
|
+
#
|
50
|
+
# The `universe_domain` is also updated in `update_token!` but is
|
51
|
+
# included here for completeness
|
52
|
+
self.universe_domain = options[:universe_domain] if options.key? :universe_domain
|
53
|
+
@logger = options[:logger] if options.key? :logger
|
54
|
+
|
55
|
+
update_signet_base options
|
56
|
+
end
|
57
|
+
|
28
58
|
def configure_connection options
|
29
59
|
@connection_info =
|
30
60
|
options[:connection_builder] || options[:default_connection]
|
@@ -36,6 +66,9 @@ module Signet
|
|
36
66
|
target_audience ? :id_token : :access_token
|
37
67
|
end
|
38
68
|
|
69
|
+
# Set the universe domain
|
70
|
+
attr_accessor :universe_domain
|
71
|
+
|
39
72
|
alias orig_fetch_access_token! fetch_access_token!
|
40
73
|
def fetch_access_token! options = {}
|
41
74
|
unless options[:connection]
|
@@ -49,6 +82,24 @@ module Signet
|
|
49
82
|
info
|
50
83
|
end
|
51
84
|
|
85
|
+
alias googleauth_orig_generate_access_token_request generate_access_token_request
|
86
|
+
def generate_access_token_request options = {}
|
87
|
+
parameters = googleauth_orig_generate_access_token_request options
|
88
|
+
logger&.info do
|
89
|
+
Google::Logging::Message.from(
|
90
|
+
message: "Requesting access token from #{parameters['grant_type']}",
|
91
|
+
"credentialsId" => object_id
|
92
|
+
)
|
93
|
+
end
|
94
|
+
logger&.debug do
|
95
|
+
Google::Logging::Message.from(
|
96
|
+
message: "Token fetch params: #{parameters}",
|
97
|
+
"credentialsId" => object_id
|
98
|
+
)
|
99
|
+
end
|
100
|
+
parameters
|
101
|
+
end
|
102
|
+
|
52
103
|
def build_default_connection
|
53
104
|
if !defined?(@connection_info)
|
54
105
|
nil
|
@@ -59,24 +110,138 @@ module Signet
|
|
59
110
|
end
|
60
111
|
end
|
61
112
|
|
113
|
+
# rubocop:disable Metrics/MethodLength
|
114
|
+
|
115
|
+
# Retries the provided block with exponential backoff, handling and wrapping errors.
|
116
|
+
#
|
117
|
+
# @param [Integer] max_retry_count The maximum number of retries before giving up
|
118
|
+
# @yield The block to execute and potentially retry
|
119
|
+
# @return [Object] The result of the block if successful
|
120
|
+
# @raise [Google::Auth::AuthorizationError] If a Signet::AuthorizationError occurs or if retries are exhausted
|
121
|
+
# @raise [Google::Auth::ParseError] If a Signet::ParseError occurs during token parsing
|
62
122
|
def retry_with_error max_retry_count = 5
|
63
123
|
retry_count = 0
|
64
124
|
|
65
125
|
begin
|
66
|
-
yield
|
126
|
+
yield.tap { |resp| log_response resp }
|
127
|
+
rescue Signet::AuthorizationError, Signet::ParseError => e
|
128
|
+
log_auth_error e
|
129
|
+
error_class = e.is_a?(Signet::ParseError) ? Google::Auth::ParseError : Google::Auth::AuthorizationError
|
130
|
+
raise error_class.with_details(
|
131
|
+
e.message,
|
132
|
+
credential_type_name: self.class.name,
|
133
|
+
principal: respond_to?(:principal) ? principal : :signet_client
|
134
|
+
)
|
67
135
|
rescue StandardError => e
|
68
|
-
raise e if e.is_a?(Signet::AuthorizationError) || e.is_a?(Signet::ParseError)
|
69
|
-
|
70
136
|
if retry_count < max_retry_count
|
137
|
+
log_transient_error e
|
71
138
|
retry_count += 1
|
72
139
|
sleep retry_count * 0.3
|
73
140
|
retry
|
74
141
|
else
|
142
|
+
log_retries_exhausted e
|
75
143
|
msg = "Unexpected error: #{e.inspect}"
|
76
|
-
raise
|
144
|
+
raise Google::Auth::AuthorizationError.with_details(
|
145
|
+
msg,
|
146
|
+
credential_type_name: self.class.name,
|
147
|
+
principal: respond_to?(:principal) ? principal : :signet_client
|
148
|
+
)
|
77
149
|
end
|
78
150
|
end
|
79
151
|
end
|
152
|
+
# rubocop:enable Metrics/MethodLength
|
153
|
+
|
154
|
+
# Creates a duplicate of these credentials
|
155
|
+
# without the Signet::OAuth2::Client-specific
|
156
|
+
# transient state (e.g. cached tokens)
|
157
|
+
#
|
158
|
+
# @param options [Hash] Overrides for the credentials parameters.
|
159
|
+
# @see Signet::OAuth2::Client#update!
|
160
|
+
def duplicate options = {}
|
161
|
+
options = deep_hash_normalize options
|
162
|
+
|
163
|
+
opts = {
|
164
|
+
authorization_uri: @authorization_uri,
|
165
|
+
token_credential_uri: @token_credential_uri,
|
166
|
+
client_id: @client_id,
|
167
|
+
client_secret: @client_secret,
|
168
|
+
scope: @scope,
|
169
|
+
target_audience: @target_audience,
|
170
|
+
redirect_uri: @redirect_uri,
|
171
|
+
username: @username,
|
172
|
+
password: @password,
|
173
|
+
issuer: @issuer,
|
174
|
+
person: @person,
|
175
|
+
sub: @sub,
|
176
|
+
audience: @audience,
|
177
|
+
signing_key: @signing_key,
|
178
|
+
extension_parameters: @extension_parameters,
|
179
|
+
additional_parameters: @additional_parameters,
|
180
|
+
access_type: @access_type,
|
181
|
+
universe_domain: @universe_domain,
|
182
|
+
logger: @logger
|
183
|
+
}.merge(options)
|
184
|
+
|
185
|
+
new_client = self.class.new opts
|
186
|
+
|
187
|
+
new_client.configure_connection options
|
188
|
+
end
|
189
|
+
|
190
|
+
private
|
191
|
+
|
192
|
+
def expires_at_from_id_token id_token
|
193
|
+
match = /^[\w=-]+\.([\w=-]+)\.[\w=-]+$/.match id_token.to_s
|
194
|
+
return unless match
|
195
|
+
json = JSON.parse Base64.urlsafe_decode64 match[1]
|
196
|
+
return unless json.key? "exp"
|
197
|
+
Time.at json["exp"].to_i
|
198
|
+
rescue StandardError
|
199
|
+
# Shouldn't happen unless we get a garbled ID token
|
200
|
+
nil
|
201
|
+
end
|
202
|
+
|
203
|
+
def log_response token_response
|
204
|
+
response_hash = JSON.parse token_response rescue {}
|
205
|
+
if response_hash["access_token"]
|
206
|
+
digest = Digest::SHA256.hexdigest response_hash["access_token"]
|
207
|
+
response_hash["access_token"] = "(sha256:#{digest})"
|
208
|
+
end
|
209
|
+
if response_hash["id_token"]
|
210
|
+
digest = Digest::SHA256.hexdigest response_hash["id_token"]
|
211
|
+
response_hash["id_token"] = "(sha256:#{digest})"
|
212
|
+
end
|
213
|
+
Google::Logging::Message.from(
|
214
|
+
message: "Received auth token response: #{response_hash}",
|
215
|
+
"credentialsId" => object_id
|
216
|
+
)
|
217
|
+
end
|
218
|
+
|
219
|
+
def log_auth_error err
|
220
|
+
logger&.info do
|
221
|
+
Google::Logging::Message.from(
|
222
|
+
message: "Auth error when fetching auth token: #{err}",
|
223
|
+
"credentialsId" => object_id
|
224
|
+
)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def log_transient_error err
|
229
|
+
logger&.info do
|
230
|
+
Google::Logging::Message.from(
|
231
|
+
message: "Transient error when fetching auth token: #{err}",
|
232
|
+
"credentialsId" => object_id
|
233
|
+
)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def log_retries_exhausted err
|
238
|
+
logger&.info do
|
239
|
+
Google::Logging::Message.from(
|
240
|
+
message: "Exhausted retries when fetching auth token: #{err}",
|
241
|
+
"credentialsId" => object_id
|
242
|
+
)
|
243
|
+
end
|
244
|
+
end
|
80
245
|
end
|
81
246
|
end
|
82
247
|
end
|
@@ -29,7 +29,7 @@ module Google
|
|
29
29
|
# @return [String]
|
30
30
|
# The loaded token data.
|
31
31
|
def load _id
|
32
|
-
raise "
|
32
|
+
raise NoMethodError, "load not implemented"
|
33
33
|
end
|
34
34
|
|
35
35
|
# Put the token data into storage for the given ID.
|
@@ -39,7 +39,7 @@ module Google
|
|
39
39
|
# @param [String] token
|
40
40
|
# The token data to store.
|
41
41
|
def store _id, _token
|
42
|
-
raise "
|
42
|
+
raise NoMethodError, "store not implemented"
|
43
43
|
end
|
44
44
|
|
45
45
|
# Remove the token data from storage for the given ID.
|
@@ -47,7 +47,7 @@ module Google
|
|
47
47
|
# @param [String] id
|
48
48
|
# ID of the token data to delete
|
49
49
|
def delete _id
|
50
|
-
raise "
|
50
|
+
raise NoMethodError, "delete not implemented"
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
@@ -16,6 +16,7 @@ require "uri"
|
|
16
16
|
require "multi_json"
|
17
17
|
require "googleauth/signet"
|
18
18
|
require "googleauth/user_refresh"
|
19
|
+
require "securerandom"
|
19
20
|
|
20
21
|
module Google
|
21
22
|
module Auth
|
@@ -54,17 +55,28 @@ module Google
|
|
54
55
|
# Authorization scope to request
|
55
56
|
# @param [Google::Auth::Stores::TokenStore] token_store
|
56
57
|
# Backing storage for persisting user credentials
|
57
|
-
# @param [String]
|
58
|
+
# @param [String] legacy_callback_uri
|
58
59
|
# URL (either absolute or relative) of the auth callback.
|
59
|
-
# Defaults to '/oauth2callback'
|
60
|
-
|
61
|
-
|
62
|
-
|
60
|
+
# Defaults to '/oauth2callback'.
|
61
|
+
# @deprecated This field is deprecated. Instead, use the keyword
|
62
|
+
# argument callback_uri.
|
63
|
+
# @param [String] code_verifier
|
64
|
+
# Random string of 43-128 chars used to verify the key exchange using
|
65
|
+
# PKCE.
|
66
|
+
# @raise [Google::Auth::InitializationError]
|
67
|
+
# If client_id is nil or scope is nil
|
68
|
+
def initialize client_id, scope, token_store,
|
69
|
+
legacy_callback_uri = nil,
|
70
|
+
callback_uri: nil,
|
71
|
+
code_verifier: nil
|
72
|
+
raise InitializationError, NIL_CLIENT_ID_ERROR if client_id.nil?
|
73
|
+
raise InitializationError, NIL_SCOPE_ERROR if scope.nil?
|
63
74
|
|
64
75
|
@client_id = client_id
|
65
76
|
@scope = Array(scope)
|
66
77
|
@token_store = token_store
|
67
|
-
@callback_uri = callback_uri || "/oauth2callback"
|
78
|
+
@callback_uri = legacy_callback_uri || callback_uri || "/oauth2callback"
|
79
|
+
@code_verifier = code_verifier
|
68
80
|
end
|
69
81
|
|
70
82
|
# Build the URL for requesting authorization.
|
@@ -86,6 +98,18 @@ module Google
|
|
86
98
|
# Authorization url
|
87
99
|
def get_authorization_url options = {}
|
88
100
|
scope = options[:scope] || @scope
|
101
|
+
|
102
|
+
options[:additional_parameters] ||= {}
|
103
|
+
|
104
|
+
if @code_verifier
|
105
|
+
options[:additional_parameters].merge!(
|
106
|
+
{
|
107
|
+
code_challenge: generate_code_challenge(@code_verifier),
|
108
|
+
code_challenge_method: code_challenge_method
|
109
|
+
}
|
110
|
+
)
|
111
|
+
end
|
112
|
+
|
89
113
|
credentials = UserRefreshCredentials.new(
|
90
114
|
client_id: @client_id.id,
|
91
115
|
client_secret: @client_id.secret,
|
@@ -111,14 +135,19 @@ module Google
|
|
111
135
|
# the requested scopes
|
112
136
|
# @return [Google::Auth::UserRefreshCredentials]
|
113
137
|
# Stored credentials, nil if none present
|
138
|
+
# @raise [Google::Auth::CredentialsError]
|
139
|
+
# If the client ID in the stored token doesn't match the configured client ID
|
114
140
|
def get_credentials user_id, scope = nil
|
115
141
|
saved_token = stored_token user_id
|
116
142
|
return nil if saved_token.nil?
|
117
143
|
data = MultiJson.load saved_token
|
118
144
|
|
119
145
|
if data.fetch("client_id", @client_id.id) != @client_id.id
|
120
|
-
raise
|
121
|
-
|
146
|
+
raise CredentialsError.with_details(
|
147
|
+
format(MISMATCHED_CLIENT_ID_ERROR, data["client_id"], @client_id.id),
|
148
|
+
credential_type_name: self.class.name,
|
149
|
+
principal: principal
|
150
|
+
)
|
122
151
|
end
|
123
152
|
|
124
153
|
credentials = UserRefreshCredentials.new(
|
@@ -157,6 +186,8 @@ module Google
|
|
157
186
|
code = options[:code]
|
158
187
|
scope = options[:scope] || @scope
|
159
188
|
base_url = options[:base_url]
|
189
|
+
options[:additional_parameters] ||= {}
|
190
|
+
options[:additional_parameters].merge!({ code_verifier: @code_verifier })
|
160
191
|
credentials = UserRefreshCredentials.new(
|
161
192
|
client_id: @client_id.id,
|
162
193
|
client_secret: @client_id.secret,
|
@@ -216,6 +247,8 @@ module Google
|
|
216
247
|
# Unique ID of the user for loading/storing credentials.
|
217
248
|
# @param [Google::Auth::UserRefreshCredentials] credentials
|
218
249
|
# Credentials to store.
|
250
|
+
# @return [Google::Auth::UserRefreshCredentials]
|
251
|
+
# The stored credentials
|
219
252
|
def store_credentials user_id, credentials
|
220
253
|
json = MultiJson.dump(
|
221
254
|
client_id: credentials.client_id,
|
@@ -228,6 +261,32 @@ module Google
|
|
228
261
|
credentials
|
229
262
|
end
|
230
263
|
|
264
|
+
# The code verifier for PKCE for OAuth 2.0. When set, the
|
265
|
+
# authorization URI will contain the Code Challenge and Code
|
266
|
+
# Challenge Method querystring parameters, and the token URI will
|
267
|
+
# contain the Code Verifier parameter.
|
268
|
+
#
|
269
|
+
# @param [String|nil] new_code_erifier
|
270
|
+
def code_verifier= new_code_verifier
|
271
|
+
@code_verifier = new_code_verifier
|
272
|
+
end
|
273
|
+
|
274
|
+
# Generate the code verifier needed to be sent while fetching
|
275
|
+
# authorization URL.
|
276
|
+
def self.generate_code_verifier
|
277
|
+
random_number = rand 32..96
|
278
|
+
SecureRandom.alphanumeric random_number
|
279
|
+
end
|
280
|
+
|
281
|
+
# Returns the principal identifier for this authorizer
|
282
|
+
# The client ID is used as the principal for user authorizers
|
283
|
+
#
|
284
|
+
# @private
|
285
|
+
# @return [String] The client ID associated with this authorizer
|
286
|
+
def principal
|
287
|
+
@client_id.id
|
288
|
+
end
|
289
|
+
|
231
290
|
private
|
232
291
|
|
233
292
|
# @private Fetch stored token with given user_id
|
@@ -235,9 +294,11 @@ module Google
|
|
235
294
|
# @param [String] user_id
|
236
295
|
# Unique ID of the user for loading/storing credentials.
|
237
296
|
# @return [String] The saved token from @token_store
|
297
|
+
# @raise [Google::Auth::InitializationError]
|
298
|
+
# If user_id is nil or token_store is nil
|
238
299
|
def stored_token user_id
|
239
|
-
raise NIL_USER_ID_ERROR if user_id.nil?
|
240
|
-
raise NIL_TOKEN_STORE_ERROR if @token_store.nil?
|
300
|
+
raise InitializationError, NIL_USER_ID_ERROR if user_id.nil?
|
301
|
+
raise InitializationError, NIL_TOKEN_STORE_ERROR if @token_store.nil?
|
241
302
|
|
242
303
|
@token_store.load user_id
|
243
304
|
end
|
@@ -262,9 +323,17 @@ module Google
|
|
262
323
|
# Absolute URL to resolve the callback against if necessary.
|
263
324
|
# @return [String]
|
264
325
|
# Redirect URI
|
326
|
+
# @raise [Google::Auth::CredentialsError]
|
327
|
+
# If the callback URI is relative and base_url is nil or not absolute
|
265
328
|
def redirect_uri_for base_url
|
266
329
|
return @callback_uri if uri_is_postmessage?(@callback_uri) || !URI(@callback_uri).scheme.nil?
|
267
|
-
|
330
|
+
if base_url.nil? || URI(base_url).scheme.nil?
|
331
|
+
raise CredentialsError.with_details(
|
332
|
+
format(MISSING_ABSOLUTE_URL_ERROR, @callback_uri),
|
333
|
+
credential_type_name: self.class.name,
|
334
|
+
principal: principal
|
335
|
+
)
|
336
|
+
end
|
268
337
|
URI.join(base_url, @callback_uri).to_s
|
269
338
|
end
|
270
339
|
|
@@ -272,6 +341,15 @@ module Google
|
|
272
341
|
def uri_is_postmessage? uri
|
273
342
|
uri.to_s.casecmp("postmessage").zero?
|
274
343
|
end
|
344
|
+
|
345
|
+
def generate_code_challenge code_verifier
|
346
|
+
digest = Digest::SHA256.digest code_verifier
|
347
|
+
Base64.urlsafe_encode64 digest, padding: false
|
348
|
+
end
|
349
|
+
|
350
|
+
def code_challenge_method
|
351
|
+
"S256"
|
352
|
+
end
|
275
353
|
end
|
276
354
|
end
|
277
355
|
end
|
@@ -12,9 +12,10 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
require "googleauth/signet"
|
16
15
|
require "googleauth/credentials_loader"
|
16
|
+
require "googleauth/errors"
|
17
17
|
require "googleauth/scope_util"
|
18
|
+
require "googleauth/signet"
|
18
19
|
require "multi_json"
|
19
20
|
|
20
21
|
module Google
|
@@ -40,7 +41,7 @@ module Google
|
|
40
41
|
|
41
42
|
# Create a UserRefreshCredentials.
|
42
43
|
#
|
43
|
-
# @param json_key_io [IO]
|
44
|
+
# @param json_key_io [IO] An IO object containing the JSON key
|
44
45
|
# @param scope [string|array|nil] the scope(s) to access
|
45
46
|
def self.make_creds options = {}
|
46
47
|
json_key_io, scope = options.values_at :json_key_io, :scope
|
@@ -50,7 +51,8 @@ module Google
|
|
50
51
|
"client_secret" => ENV[CredentialsLoader::CLIENT_SECRET_VAR],
|
51
52
|
"refresh_token" => ENV[CredentialsLoader::REFRESH_TOKEN_VAR],
|
52
53
|
"project_id" => ENV[CredentialsLoader::PROJECT_ID_VAR],
|
53
|
-
"quota_project_id" => nil
|
54
|
+
"quota_project_id" => nil,
|
55
|
+
"universe_domain" => nil
|
54
56
|
}
|
55
57
|
new(token_credential_uri: TOKEN_CRED_URI,
|
56
58
|
client_id: user_creds["client_id"],
|
@@ -58,17 +60,21 @@ module Google
|
|
58
60
|
refresh_token: user_creds["refresh_token"],
|
59
61
|
project_id: user_creds["project_id"],
|
60
62
|
quota_project_id: user_creds["quota_project_id"],
|
61
|
-
scope: scope
|
63
|
+
scope: scope,
|
64
|
+
universe_domain: user_creds["universe_domain"] || "googleapis.com")
|
62
65
|
.configure_connection(options)
|
63
66
|
end
|
64
67
|
|
65
|
-
# Reads
|
66
|
-
#
|
68
|
+
# Reads a JSON key from an IO object and extracts required fields.
|
69
|
+
#
|
70
|
+
# @param [IO] json_key_io An IO object containing the JSON key
|
71
|
+
# @return [Hash] The parsed JSON key
|
72
|
+
# @raise [Google::Auth::InitializationError] If the JSON is missing required fields
|
67
73
|
def self.read_json_key json_key_io
|
68
74
|
json_key = MultiJson.load json_key_io.read
|
69
75
|
wanted = ["client_id", "client_secret", "refresh_token"]
|
70
76
|
wanted.each do |key|
|
71
|
-
raise "the json is missing the #{key} field" unless json_key.key? key
|
77
|
+
raise InitializationError, "the json is missing the #{key} field" unless json_key.key? key
|
72
78
|
end
|
73
79
|
json_key
|
74
80
|
end
|
@@ -83,7 +89,31 @@ module Google
|
|
83
89
|
super options
|
84
90
|
end
|
85
91
|
|
92
|
+
# Creates a duplicate of these credentials
|
93
|
+
# without the Signet::OAuth2::Client-specific
|
94
|
+
# transient state (e.g. cached tokens)
|
95
|
+
#
|
96
|
+
# @param options [Hash] Overrides for the credentials parameters.
|
97
|
+
# The following keys are recognized in addition to keys in the
|
98
|
+
# Signet::OAuth2::Client
|
99
|
+
# * `project_id` the project id to use during the authentication
|
100
|
+
# * `quota_project_id` the quota project id to use
|
101
|
+
# during the authentication
|
102
|
+
def duplicate options = {}
|
103
|
+
options = deep_hash_normalize options
|
104
|
+
super(
|
105
|
+
{
|
106
|
+
project_id: @project_id,
|
107
|
+
quota_project_id: @quota_project_id
|
108
|
+
}.merge(options)
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
86
112
|
# Revokes the credential
|
113
|
+
#
|
114
|
+
# @param [Hash] options Options for revoking the credential
|
115
|
+
# @option options [Faraday::Connection] :connection The connection to use
|
116
|
+
# @raise [Google::Auth::AuthorizationError] If the revocation request fails
|
87
117
|
def revoke! options = {}
|
88
118
|
c = options[:connection] || Faraday.default_connection
|
89
119
|
|
@@ -95,8 +125,11 @@ module Google
|
|
95
125
|
self.refresh_token = nil
|
96
126
|
self.expires_at = 0
|
97
127
|
else
|
98
|
-
raise(
|
99
|
-
|
128
|
+
raise AuthorizationError.with_details(
|
129
|
+
"Unexpected error code #{resp.status}",
|
130
|
+
credential_type_name: self.class.name,
|
131
|
+
principal: principal
|
132
|
+
)
|
100
133
|
end
|
101
134
|
end
|
102
135
|
end
|
@@ -112,6 +145,36 @@ module Google
|
|
112
145
|
Google::Auth::ScopeUtil.normalize(scope)
|
113
146
|
missing_scope.empty?
|
114
147
|
end
|
148
|
+
|
149
|
+
# Destructively updates these credentials
|
150
|
+
#
|
151
|
+
# This method is called by `Signet::OAuth2::Client`'s constructor
|
152
|
+
#
|
153
|
+
# @param options [Hash] Overrides for the credentials parameters.
|
154
|
+
# The following keys are recognized in addition to keys in the
|
155
|
+
# Signet::OAuth2::Client
|
156
|
+
# * `project_id` the project id to use during the authentication
|
157
|
+
# * `quota_project_id` the quota project id to use
|
158
|
+
# during the authentication
|
159
|
+
# @return [Google::Auth::UserRefreshCredentials]
|
160
|
+
def update! options = {}
|
161
|
+
# Normalize all keys to symbols to allow indifferent access.
|
162
|
+
options = deep_hash_normalize options
|
163
|
+
|
164
|
+
@project_id = options[:project_id] if options.key? :project_id
|
165
|
+
@quota_project_id = options[:quota_project_id] if options.key? :quota_project_id
|
166
|
+
|
167
|
+
super(options)
|
168
|
+
|
169
|
+
self
|
170
|
+
end
|
171
|
+
|
172
|
+
# Returns the client ID as the principal for user refresh credentials
|
173
|
+
# @private
|
174
|
+
# @return [String, Symbol] the client ID or :user_refresh if not available
|
175
|
+
def principal
|
176
|
+
@client_id || :user_refresh
|
177
|
+
end
|
115
178
|
end
|
116
179
|
end
|
117
180
|
end
|