googleauth 1.11.2 → 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1cfe9034bbf9362f45a8489765bee5b8253deb27c75d50a43b01a4ff3f46a002
4
- data.tar.gz: e5bd5f777b3caa2aeae4d42e0576d8f10295cba035736f741399db80a58c50e0
3
+ metadata.gz: 7f0abc7ed9fc9deefc0b45abbd3b66d6b4a5aa859774f652e2b8c8c8ff12ff77
4
+ data.tar.gz: c8a2ab192e92bf610f96044c04d5e02ad138dd99f2a1311d14d4e029639b210e
5
5
  SHA512:
6
- metadata.gz: 1bf31f0d2c50d10cbb1f2e0aec62bb720e0274b518617d0263e30c0952c72589784102a09f251a11996eb7c05181b1639b685803c575d1090c55ce573a62a9d7
7
- data.tar.gz: e941dad8ae8e72483587a28d014870a694773dac46aa77f8a8ce0e7fabd2202f82d08e5b1bbc062a4e748effa40fb21f450b4b1b5e423cc66ea76ec04b1c2e23
6
+ metadata.gz: 17bde72c9466591719a1f069875242a6abbfa9533fef63d5c9d696c16227d68ac2fd053d63f9d46588cb23c38841eb04a27953a38bbd63c2bc8f8f02709f684c
7
+ data.tar.gz: 4e4c81aad747f3beb7fd610e836ef871e5fa03a2ab7e9b6c2b223cd48e5e891976e736ad90539a493de8ce2805c692667bf3f11162ecb8646ba7a59be82eface
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Release History
2
2
 
