googleauth 1.15.0 → 1.16.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34a8ff0141f7866d8f253bea4adbdd406c8aab873c7f18be6ac78ee981e16a39
4
- data.tar.gz: 1191ce29114f13eb0d855ef46502015eb309cf694bd053171614b15d571e15f7
3
+ metadata.gz: 4cadf68940056f975d386c6cb0cc55b4e1dcebb72a0313a5a5b58347994fa9dd
4
+ data.tar.gz: cad38a2d2014e88abda0e63092786302294c2e963a4e4fca56a681ec7f84f08b
5
5
  SHA512:
6
- metadata.gz: 9286311e4de659c8820cdbdfb4ae48f1a3b0c519c66208a3c3f56a3a1079597b10306d6ab8aef05687df347262f26c75af63c4cbeac7570162829d926edf0317
7
- data.tar.gz: 43954778b73cf95537a29a2317abe5b22eebfc3336e560657ec580542df9a7e9f42513968d27fc2b13621b0da0aa8fb8510ef59c9170bad611b0bd5b86f16888
6
+ metadata.gz: 195efa15c10d767d5ab032291d1c341e760e9bbf326f025f705595e09e6f122f18d582a185ca6c1604a710b8facc11b37cb02e04664b4ba2488b8a85e2318f47
7
+ data.tar.gz: ce881af766994bd312208a9531da24a92efa6f3311abae02cda4f7c1b1d303f54d765b571574745d0cc61cd35e8cc9ceccfdc39a8d9d5368dad6e18f6dbcbd3d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Release History
2
2
 
