googleauth 1.12.2 → 1.14.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.
@@ -0,0 +1,180 @@
1
+ # Copyright 2025 Google, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require "google/logging/message"
16
+ require "googleauth/credentials_loader"
17
+ require "googleauth/json_key_reader"
18
+ require "jwt"
19
+
20
+ module Google
21
+ # Module Auth provides classes that provide Google-specific authorization
22
+ # used to access Google APIs.
23
+ module Auth
24
+ # Authenticates requests using Google's Service Account credentials via
25
+ # JWT Header.
26
+ #
27
+ # This class allows authorizing requests for service accounts directly
28
+ # from credentials from a json key file downloaded from the developer
29
+ # console (via 'Generate new Json Key'). It is not part of any OAuth2
30
+ # flow, rather it creates a JWT and sends that as a credential.
31
+ #
32
+ # cf [Application Default Credentials](https://cloud.google.com/docs/authentication/production)
33
+ class ServiceAccountJwtHeaderCredentials
34
+ JWT_AUD_URI_KEY = :jwt_aud_uri
35
+ AUTH_METADATA_KEY = Google::Auth::BaseClient::AUTH_METADATA_KEY
36
+ TOKEN_CRED_URI = "https://www.googleapis.com/oauth2/v4/token".freeze
37
+ SIGNING_ALGORITHM = "RS256".freeze
38
+ EXPIRY = 60
39
+
40
+ extend CredentialsLoader
41
+ extend JsonKeyReader
42
+
43
+ attr_reader :project_id
44
+ attr_reader :quota_project_id
45
+ attr_accessor :universe_domain
46
+ attr_accessor :logger
47
+
48
+ # Create a ServiceAccountJwtHeaderCredentials.
49
+ #
50
+ # @param json_key_io [IO] an IO from which the JSON key can be read
51
+ # @param scope [string|array|nil] the scope(s) to access
52
+ def self.make_creds options = {}
53
+ json_key_io, scope = options.values_at :json_key_io, :scope
54
+ new json_key_io: json_key_io, scope: scope
55
+ end
56
+
57
+ # Initializes a ServiceAccountJwtHeaderCredentials.
58
+ #
59
+ # @param json_key_io [IO] an IO from which the JSON key can be read
60
+ def initialize options = {}
61
+ json_key_io = options[:json_key_io]
62
+ if json_key_io
63
+ @private_key, @issuer, @project_id, @quota_project_id, @universe_domain =
64
+ self.class.read_json_key json_key_io
65
+ else
66
+ @private_key = options.key?(:private_key) ? options[:private_key] : ENV[CredentialsLoader::PRIVATE_KEY_VAR]
67
+ @issuer = options.key?(:issuer) ? options[:issuer] : ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
68
+ @project_id = options.key?(:project_id) ? options[:project_id] : ENV[CredentialsLoader::PROJECT_ID_VAR]
69
+ @quota_project_id = options[:quota_project_id] if options.key? :quota_project_id
70
+ @universe_domain = options[:universe_domain] if options.key? :universe_domain
71
+ end
72
+ @universe_domain ||= "googleapis.com"
73
+ @project_id ||= CredentialsLoader.load_gcloud_project_id
74
+ @signing_key = OpenSSL::PKey::RSA.new @private_key
75
+ @scope = options[:scope] if options.key? :scope
76
+ @logger = options[:logger] if options.key? :logger
77
+ end
78
+
79
+ # Creates a duplicate of these credentials
80
+ #
81
+ # @param options [Hash] Overrides for the credentials parameters.
82
+ # The following keys are recognized
83
+ # * `private key` the private key in string form
84
+ # * `issuer` the SA issuer
85
+ # * `scope` the scope(s) to access
86
+ # * `project_id` the project id to use during the authentication
87
+ # * `quota_project_id` the quota project id to use
88
+ # * `universe_domain` the universe domain of the credentials
89
+ def duplicate options = {}
90
+ options = deep_hash_normalize options
91
+
92
+ options = {
93
+ private_key: @private_key,
94
+ issuer: @issuer,
95
+ scope: @scope,
96
+ project_id: project_id,
97
+ quota_project_id: quota_project_id,
98
+ universe_domain: universe_domain,
99
+ logger: logger
100
+ }.merge(options)
101
+
102
+ self.class.new options
103
+ end
104
+
105
+ # Construct a jwt token if the JWT_AUD_URI key is present in the input
106
+ # hash.
107
+ #
108
+ # The jwt token is used as the value of a 'Bearer '.
109
+ def apply! a_hash, opts = {}
110
+ jwt_aud_uri = a_hash.delete JWT_AUD_URI_KEY
111
+ return a_hash if jwt_aud_uri.nil? && @scope.nil?
112
+ jwt_token = new_jwt_token jwt_aud_uri, opts
113
+ a_hash[AUTH_METADATA_KEY] = "Bearer #{jwt_token}"
114
+ logger&.debug do
115
+ hash = Digest::SHA256.hexdigest jwt_token
116
+ Google::Logging::Message.from message: "Sending JWT auth token. (sha256:#{hash})"
117
+ end
118
+ a_hash
119
+ end
120
+
121
+ # Returns a clone of a_hash updated with the authorization header
122
+ def apply a_hash, opts = {}
123
+ a_copy = a_hash.clone
124
+ apply! a_copy, opts
125
+ a_copy
126
+ end
127
+
128
+ # Returns a reference to the #apply method, suitable for passing as
129
+ # a closure
130
+ def updater_proc
131
+ proc { |a_hash, opts = {}| apply a_hash, opts }
132
+ end
133
+
134
+ # Creates a jwt uri token.
135
+ def new_jwt_token jwt_aud_uri = nil, options = {}
136
+ now = Time.new
137
+ skew = options[:skew] || 60
138
+ assertion = {
139
+ "iss" => @issuer,
140
+ "sub" => @issuer,
141
+ "exp" => (now + EXPIRY).to_i,
142
+ "iat" => (now - skew).to_i
143
+ }
144
+
145
+ jwt_aud_uri = nil if @scope
146
+
147
+ assertion["scope"] = Array(@scope).join " " if @scope
148
+ assertion["aud"] = jwt_aud_uri if jwt_aud_uri
149
+
150
+ logger&.debug do
151
+ Google::Logging::Message.from message: "JWT assertion: #{assertion}"
152
+ end
153
+
154
+ JWT.encode assertion, @signing_key, SIGNING_ALGORITHM
155
+ end
156
+
157
+ # Duck-types the corresponding method from BaseClient
158
+ def needs_access_token?
159
+ false
160
+ end
161
+
162
+ private
163
+
164
+ def deep_hash_normalize old_hash
165
+ sym_hash = {}
166
+ old_hash&.each { |k, v| sym_hash[k.to_sym] = recursive_hash_normalize_keys v }
167
+ sym_hash
168
+ end
169
+
170
+ # Convert all keys in this hash (nested) to symbols for uniform retrieval
171
+ def recursive_hash_normalize_keys val
172
+ if val.is_a? Hash
173
+ deep_hash_normalize val
174
+ else
175
+ val
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
@@ -38,6 +38,22 @@ module Signet
38
38
  self