3
+ ### 1.12.0 (2024-12-05)
4
+
5
+ #### Features
6
+
7
+ * provided opt-in debug logging ([#490](https://github.com/googleapis/google-auth-library-ruby/issues/490))
8
+
3
9
  ### 1.11.2 (2024-10-23)
4
10
 
5
11
  #### Bug Fixes
@@ -12,6 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ require "google/logging/message"
16
+
15
17
  module Google
16
18
  # Module Auth provides classes that provide Google-specific authorization
17
19
  # used to access Google APIs.
@@ -29,7 +31,12 @@ module Google
29
31
  # fetch the access token there is currently not one, or if the client
30
32
  # has expired
31
33
  fetch_access_token! opts if needs_access_token?
32
- a_hash[AUTH_METADATA_KEY] = "Bearer #{send token_type}"
34
+ token = send token_type
35
+ a_hash[AUTH_METADATA_KEY] = "Bearer #{token}"
36
+ logger&.debug do
37
+ hash = Digest::SHA256.hexdigest token
38
+ Google::Logging::Message.from message: "Sending auth token. (sha256:#{hash})"
39
+ end
33
40
  end
34
41
 
35
42
  # Returns a clone of a_hash updated with the authentication token
@@ -66,6 +73,9 @@ module Google
66
73
  raise NoMethodError, "expires_within? not implemented"
67
74
  end
68
75
 
76
+ # The logger used to log operations on this client, such as token refresh.
77
+ attr_accessor :logger
78
+
69
79
  private
70
80
 
71
81
  def token_type
@@ -96,35 +96,71 @@ module Google
96
96
  # Overrides the super class method to change how access tokens are
97
97
  # fetched.
98
98
  def fetch_access_token _options = {}
99
- if token_type == :id_token
100
- query = { "audience" => target_audience, "format" => "full" }
101
- entry = "service-accounts/default/identity"
102
- else
103
- query = {}
104
- entry = "service-accounts/default/token"
105
- end
99
+ query, entry =
100
+ if token_type == :id_token
101
+ [{ "audience" => target_audience, "format" => "full" }, "service-accounts/default/identity"]
102
+ else
103
+ [{}, "service-accounts/default/token"]
104
+ end
106
105
  query[:scopes] = Array(scope).join "," if scope
107
106
  begin
107
+ log_fetch_query
108
108
  resp = Google::Cloud.env.lookup_metadata_response "instance", entry, query: query
109
+ log_fetch_resp resp
109
110
  case resp.status
110
111
  when 200
111
112
  build_token_hash resp.body, resp.headers["content-type"], resp.retrieval_monotonic_time
112
113
  when 403, 500
113
- msg = "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
114
- raise Signet::UnexpectedStatusError, msg
114
+ raise Signet::UnexpectedStatusError, "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
115
115
  when 404
116
116
  raise Signet::AuthorizationError, NO_METADATA_SERVER_ERROR
117
117
  else
118
- msg = "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
119
- raise Signet::AuthorizationError, msg
118
+ raise Signet::AuthorizationError, "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
120
119
  end
121
120
  rescue Google::Cloud::Env::MetadataServerNotResponding => e
121
+ log_fetch_err e
122
122
  raise Signet::AuthorizationError, e.message
123
123
  end
124
124
  end
125
125
 
126
126
  private
127
127
 
128
+ def log_fetch_query
129
+ if token_type == :id_token
130
+ logger&.info do
131
+ Google::Logging::Message.from(
132
+ message: "Requesting id token from MDS with aud=#{target_audience}",
133
+ "credentialsId" => object_id
134
+ )
135
+ end
136
+ else
137
+ logger&.info do
138
+ Google::Logging::Message.from(
139
+ message: "Requesting access token from MDS",
140
+ "credentialsId" => object_id
141
+ )
142
+ end
143
+ end
144
+ end
145
+
146
+ def log_fetch_resp resp
147
+ logger&.info do
148
+ Google::Logging::Message.from(
149
+ message: "Received #{resp.status} from MDS",
150
+ "credentialsId" => object_id
151
+ )
152
+ end
153
+ end
154
+
155
+ def log_fetch_err _err
156
+ logger&.info do
157
+ Google::Logging::Message.from(
158
+ message: "MDS did not respond to token request",
159
+ "credentialsId" => object_id
160
+ )
161
+ end
162
+ end
163
+
128
164
  def build_token_hash body, content_type, retrieval_time
129
165
  hash =
130
166
  if ["text/html", "application/text"].include? content_type
@@ -337,10 +337,13 @@ module Google
337
337
  # @!attribute [rw] universe_domain
338
338
  # @return [String] The universe domain issuing these credentials.
339
339
  #
340
+ # @!attribute [rw] logger
341
+ # @return [Logger] The logger used to log credential operations such as token refresh.
342
+ #
340
343
  def_delegators :@client,
341
344
  :token_credential_uri, :audience,
342
345
  :scope, :issuer, :signing_key, :updater_proc, :target_audience,
343
- :universe_domain, :universe_domain=
346
+ :universe_domain, :universe_domain=, :logger, :logger=
344
347
 
345
348
  ##
346
349
  # Creates a new Credentials instance with the provided auth credentials, and with the default
@@ -349,16 +352,17 @@ module Google
349
352
  # @param [String, Hash, Signet::OAuth2::Client] keyfile
350
353
  # The keyfile can be provided as one of the following:
351
354
  #
352
- # * The path to a JSON keyfile (as a +String+)
353
- # * The contents of a JSON keyfile (as a +Hash+)
354
- # * A +Signet::OAuth2::Client+ object
355
+ # * The path to a JSON keyfile (as a `String`)
356
+ # * The contents of a JSON keyfile (as a `Hash`)
357
+ # * A `Signet::OAuth2::Client` object
355
358
  # @param [Hash] options
356
359
  # The options for configuring the credentials instance. The following is supported:
357
360
  #
358
- # * +:scope+ - the scope for the client
359
- # * +"project_id"+ (and optionally +"project"+) - the project identifier for the client
360
- # * +:connection_builder+ - the connection builder to use for the client
361
- # * +:default_connection+ - the default connection to use for the client
361
+ # * `:scope` - the scope for the client
362
+ # * `project_id` (and optionally `project`) - the project identifier for the client
363
+ # * `:connection_builder` - the connection builder to use for the client
364
+ # * `:default_connection` - the default connection to use for the client
365
+ # * `:logger` - the logger used to log credential operations such as token refresh.
362
366
  #
363
367
  def initialize keyfile, options = {}
364
368
  verify_keyfile_provided! keyfile
@@ -373,8 +377,8 @@ module Google
373
377
  else
374
378
  update_from_filepath keyfile, options
375
379
  end
380
+ setup_logging logger: options.fetch(:logger, :default)
376
381
  @project_id ||= CredentialsLoader.load_gcloud_project_id
377
- @client.fetch_access_token! if @client.needs_access_token?
378
382
  @env_vars = nil
379
383
  @paths = nil
380
384
  @scope = nil
@@ -468,7 +472,8 @@ module Google
468
472
  audience: options[:audience] || audience
469
473
  }
470
474
  client = Google::Auth::DefaultCredentials.make_creds creds_input
471
- new client
475
+ options = options.select { |k, _v| k == :logger }
476
+ new client, options
472
477
  end
473
478
 
474
479
  private_class_method :from_env_vars,
@@ -549,6 +554,23 @@ module Google
549
554
  @quota_project_id ||= json["quota_project_id"]
550
555
  @client = init_client json, options
551
556
  end
557
+
558
+ def setup_logging logger: :default
559
+ return unless @client.respond_to? :logger=
560
+ logging_env = ENV["GOOGLE_SDK_RUBY_LOGGING_GEMS"].to_s.downcase
561
+ if ["false", "none"].include? logging_env
562
+ logger = nil
563
+ elsif @client.logger
564
+ logger = @client.logger
565
+ elsif logger == :default
566
+ logger = nil
567
+ if ["true", "all"].include?(logging_env) || logging_env.split(",").include?("googleauth")
568
+ formatter = Google::Logging::StructuredFormatter.new if Google::Cloud::Env.get.logging_agent_expected?
569
+ logger = Logger.new $stderr, progname: "googleauth", formatter: formatter
570
+ end
571
+ end
572
+ @client.logger = logger
573
+ end
552
574
  end
553
575
  end
554
576
  end
@@ -129,7 +129,7 @@ module Google
129
129
  if @client_id.nil? && @workforce_pool_user_project
130
130
  additional_options = { userProject: @workforce_pool_user_project }
131
131
  end
132
- @sts_client.exchange_token(
132
+ token_request = {
133
133
  audience: @audience,
134
134
  grant_type: STS_GRANT_TYPE,
135
135
  subject_token: retrieve_subject_token!,
@@ -137,10 +137,31 @@ module Google
137
137
  scopes: @service_account_impersonation_url ? IAM_SCOPE : @scope,
138
138
  requested_token_type: STS_REQUESTED_TOKEN_TYPE,
139
139
  additional_options: additional_options
140
- )
140
+ }
141
+ log_token_request token_request
142
+ @sts_client.exchange_token token_request
143
+ end
144
+
145
+ def log_token_request token_request
146
+ logger&.info do
147
+ Google::Logging::Message.from(
148
+ message: "Requesting access token from #{token_request[:grant_type]}",
149
+ "credentialsId" => object_id
150
+ )
151
+ end
152
+ logger&.debug do
153
+ digest = Digest::SHA256.hexdigest token_request[:subject_token].to_s
154
+ loggable_request = token_request.merge subject_token: "(sha256:#{digest})"
155
+ Google::Logging::Message.from(
156
+ message: "Request data",
157
+ "request" => loggable_request,
158
+ "credentialsId" => object_id
159
+ )
160
+ end
141
161
  end
142
162
 
143
163
  def get_impersonated_access_token token, _options = {}
164
+ log_impersonated_token_request token
144
165
  response = connection.post @service_account_impersonation_url do |req|
145
166
  req.headers["Authorization"] = "Bearer #{token}"
146
167
  req.headers["Content-Type"] = "application/json"
@@ -153,6 +174,16 @@ module Google
153
174
 
154
175
  MultiJson.load response.body
155
176
  end
177
+
178
+ def log_impersonated_token_request original_token
179
+ logger&.info do
180
+ digest = Digest::SHA256.hexdigest original_token
181
+ Google::Logging::Message.from(
182
+ message: "Requesting impersonated access token with original token (sha256:#{digest})",
183
+ "credentialsId" => object_id
184
+ )
185
+ end
186
+ end
156
187
  end
157
188
  end
158
189
  end
@@ -168,7 +168,6 @@ module Google
168
168
  aud: nil,
169
169
  azp: nil,
170
170
  iss: OIDC_ISSUERS
171
-
172
171
  verifier = Verifier.new key_source: oidc_key_source,
173
172
  aud: aud,
174
173
  azp: azp,
@@ -206,7 +205,6 @@ module Google
206
205
  aud: nil,
207
206
  azp: nil,
208
207
  iss: IAP_ISSUERS
209
-
210
208
  verifier = Verifier.new key_source: iap_key_source,
211
209
  aud: aud,
212
210
  azp: azp,
@@ -12,6 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ require "google/logging/message"
15
16
  require "googleauth/signet"
16
17
  require "googleauth/credentials_loader"
17
18
  require "googleauth/json_key_reader"
@@ -123,6 +124,7 @@ module Google
123
124
  }