3
+ ### 1.16.0 (2025-11-21)
4
+
5
+ #### Features
6
+
7
+ * Add ADC support for impersonated credentials ([#547](https://github.com/googleapis/google-auth-library-ruby/issues/547))
8
+ #### Bug Fixes
9
+
10
+ * Include security warning in ExternalAccount and ImpersonatedServiceAccount credentials ([#551](https://github.com/googleapis/google-auth-library-ruby/issues/551))
11
+
12
+ ### 1.15.1 (2025-10-14)
13
+
14
+ #### Bug Fixes
15
+
16
+ * Deprecate method make_creds in DefaultCredentials ([#545](https://github.com/googleapis/google-auth-library-ruby/issues/545))
17
+
3
18
  ### 1.15.0 (2025-08-25)
4
19
 
5
20
  #### Features
data/Credentials.md CHANGED
@@ -38,17 +38,19 @@ that exposes common initialization functionality, such as creating credentials f
38
38
  - For obtaining authentication tokens from GCE metadata server
39
39
  - Used automatically when code is running on Google Compute Engine
40
40
  - Fetches tokens from the metadata server with no additional configuration needed
41
+ - This credential type does not have a supported JSON form
41
42
 
42
43
  4. **Google::Auth::IAMCredentials < Signet::OAuth2::Client** - `lib/googleauth/iam.rb`
43
44
  - For IAM-based authentication (e.g. service-to-service)
44
45
  - Implements authentication-as-a-service for systems already authenticated
45
46
  - Exchanges existing credentials for a short-lived access token
47
+ - This credential type does not have a supported JSON form
46
48
 
47
49
  ## Service Account Authentication
48
50
 
49
51
  5. **Google::Auth::ServiceAccountCredentials < Signet::OAuth2::Client** - `lib/googleauth/service_account.rb`
50
52
  - Authenticates requests using Service Account credentials via an OAuth access token
51
- - Created from JSON key file downloaded from Google Cloud Console
53
+ - Created from JSON key file downloaded from Google Cloud Console. The JSON form of this credential type has a `"type"` field with the value `"service_account"`.
52
54
  - Supports both OAuth access tokens and self-signed JWT authentication
53
55
  - Can specify scopes for access token requests
54
56
 
@@ -64,6 +66,7 @@ that exposes common initialization functionality, such as creating credentials f
64
66
  - Allows a GCP principal identified by a set of source credentials to impersonate a service account
65
67
  - Useful for delegation of authority and managing permissions across service accounts
66
68
  - Source credentials must have the Service Account Token Creator role on the target
69
+ - This credential type supports JSON configuration. The JSON form of this credential type has a `"type"` field with the value `"impersonated_service_account"`.
67
70
 
68
71
  ## User Authentication
69
72
 
@@ -71,7 +74,7 @@ that exposes common initialization functionality, such as creating credentials f
71
74
  - For user refresh token authentication (from 3-legged OAuth flow)
72
75
  - Authenticates on behalf of a user who has authorized the application
73
76
  - Handles token refresh when original access token expires
74
- - Typically obtained through web or installed application flow
77
+ - Typically obtained through web or installed application flow. The JSON form of this credential type has a `"type"` field with the value `"authorized_user"`.
75
78
 
76
79
  `Google::Auth::UserAuthorizer` (`lib/googleauth/user_authorizer.rb`) and `Google::Auth::WebUserAuthorizer` (`lib/googleauth/web_user_authorizer.rb`)
77
80
  are used to facilitate user authentication. The `UserAuthorizer` handles interactive 3-Legged-OAuth2 (3LO) user consent authorization for command-line applications.
@@ -83,6 +86,7 @@ that exposes common initialization functionality, such as creating credentials f
83
86
  types based on credential source (similar to `Google::Auth::get_application_default`).
84
87
  It is included in all External Account credentials types, and it itself includes `Google::Auth::BaseClient` module so all External
85
88
  Account credentials types include `Google::Auth::BaseClient`.
89
+ The JSON form of this credential type has a `"type"` field with the value `"external_account"`.
86
90
 
87
91
  9. **Google::Auth::ExternalAccount::AwsCredentials** - `lib/googleauth/external_account/aws_credentials.rb`
88
92
  - Includes `Google::Auth::BaseClient` module
data/README.md CHANGED
@@ -292,5 +292,4 @@ hesitate to
292
292
  about the client or APIs on [StackOverflow](http://stackoverflow.com).
293
293
 
294
294
  [application default credentials]: https://cloud.google.com/docs/authentication/provide-credentials-adc
295
- [contributing]: https://github.com/googleapis/google-auth-library-ruby/tree/main/.github/CONTRIBUTING.md
296
295
  [license]: https://github.com/googleapis/google-auth-library-ruby/tree/main/LICENSE
@@ -16,6 +16,7 @@ require "forwardable"
16
16
  require "json"
17
17
  require "pathname"
18
18
  require "signet/oauth_2/client"
19
+ require "multi_json"
19
20
 
20
21
  require "googleauth/credentials_loader"
21
22
  require "googleauth/errors"
@@ -508,17 +509,61 @@ module Google
508
509
  token_credential_uri: options[:token_credential_uri] || token_credential_uri,
509
510
  audience: options[:audience] || audience
510
511
  }
511
- client = Google::Auth::DefaultCredentials.make_creds creds_input
512
+
513
+ # Determine the class, which consumes the IO stream
514
+ json_key, clz = Google::Auth::DefaultCredentials.determine_creds_class creds_input[:json_key_io]
515
+
516
+ # Re-serialize the parsed JSON and replace the IO stream in creds_input
517
+ creds_input[:json_key_io] = StringIO.new MultiJson.dump(json_key)
518
+
519
+ client = clz.make_creds creds_input
512
520
  options = options.select { |k, _v| k == :logger }
513
521
  new client, options
514
522
  end
515
523
 
524
+ # @private
525
+ # Initializes the Signet client.
526
+ def self.init_client hash, options = {}
527
+ options = update_client_options options
528
+ io = StringIO.new JSON.generate hash
529
+
530
+ # Determine the class, which consumes the IO stream
531
+ json_key, clz = Google::Auth::DefaultCredentials.determine_creds_class io
532
+
533
+ # Re-serialize the parsed JSON and create a new IO stream.
534
+ new_io = StringIO.new MultiJson.dump(json_key)
535
+
536
+ clz.make_creds options.merge!(json_key_io: new_io)
537
+ end
538
+
539
+ # @private
540
+ # Updates client options with defaults from the credential class
541
+ #
542
+ # @param [Hash] options Options to update
543
+ # @return [Hash] Updated options hash
544
+ # @raise [ArgumentError] If both scope and target_audience are specified
545
+ def self.update_client_options options
546
+ options = options.dup
547
+
548
+ # options have higher priority over constructor defaults
549
+ options[:token_credential_uri] ||= token_credential_uri
550
+ options[:audience] ||= audience
551
+ options[:scope] ||= scope
552
+ options[:target_audience] ||= target_audience
553
+
554
+ if !Array(options[:scope]).empty? && options[:target_audience]
555
+ raise ArgumentError, "Cannot specify both scope and target_audience"
556
+ end
557
+ options.delete :scope unless options[:target_audience].nil?
558
+
559
+ options
560
+ end
561
+
516
562
  private_class_method :from_env_vars,
517
563
  :from_default_paths,
518
564
  :from_application_default,
519
565
  :from_io
520
566
 
521
-
522
567
  # Creates a duplicate of these credentials. This method tries to create the duplicate of the
523
568
  # wrapped credentials if they support duplication and use them as is if they don't.
524
569
  #
@@ -571,14 +616,6 @@ module Google
571
616
  raise InitializationError, "The keyfile '#{keyfile}' is not a valid file." unless exists
572
617
  end
573
618
 
574
- # Initializes the Signet client.
575
- def init_client hash, options = {}
576
- options = update_client_options options
577
- io = StringIO.new JSON.generate hash
578
- options.merge! json_key_io: io
579
- Google::Auth::DefaultCredentials.make_creds options
580
- end
581
-
582
619
  # returns a new Hash with string keys instead of symbol keys.
583
620
  def stringify_hash_keys hash
584
621
  hash.to_h.transform_keys(&:to_s)
@@ -589,28 +626,6 @@ module Google
589
626
  hash.to_h.transform_keys(&:to_sym)
590
627
  end
591
628
 
592
- # Updates client options with defaults from the credential class
593
- #
594
- # @param [Hash] options Options to update
595
- # @return [Hash] Updated options hash
596
- # @raise [ArgumentError] If both scope and target_audience are specified
597
- def update_client_options options
598
- options = options.dup
599
-
600
- # options have higher priority over constructor defaults
601
- options[:token_credential_uri] ||= self.class.token_credential_uri
602
- options[:audience] ||= self.class.audience
603
- options[:scope] ||= self.class.scope
604
- options[:target_audience] ||= self.class.target_audience
605
-
606
- if !Array(options[:scope]).empty? && options[:target_audience]
607
- raise ArgumentError, "Cannot specify both scope and target_audience"
608
- end
609
- options.delete :scope unless options[:target_audience].nil?
610
-
611
- options
612
- end
613
-
614
629
  def update_from_client client
615
630
  @project_id ||= client.project_id if client.respond_to? :project_id
616
631
  @quota_project_id ||= client.quota_project_id if client.respond_to? :quota_project_id
@@ -624,7 +639,7 @@ module Google
624
639
  hash["target_audience"] ||= options[:target_audience]
625
640
  @project_id ||= hash["project_id"] || hash["project"]
626
641
  @quota_project_id ||= hash["quota_project_id"]
627
- @client = init_client hash, options
642
+ @client = self.class.init_client hash, options
628
643
  end
629
644
 
630
645
  def update_from_filepath path, options
@@ -634,7 +649,7 @@ module Google
634
649
  json["target_audience"] ||= options[:target_audience]
635
650
  @project_id ||= json["project_id"] || json["project"]
636
651
  @quota_project_id ||= json["quota_project_id"]
637
- @client = init_client json, options
652
+ @client = self.class.init_client json, options
638
653
  end
639
654
 
640
655
  def setup_logging logger: :default
@@ -158,6 +158,21 @@ module Google
158
158
  nil
159
159
  end
160
160
 
161
+ # @private
162
+ # Loads a JSON key from an IO object, verifies its type, and rewinds the IO.
163
+ #
164
+ # @param json_key_io [IO] An IO object containing the JSON key.
165
+ # @param expected_type [String] The expected credential type name.
166
+ # @raise [Google::Auth::InitializationError] If the JSON key type does not match the expected type.
167
+ def load_and_verify_json_key_type json_key_io, expected_type
168
+ json_key = MultiJson.load json_key_io.read
169
+ json_key_io.rewind # Rewind the stream so it can be read again.
170
+ return if json_key["type"] == expected_type
171
+ raise Google::Auth::InitializationError,
172
+ "The provided credentials were not of type '#{expected_type}'. " \
173
+ "Instead, the type was '#{json_key['type']}'."
174
+ end
175
+
161
176
  private
162
177
 
163
178
  def interpret_options scope, options
@@ -21,6 +21,7 @@ require "googleauth/external_account"
21
21
  require "googleauth/service_account"
22
22
  require "googleauth/service_account_jwt_header"
23
23
  require "googleauth/user_refresh"
24
+ require "googleauth/impersonated_service_account"
24
25
 
25
26
  module Google
26
27
  # Module Auth provides classes that provide Google-specific authorization
@@ -43,61 +44,83 @@ module Google
43
44
  # information, refer to [Validate credential configurations from external
44
45
  # sources](https://cloud.google.com/docs/authentication/external/externally-sourced-credentials).
45
46
  #
47
+ # @deprecated This method is deprecated and will be removed in a future version.
48
+ # Please use the `make_creds` method on the specific credential class you intend to load,
49
+ # e.g., `Google::Auth::ServiceAccountCredentials.make_creds`.
50
+ #
51
+ # This method does not validate the credential configuration. The security
52
+ # risk occurs when a credential configuration is accepted from a source that
53
+ # is not under your control and used without validation on your side.
54
+ #
55
+ # If you know that you will be loading credential configurations of a
56
+ # specific type, it is recommended to use a credential-type-specific
57
+ # `make_creds` method.
58
+ # This will ensure that an unexpected credential type with potential for
59
+ # malicious intent is not loaded unintentionally. You might still have to do
60
+ # validation for certain credential types. Please follow the recommendation
61
+ # for that method. For example, if you want to load only service accounts,
62
+ # you can use:
63
+ # ```
64
+ # creds = Google::Auth::ServiceAccountCredentials.make_creds
65
+ # ```
66
+ # @see Google::Auth::ServiceAccountCredentials.make_creds
67
+ #
68
+ # If you are loading your credential configuration from an untrusted source and have
69
+ # not mitigated the risks (e.g. by validating the configuration yourself), make
70
+ # these changes as soon as possible to prevent security risks to your environment.
71
+ #
72
+ # Regardless of the method used, it is always your responsibility to validate
73
+ # configurations received from external sources.
74
+ #
75
+ # See https://cloud.google.com/docs/authentication/external/externally-sourced-credentials for more details.
76
+ #
46
77
  # @param options [Hash] Options for creating the credentials
47
78
  # @return [Google::Auth::Credentials] The credentials instance
48
79
  # @raise [Google::Auth::InitializationError] If the credentials cannot be determined
49
80
  def self.make_creds options = {}
50
81
  json_key_io = options[:json_key_io]
51
- if json_key_io
52
- json_key, clz = determine_creds_class json_key_io
82
+ json_key, clz = determine_creds_class json_key_io
83
+ if json_key
53
84
  io = StringIO.new MultiJson.dump(json_key)
54
85
  clz.make_creds options.merge(json_key_io: io)
55
86
  else
56
- clz = read_creds
57
87
  clz.make_creds options
58
88
  end
59
89
  end
60
90
 
61
- # Reads the credential type from environment and returns the appropriate class
62
- #
63
- # @return [Class] The credential class to use
64
- # @raise [Google::Auth::InitializationError] If the credentials type is undefined or unsupported
65
- def self.read_creds
66
- env_var = CredentialsLoader::ACCOUNT_TYPE_VAR
67
- type = ENV[env_var]
68
- raise InitializationError, "#{env_var} is undefined in env" unless type
69
- case type
70
- when "service_account"
71
- ServiceAccountCredentials
72
- when "authorized_user"
73
- UserRefreshCredentials
74
- when "external_account"
75
- ExternalAccount::Credentials
76
- else
77
- raise InitializationError, "credentials type '#{type}' is not supported"
78
- end
79
- end
80
-
81
91
  # Reads the input json and determines which creds class to use.
82
92
  #
83
- # @param json_key_io [IO] An IO object containing the JSON key
84
- # @return [Array(Hash, Class)] The JSON key and the credential class to use
85
- # @raise [Google::Auth::InitializationError] If the JSON is missing the type field or has an unsupported type
86
- def self.determine_creds_class json_key_io
87
- json_key = MultiJson.load json_key_io.read
88
- key = "type"
89
- raise InitializationError, "the json is missing the '#{key}' field" unless json_key.key? key
90
- type = json_key[key]
91
- case type
92
- when "service_account"
93
- [json_key, ServiceAccountCredentials]
94
- when "authorized_user"
95
- [json_key, UserRefreshCredentials]
96
- when "external_account"
97
- [json_key, ExternalAccount::Credentials]
93
+ # @param json_key_io [IO, nil] An optional IO object containing the JSON key.
94
+ # If nil, the credential type is determined from environment variables.
95
+ # @return [Array(Hash, Class)] The JSON key (or nil if from environment) and the credential class to use
96
+ # @raise [Google::Auth::InitializationError] If the JSON is missing the type field or has an unsupported type,
97
+ # or if the environment variable is undefined or unsupported.
98
+ def self.determine_creds_class json_key_io = nil
99
+ if json_key_io
100
+ json_key = MultiJson.load json_key_io.read
101
+ key = "type"
102
+ raise InitializationError, "the json is missing the '#{key}' field" unless json_key.key? key
103
+ type = json_key[key]
98
104
  else
99
- raise InitializationError, "credentials type '#{type}' is not supported"
105
+ env_var = CredentialsLoader::ACCOUNT_TYPE_VAR
106
+ type = ENV[env_var]
107
+ raise InitializationError, "#{env_var} is undefined in env" unless type
108
+ json_key = nil
100
109
  end
110
+
111
+ clz = case type
112
+ when ServiceAccountCredentials::CREDENTIAL_TYPE_NAME
113
+ ServiceAccountCredentials
114
+ when UserRefreshCredentials::CREDENTIAL_TYPE_NAME
115
+ UserRefreshCredentials
116
+ when ExternalAccount::Credentials::CREDENTIAL_TYPE_NAME
117
+ ExternalAccount::Credentials
118
+ when ImpersonatedServiceAccountCredentials::CREDENTIAL_TYPE_NAME
119
+ ImpersonatedServiceAccountCredentials
120
+ else
121
+ raise InitializationError, "credentials type '#{type}' is not supported"
122
+ end
123
+ [json_key, clz]
101
124
  end
102
125
  end
103
126
  end
@@ -34,8 +34,21 @@ module Google
34
34
  MISSING_CREDENTIAL_SOURCE = "missing credential source for external account".freeze
35
35
  INVALID_EXTERNAL_ACCOUNT_TYPE = "credential source is not supported external account type".freeze
36
36
 
37
+ # @private
38
+ # @type [::String] The type name for this credential.
39
+ CREDENTIAL_TYPE_NAME = "external_account".freeze
40
+
37
41
  # Create a ExternalAccount::Credentials
38
42
  #
43
+ # @note Warning:
44
+ # This method does not validate the credential configuration. A security
45
+ # risk occurs when a credential configuration configured with malicious urls
46
+ # is used.
47
+ # When the credential configuration is accepted from an
48
+ # untrusted source, you should validate it before using with this method.
49
+ # See https://cloud.google.com/docs/authentication/external/externally-sourced-credentials
50
+ # for more details.
51
+ #
39
52
  # @param options [Hash] Options for creating credentials
40
53
  # @option options [IO] :json_key_io (required) An IO object containing the JSON key
41
54
  # @option options [String,Array,nil] :scope The scope(s) to access
@@ -49,6 +62,7 @@ module Google
49
62
  json_key_io, scope = options.values_at :json_key_io, :scope
50
63
 
51
64
  raise InitializationError, "A json file is required for external account credentials." unless json_key_io
65
+ CredentialsLoader.load_and_verify_json_key_type json_key_io, CREDENTIAL_TYPE_NAME
52
66
  user_creds = read_json_key json_key_io
53
67
 
54
68
  # AWS credentials is determined by aws subject token type
@@ -23,6 +23,9 @@ module Google
23
23
  # and then that claim is exchanged for a short-lived token at an IAMCredentials endpoint.
24
24
  # The short-lived token and its expiration time are cached.
25
25
  class ImpersonatedServiceAccountCredentials
26
+ # @private
27
+ CREDENTIAL_TYPE_NAME = "impersonated_service_account".freeze
28
+
26
29
  # @private
27
30
  ERROR_SUFFIX = <<~ERROR.freeze
28
31
  when trying to get security access token
@@ -69,6 +72,15 @@ module Google
69
72
  # and request short-lived credentials for a service account
70
73
  # that has the authorization that your use case requires.
71
74
  #
75
+ # @note Warning:
76
+ # This method does not validate the credential configuration. A security
77
+ # risk occurs when a credential configuration configured with malicious urls
78
+ # is used.
79
+ # When the credential configuration is accepted from an
80
+ # untrusted source, you should validate it before using with this method.
81
+ # See https://cloud.google.com/docs/authentication/external/externally-sourced-credentials
82
+ # for more details.
83
+ #
72
84
  # @param options [Hash] A hash of options to configure the credentials.
73
85
  # @option options [Object] :base_credentials (required) The authenticated principal.
74
86
  # It will be used as following:
@@ -84,11 +96,50 @@ module Google
84
96
  # defining the permissions required for the token.
85
97
  # @option options [Object] :source_credentials The authenticated principal that will be used
86
98
  # to fetch the short-lived impersonation access token. It is an alternative to providing the base credentials.
99
+ # @option options [IO] :json_key_io The IO object that contains the credential configuration.
100
+ # It is exclusive with `:base_credentials` and `:source_credentials` options.
87
101
  #
88
102
  # @return [Google::Auth::ImpersonatedServiceAccountCredentials]
89
103
  def self.make_creds options = {}
90
- new options
104
+ if options[:json_key_io]
105
+ make_creds_from_json options
106
+ else
107
+ new options
108
+ end
109
+ end
110
+
111
+ # @private
112
+ def self.make_creds_from_json options
113
+ json_key_io = options[:json_key_io]
114
+ if options[:base_credentials] || options[:source_credentials]
115
+ raise Google::Auth::InitializationError,
116
+ "json_key_io is not compatible with base_credentials or source_credentials"
117
+ end
118
+
119
+ require "googleauth/default_credentials"
120
+ impersonated_json = MultiJson.load json_key_io.read
121
+ source_credentials_info = impersonated_json["source_credentials"]
122
+
123
+ if source_credentials_info["type"] == CREDENTIAL_TYPE_NAME
124
+ raise Google::Auth::InitializationError,
125
+ "Source credentials can't be of type impersonated_service_account, " \
126
+ "use delegates to chain impersonation."
127
+ end
128
+
129
+ source_credentials = DefaultCredentials.make_creds(
130
+ json_key_io: StringIO.new(MultiJson.dump(source_credentials_info))
131
+ )
132
+
133
+ impersonation_url = impersonated_json["service_account_impersonation_url"]
134
+ scope = options[:scope] || impersonated_json["scopes"]
135
+
136
+ new(
137
+ source_credentials: source_credentials,
138
+ impersonation_url: impersonation_url,
139
+ scope: scope
140
+ )
91
141
  end
142
+ private_class_method :make_creds_from_json
92
143
 
93
144
  # Initializes a new instance of ImpersonatedServiceAccountCredentials.
94
145
  #
@@ -105,6 +156,7 @@ module Google
105
156
  # - `{source_sa_email}` is the email address of the service account to impersonate.
106
157
  # @option options [Array<String>, String] :scope (required) The scope(s) for the short-lived impersonation token,
107
158
  # defining the permissions required for the token.
159
+ # It will override the scope from the `json_key_io` file if provided.
108
160
  # @option options [Object] :source_credentials The authenticated principal that will be used
109
161
  # to fetch the short-lived impersonation access token. It is an alternative to providing the base credentials.
110
162
  # It is redundant to provide both source and base credentials as only source will be used,
@@ -41,6 +41,10 @@ module Google
41
41
  attr_reader :project_id
42
42
  attr_reader :quota_project_id
43
43
 
44
+ # @private
45
+ # @type [::String] The type name for this credential.
46
+ CREDENTIAL_TYPE_NAME = "service_account".freeze
47
+
44
48
  def enable_self_signed_jwt?
45
49
  # Use a self-singed JWT if there's no information that can be used to
46
50
  # obtain an OAuth token, OR if there are scopes but also an assertion
@@ -60,15 +64,13 @@ module Google
60
64
  :audience, :token_credential_uri
61
65
  raise ArgumentError, "Cannot specify both scope and target_audience" if scope && target_audience
62
66
 
63
- if json_key_io
64
- private_key, client_email, project_id, quota_project_id, universe_domain = read_json_key json_key_io
65
- else
66
- private_key = unescape ENV[CredentialsLoader::PRIVATE_KEY_VAR]
67
- client_email = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
68
- project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
69
- quota_project_id = nil
70
- universe_domain = nil
71
- end
67
+ private_key, client_email, project_id, quota_project_id, universe_domain =
68
+ if json_key_io
69
+ CredentialsLoader.load_and_verify_json_key_type json_key_io, CREDENTIAL_TYPE_NAME
70
+ read_json_key json_key_io
71
+ else
72
+ creds_from_env
73
+ end
72
74
  project_id ||= CredentialsLoader.load_gcloud_project_id
73
75
 
74
76
  new(token_credential_uri: token_credential_uri || TOKEN_CRED_URI,
@@ -190,6 +192,20 @@ module Google
190
192
  alt.logger = logger
191
193
  alt.apply! a_hash
192
194
  end
195
+
196
+ # @private
197
+ # Loads service account credential details from environment variables.
198
+ #
199
+ # @return [Array<String, String, String, nil, nil>] An array containing private_key,
200
+ # client_email, project_id, quota_project_id, and universe_domain.
201
+ def self.creds_from_env
202
+ private_key = unescape ENV[CredentialsLoader::PRIVATE_KEY_VAR]
203
+ client_email = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
204
+ project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
205
+ [private_key, client_email, project_id, nil, nil]
206
+ end
207
+
208
+ private_class_method :creds_from_env
193
209
  end
194
210
  end
195
211
  end
@@ -39,21 +39,29 @@ module Google
39
39
  attr_reader :project_id
40
40
  attr_reader :quota_project_id
41
41
 
42
+ # @private
43
+ # @type [::String] The type name for this credential.
44
+ CREDENTIAL_TYPE_NAME = "authorized_user".freeze
45
+
42
46
  # Create a UserRefreshCredentials.
43
47
  #
44
48
  # @param json_key_io [IO] An IO object containing the JSON key
45
49
  # @param scope [string|array|nil] the scope(s) to access
46
50
  def self.make_creds options = {}
47
51
  json_key_io, scope = options.values_at :json_key_io, :scope
48
- user_creds = read_json_key json_key_io if json_key_io
49
- user_creds ||= {
50
- "client_id" => ENV[CredentialsLoader::CLIENT_ID_VAR],
51
- "client_secret" => ENV[CredentialsLoader::CLIENT_SECRET_VAR],
52
- "refresh_token" => ENV[CredentialsLoader::REFRESH_TOKEN_VAR],
53
- "project_id" => ENV[CredentialsLoader::PROJECT_ID_VAR],
54
- "quota_project_id" => nil,
55
- "universe_domain" => nil
56
- }
52
+ user_creds = if json_key_io
53
+ CredentialsLoader.load_and_verify_json_key_type json_key_io, CREDENTIAL_TYPE_NAME
54
+ read_json_key json_key_io
55
+ else
56
+ {
57
+ "client_id" => ENV[CredentialsLoader::CLIENT_ID_VAR],
58
+ "client_secret" => ENV[CredentialsLoader::CLIENT_SECRET_VAR],
59
+ "refresh_token" => ENV[CredentialsLoader::REFRESH_TOKEN_VAR],
60
+ "project_id" => ENV[CredentialsLoader::PROJECT_ID_VAR],
61
+ "quota_project_id" => nil,
62
+ "universe_domain" => nil
63
+ }
64
+ end
57
65
  new(token_credential_uri: TOKEN_CRED_URI,
58
66
  client_id: user_creds["client_id"],
59
67
  client_secret: user_creds["client_secret"],
@@ -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.15.0".freeze
19
+ VERSION = "1.16.0".freeze
20
20
  end
21
21
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: googleauth
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.15.0
4
+ version: 1.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Google LLC