39
39
  end
40
40
 
41
+ alias update_signet_base update!
42
+ def update! options = {}
43
+ # Normalize all keys to symbols to allow indifferent access.
44
+ options = deep_hash_normalize options
45
+
46
+ # This `update!` method "overide" adds the `@logger`` update and
47
+ # the `universe_domain` update.
48
+ #
49
+ # The `universe_domain` is also updated in `update_token!` but is
50
+ # included here for completeness
51
+ self.universe_domain = options[:universe_domain] if options.key? :universe_domain
52
+ @logger = options[:logger] if options.key? :logger
53
+
54
+ update_signet_base options
55
+ end
56
+
41
57
  def configure_connection options
42
58
  @connection_info =
43
59
  options[:connection_builder] || options[:default_connection]
@@ -117,6 +133,42 @@ module Signet
117
133
  end
118
134
  end
119
135
 
136
+ # Creates a duplicate of these credentials
137
+ # without the Signet::OAuth2::Client-specific
138
+ # transient state (e.g. cached tokens)
139
+ #
140
+ # @param options [Hash] Overrides for the credentials parameters.
141
+ # @see Signet::OAuth2::Client#update!
142
+ def duplicate options = {}
143
+ options = deep_hash_normalize options
144
+
145
+ opts = {
146
+ authorization_uri: @authorization_uri,
147
+ token_credential_uri: @token_credential_uri,
148
+ client_id: @client_id,
149
+ client_secret: @client_secret,
150
+ scope: @scope,
151
+ target_audience: @target_audience,
152
+ redirect_uri: @redirect_uri,
153
+ username: @username,
154
+ password: @password,
155
+ issuer: @issuer,
156
+ person: @person,
157
+ sub: @sub,
158
+ audience: @audience,
159
+ signing_key: @signing_key,
160
+ extension_parameters: @extension_parameters,
161
+ additional_parameters: @additional_parameters,
162
+ access_type: @access_type,
163
+ universe_domain: @universe_domain,
164
+ logger: @logger
165
+ }.merge(options)
166
+
167
+ new_client = self.class.new opts
168
+
169
+ new_client.configure_connection options
170
+ end
171
+
120
172
  private