124
125
  key_io = StringIO.new MultiJson.dump(cred_json)
125
126
  alt = ServiceAccountJwtHeaderCredentials.make_creds json_key_io: key_io, scope: scope
127
+ alt.logger = logger
126
128
  alt.apply! a_hash
127
129
  end
128
130
  end
@@ -147,6 +149,7 @@ module Google
147
149
  attr_reader :project_id
148
150
  attr_reader :quota_project_id
149
151
  attr_accessor :universe_domain
152
+ attr_accessor :logger
150
153
 
151
154
  # Create a ServiceAccountJwtHeaderCredentials.
152
155
  #
@@ -187,10 +190,14 @@ module Google
187
190
  return a_hash if jwt_aud_uri.nil? && @scope.nil?
188
191
  jwt_token = new_jwt_token jwt_aud_uri, opts
189
192
  a_hash[AUTH_METADATA_KEY] = "Bearer #{jwt_token}"
193
+ logger&.debug do
194
+ hash = Digest::SHA256.hexdigest jwt_token
195
+ Google::Logging::Message.from message: "Sending JWT auth token. (sha256:#{hash})"
196
+ end
190
197
  a_hash
191
198
  end
192
199
 
193
- # Returns a clone of a_hash updated with the authoriation header
200
+ # Returns a clone of a_hash updated with the authorization header
194
201
  def apply a_hash, opts = {}
