From e913f1b722b71f9e695305ea491b2aec8a673029 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Andreassa?= Date: Wed, 14 Jan 2026 13:21:53 -0800 Subject: [PATCH] fix: restore support for JSON keys missing 'type' field --- lib/googleauth/external_account.rb | 7 +++++++ lib/googleauth/service_account.rb | 9 ++++++++- lib/googleauth/user_refresh.rb | 9 ++++++++- spec/googleauth/external_account_spec.rb | 22 ++++++++++++++++++++++ spec/googleauth/service_account_spec.rb | 11 +++++++++++ spec/googleauth/user_refresh_spec.rb | 11 +++++++++++ 6 files changed, 67 insertions(+), 2 deletions(-) diff --git a/lib/googleauth/external_account.rb b/lib/googleauth/external_account.rb index 6f88b45..8886161 100644 --- a/lib/googleauth/external_account.rb +++ b/lib/googleauth/external_account.rb @@ -62,6 +62,13 @@ def self.make_creds options = {} json_key_io, scope = options.values_at :json_key_io, :scope raise InitializationError, "A json file is required for external account credentials." unless json_key_io + json_key = MultiJson.load json_key_io.read, symbolize_keys: true + if json_key.key? :type + json_key_io.rewind + else # Defaults to class credential 'type' if missing. + json_key[:type] = CREDENTIAL_TYPE_NAME + json_key_io = StringIO.new MultiJson.dump(json_key) + end CredentialsLoader.load_and_verify_json_key_type json_key_io, CREDENTIAL_TYPE_NAME user_creds = read_json_key json_key_io diff --git a/lib/googleauth/service_account.rb b/lib/googleauth/service_account.rb index 09f2637..f7be414 100644 --- a/lib/googleauth/service_account.rb +++ b/lib/googleauth/service_account.rb @@ -58,7 +58,7 @@ def enable_self_signed_jwt? # @param json_key_io [IO] An IO object containing the JSON key # @param scope [string|array|nil] the scope(s) to access # @raise [ArgumentError] If both scope and target_audience are specified - def self.make_creds options = {} + def self.make_creds options = {} # rubocop:disable Metrics/MethodLength json_key_io, scope, enable_self_signed_jwt, target_audience, audience, token_credential_uri = options.values_at :json_key_io, :scope, :enable_self_signed_jwt, :target_audience, :audience, :token_credential_uri @@ -66,6 +66,13 @@ def self.make_creds options = {} private_key, client_email, project_id, quota_project_id, universe_domain = if json_key_io + json_key = MultiJson.load json_key_io.read + if json_key.key? "type" + json_key_io.rewind + else # Defaults to class credential 'type' if missing. + json_key["type"] = CREDENTIAL_TYPE_NAME + json_key_io = StringIO.new MultiJson.dump(json_key) + end CredentialsLoader.load_and_verify_json_key_type json_key_io, CREDENTIAL_TYPE_NAME read_json_key json_key_io else diff --git a/lib/googleauth/user_refresh.rb b/lib/googleauth/user_refresh.rb index cc7cf68..37b7fb4 100644 --- a/lib/googleauth/user_refresh.rb +++ b/lib/googleauth/user_refresh.rb @@ -47,9 +47,16 @@ class UserRefreshCredentials < Signet::OAuth2::Client # # @param json_key_io [IO] An IO object containing the JSON key # @param scope [string|array|nil] the scope(s) to access - def self.make_creds options = {} + def self.make_creds options = {} # rubocop:disable Metrics/MethodLength json_key_io, scope = options.values_at :json_key_io, :scope user_creds = if json_key_io + json_key = MultiJson.load json_key_io.read + if json_key.key? "type" + json_key_io.rewind + else # Defaults to class credential 'type' if missing. + json_key["type"] = CREDENTIAL_TYPE_NAME + json_key_io = StringIO.new MultiJson.dump(json_key) + end CredentialsLoader.load_and_verify_json_key_type json_key_io, CREDENTIAL_TYPE_NAME read_json_key json_key_io else diff --git a/spec/googleauth/external_account_spec.rb b/spec/googleauth/external_account_spec.rb index 9cc28e2..90a370e 100644 --- a/spec/googleauth/external_account_spec.rb +++ b/spec/googleauth/external_account_spec.rb @@ -259,5 +259,27 @@ def load_file options f.unlink end end + + it 'should succeed if the credential type is missing (uses default)' do + f = Tempfile.new('missing_type') + begin + f.write(MultiJson.dump({ + audience: '//iam.googleapis.com/projects/123456/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID', + subject_token_type: 'urn:ietf:params:oauth:token-type:jwt', + token_url: 'https://sts.googleapis.com/v1/token', + credential_source: { + 'file' => 'external_suject_token.txt' + }, + })) + f.rewind + expect { Google::Auth::ExternalAccount::Credentials.make_creds(json_key_io: f) } + .not_to raise_error( + Google::Auth::InitializationError, /The provided credentials were not of type 'external_account'/ + ) + ensure + f.close + f.unlink + end + end end end diff --git a/spec/googleauth/service_account_spec.rb b/spec/googleauth/service_account_spec.rb index 2b4a765..25b4cd4 100644 --- a/spec/googleauth/service_account_spec.rb +++ b/spec/googleauth/service_account_spec.rb @@ -119,6 +119,17 @@ def cred_json_text_with_universe_domain ) end + it "succeeds if the credential type is missing (uses default)" do + key_without_type = cred_json.reject { |k, _| k == :type } + expect do + ServiceAccountCredentials.make_creds( + json_key_io: StringIO.new(MultiJson.dump(key_without_type)) + ) + end.not_to raise_error( + Google::Auth::InitializationError, /The provided credentials were not of type 'service_account'/ + ) + end + describe "universe_domain" do it "defaults to googleapis" do expect(@client.universe_domain).to eq("googleapis.com") diff --git a/spec/googleauth/user_refresh_spec.rb b/spec/googleauth/user_refresh_spec.rb index 1cdd9e9..5615f46 100644 --- a/spec/googleauth/user_refresh_spec.rb +++ b/spec/googleauth/user_refresh_spec.rb @@ -95,6 +95,17 @@ def cred_json_text_with_universe_domain missing = nil ) end + it "succeeds if the credential type is missing (uses default)" do + key_without_type = cred_json.reject { |k, _| k == :type } + expect do + UserRefreshCredentials.make_creds( + json_key_io: StringIO.new(MultiJson.dump(key_without_type)) + ) + end.not_to raise_error( + Google::Auth::InitializationError, /The provided credentials were not of type 'authorized_user'/ + ) + end + describe "#from_env" do before :example do @var_name = ENV_VAR