121
173
 
122
174
  def expires_at_from_id_token id_token
@@ -85,6 +85,26 @@ module Google
85
85
  super options
86
86
  end
87
87
 
88
+ # Creates a duplicate of these credentials
89
+ # without the Signet::OAuth2::Client-specific
90
+ # transient state (e.g. cached tokens)
91
+ #
92
+ # @param options [Hash] Overrides for the credentials parameters.
93
+ # The following keys are recognized in addition to keys in the
94
+ # Signet::OAuth2::Client
95
+ # * `project_id` the project id to use during the authentication
96
+ # * `quota_project_id` the quota project id to use
97
+ # during the authentication
98
+ def duplicate options = {}
99
+ options = deep_hash_normalize options
100
+ super(
101
+ {
102
+ project_id: @project_id,
103
+ quota_project_id: @quota_project_id
104
+ }.merge(options)
105
+ )
106
+ end
107
+
88
108
  # Revokes the credential
89
109
  def revoke! options = {}
90
110
  c = options[:connection] || Faraday.default_connection
@@ -114,6 +134,29 @@ module Google
114
134
  Google::Auth::ScopeUtil.normalize(scope)
115
135
  missing_scope.empty?
116
136
  end
137
+
138
+ # Destructively updates these credentials
139
+ #
140
+ # This method is called by `Signet::OAuth2::Client`'s constructor
141
+ #
142
+ # @param options [Hash] Overrides for the credentials parameters.
143
+ # The following keys are recognized in addition to keys in the
144
+ # Signet::OAuth2::Client
145
+ # * `project_id` the project id to use during the authentication
146
+ # * `quota_project_id` the quota project id to use
147
+ # during the authentication
148
+ # @return [Google::Auth::UserRefreshCredentials]
149
+ def update! options = {}
150
+ # Normalize all keys to symbols to allow indifferent access.
151
+ options = deep_hash_normalize options
152
+
153
+ @project_id = options[:project_id] if options.key? :project_id
154
+ @quota_project_id = options[:quota_project_id] if options.key? :quota_project_id
155
+
156
+ super(options)
157
+
158
+ self
159
+ end
117
160
  end
118
161
  end
119
162
  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.12.2".freeze
19
+ VERSION = "1.14.0".freeze
20
20
  end
21
21
  end