195
202
  a_copy = a_hash.clone
196
203
  apply! a_copy, opts
@@ -219,6 +226,10 @@ module Google
219
226
  assertion["scope"] = Array(@scope).join " " if @scope
220
227
  assertion["aud"] = jwt_aud_uri if jwt_aud_uri
221
228
 
229
+ logger&.debug do
230
+ Google::Logging::Message.from message: "JWT assertion: #{assertion}"
231
+ end
232
+
222
233
  JWT.encode assertion, @signing_key, SIGNING_ALGORITHM
223
234
  end
224
235
 
@@ -65,6 +65,24 @@ module Signet
65
65
  info
66
66
  end
67
67
 
68
+ alias googleauth_orig_generate_access_token_request generate_access_token_request
69
+ def generate_access_token_request options = {}
70
+ parameters = googleauth_orig_generate_access_token_request options
71
+ logger&.info do
72
+ Google::Logging::Message.from(
73
+ message: "Requesting access token from #{parameters['grant_type']}",
74
+ "credentialsId" => object_id
75
+ )
76
+ end
77
+ logger&.debug do
78
+ Google::Logging::Message.from(
79
+ message: "Token fetch params: #{parameters}",
80
+ "credentialsId" => object_id
81
+ )
82
+ end
83
+ parameters
84
+ end
85
+
68
86
  def build_default_connection
69
87
  if !defined?(@connection_info)
70
88
  nil
@@ -79,15 +97,20 @@ module Signet
79
97
  retry_count = 0
80
98
 
81
99
  begin
82
- yield
100
+ yield.tap { |resp| log_response resp }
83
101
  rescue StandardError => e
84
- raise e if e.is_a?(Signet::AuthorizationError) || e.is_a?(Signet::ParseError)
102
+ if e.is_a?(Signet::AuthorizationError) || e.is_a?(Signet::ParseError)
103
+ log_auth_error e
104
+ raise e
105
+ end
85
106
 
86
107
  if retry_count < max_retry_count
108
+ log_transient_error e
87
109
  retry_count += 1
88
110
  sleep retry_count * 0.3
89
111
  retry
90
112
  else
113
+ log_retries_exhausted e
91
114
  msg = "Unexpected error: #{e.inspect}"
92
115
  raise Signet::AuthorizationError, msg
93
116
  end
@@ -106,6 +129,49 @@ module Signet
106
129
  # Shouldn't happen unless we get a garbled ID token
107
130
  nil
108
131
  end
132
+
133
+ def log_response token_response
134
+ response_hash = JSON.parse token_response rescue {}
135
+ if response_hash["access_token"]
136
+ digest = Digest::SHA256.hexdigest response_hash["access_token"]
137
+ response_hash["access_token"] = "(sha256:#{digest})"
138
+ end
139
+ if response_hash["id_token"]
140
+ digest = Digest::SHA256.hexdigest response_hash["id_token"]
141
+ response_hash["id_token"] = "(sha256:#{digest})"
142
+ end
143
+ Google::Logging::Message.from(
144
+ message: "Received auth token response: #{response_hash}",
145
+ "credentialsId" => object_id
146
+ )
147
+ end
148
+
149
+ def log_auth_error err
150
+ logger&.info do
151
+ Google::Logging::Message.from(
152
+ message: "Auth error when fetching auth token: #{err}",
153
+ "credentialsId" => object_id
154
+ )
155
+ end
156
+ end
157
+
158
+ def log_transient_error err
159
+ logger&.info do
160
+ Google::Logging::Message.from(
161
+ message: "Transient error when fetching auth token: #{err}",
162
+ "credentialsId" => object_id
163
+ )
164
+ end
165
+ end
166
+
167
+ def log_retries_exhausted err
168
+ logger&.info do
169
+ Google::Logging::Message.from(
170
+ message: "Exhausted retries when fetching auth token: #{err}",
171
+ "credentialsId" => object_id
172
+ )
173
+ end
174
+ end
109
175
  end
110
176
  end
111
177
  end
@@ -16,6 +16,6 @@ module Google
16
16
  # Module Auth provides classes that provide Google-specific authorization
17
17
  # used to access Google APIs.
18
18
  module Auth
19
- VERSION = "1.11.2".freeze
19
+ VERSION = "1.12.0".freeze
20
20
  end
21
21
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: googleauth
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.11.2
4
+ version: 1.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Emiola
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-23 00:00:00.000000000 Z
11
+ date: 2024-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -36,14 +36,28 @@ dependencies:
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '2.1'
39
+ version: '2.2'
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '2.1'
46
+ version: '2.2'
47
+ - !ruby/object:Gem::Dependency
48
+ name: google-logging-utils
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.1'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.1'
47
61
  - !ruby/object:Gem::Dependency
48
62
  name: jwt
49
63
  requirement: !ruby/object:Gem::Requirement
@@ -186,7 +200,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
186
200
  - !ruby/object:Gem::Version
187
201
  version: '0'
188
202
  requirements: []
189
- rubygems_version: 3.5.21
203
+ rubygems_version: 3.5.23
190
204
  signing_key:
191
205
  specification_version: 4
192
206
  summary: Google Auth Library for Ruby