data/lib/googleauth.rb CHANGED
@@ -13,9 +13,16 @@
13
13
  # limitations under the License.
14
14
 
15
15
  require "googleauth/application_default"
16
+ require "googleauth/api_key"
17
+ require "googleauth/bearer_token"
16
18
  require "googleauth/client_id"
17
19
  require "googleauth/credentials"
18
20
  require "googleauth/default_credentials"
21
+ require "googleauth/external_account"
19
22
  require "googleauth/id_tokens"
23
+ require "googleauth/impersonated_service_account"
24
+ require "googleauth/service_account"
25
+ require "googleauth/service_account_jwt_header"
20
26
  require "googleauth/user_authorizer"
27
+ require "googleauth/user_refresh"
21
28
  require "googleauth/web_user_authorizer"
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: googleauth
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.12.2
4
+ version: 1.14.0
5
5
  platform: ruby
6
6
  authors:
7
- - Tim Emiola
8
- autorequire:
7
+ - Google LLC
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-12-19 00:00:00.000000000 Z
10
+ date: 2025-03-14 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: faraday
@@ -135,7 +134,7 @@ dependencies:
135
134
  description: Implements simple authorization for accessing Google APIs, and provides
136
135
  support for Application Default Credentials.
137
136
  email:
138
- - temiola@google.com
137
+ - googleapis-packages@google.com
139
138
  executables: []
140
139
  extensions: []
141
140
  extra_rdoc_files: []
@@ -147,8 +146,10 @@ files:
147
146
  - README.md
148
147
  - SECURITY.md
149
148
  - lib/googleauth.rb
149
+ - lib/googleauth/api_key.rb
150
150
  - lib/googleauth/application_default.rb
151
151
  - lib/googleauth/base_client.rb
152
+ - lib/googleauth/bearer_token.rb
152
153
  - lib/googleauth/client_id.rb
153
154
  - lib/googleauth/compute_engine.rb
154
155
  - lib/googleauth/credentials.rb
@@ -166,10 +167,12 @@ files:
166
167
  - lib/googleauth/id_tokens/errors.rb
167
168
  - lib/googleauth/id_tokens/key_sources.rb
168
169
  - lib/googleauth/id_tokens/verifier.rb
170
+ - lib/googleauth/impersonated_service_account.rb
169
171
  - lib/googleauth/json_key_reader.rb
170
172
  - lib/googleauth/oauth2/sts_client.rb
171
173
  - lib/googleauth/scope_util.rb
172
174
  - lib/googleauth/service_account.rb
175
+ - lib/googleauth/service_account_jwt_header.rb
173
176
  - lib/googleauth/signet.rb
174
177
  - lib/googleauth/stores/file_token_store.rb
175
178
  - lib/googleauth/stores/redis_token_store.rb
@@ -185,7 +188,6 @@ metadata:
185
188
  changelog_uri: https://github.com/googleapis/google-auth-library-ruby/blob/main/CHANGELOG.md
186
189
  source_code_uri: https://github.com/googleapis/google-auth-library-ruby
187
190
  bug_tracker_uri: https://github.com/googleapis/google-auth-library-ruby/issues
188
- post_install_message:
189
191
  rdoc_options: []
190
192
  require_paths:
191
193
  - lib
@@ -193,15 +195,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
193
195
  requirements:
194
196
  - - ">="
195
197
  - !ruby/object:Gem::Version
196
- version: '2.7'
198
+ version: '3.0'
197
199
  required_rubygems_version: !ruby/object:Gem::Requirement
198
200
  requirements:
199
201
  - - ">="
200
202
  - !ruby/object:Gem::Version
201
203
  version: '0'
202
204
  requirements: []
203
- rubygems_version: 3.5.23
204
- signing_key:
205
+ rubygems_version: 3.6.5
205
206
  specification_version: 4
206
207
  summary: Google Auth Library for Ruby
207
208
  test_files: []