diff --git a/googleauth.gemspec b/googleauth.gemspec index 1aed2fff..18518513 100755 --- a/googleauth.gemspec +++ b/googleauth.gemspec @@ -23,7 +23,7 @@ Gem::Specification.new do |gem| gem.required_ruby_version = ">= 2.7" gem.add_dependency "faraday", ">= 1.0", "< 3.a" - gem.add_dependency "google-cloud-env", "~> 2.0", ">= 2.0.1" + gem.add_dependency "google-cloud-env", "~> 2.1" gem.add_dependency "jwt", ">= 1.4", "< 3.0" gem.add_dependency "multi_json", "~> 1.11" gem.add_dependency "os", ">= 0.9", "< 2.0" diff --git a/lib/googleauth/compute_engine.rb b/lib/googleauth/compute_engine.rb index 441610e9..76cc0103 100644 --- a/lib/googleauth/compute_engine.rb +++ b/lib/googleauth/compute_engine.rb @@ -95,7 +95,7 @@ def fetch_access_token _options = {} resp = Google::Cloud.env.lookup_metadata_response "instance", entry, query: query case resp.status when 200 - build_token_hash resp.body, resp.headers["content-type"] + build_token_hash resp.body, resp.headers["content-type"], resp.retrieval_monotonic_time when 403, 500 msg = "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}" raise Signet::UnexpectedStatusError, msg @@ -112,7 +112,7 @@ def fetch_access_token _options = {} private - def build_token_hash body, content_type + def build_token_hash body, content_type, retrieval_time hash = if ["text/html", "application/text"].include? content_type { token_type.to_s => body } @@ -122,6 +122,15 @@ def build_token_hash body, content_type universe_domain = Google::Cloud.env.lookup_metadata "universe", "universe_domain" universe_domain = "googleapis.com" if !universe_domain || universe_domain.empty? hash["universe_domain"] = universe_domain.strip + # The response might have been cached, which means expires_in might be + # stale. Update it based on the time since the data was retrieved. + # We also ensure expires_in is conservative; subtracting at least 1 + # second to offset any skew from metadata server latency. + if hash["expires_in"].is_a? Numeric + offset = 1 + (Process.clock_gettime(Process::CLOCK_MONOTONIC) - retrieval_time).round + hash["expires_in"] -= offset if offset.positive? + hash["expires_in"] = 0 if hash["expires_in"].negative? + end hash end end diff --git a/spec/googleauth/compute_engine_spec.rb b/spec/googleauth/compute_engine_spec.rb index 324937de..1af4d8a0 100644 --- a/spec/googleauth/compute_engine_spec.rb +++ b/spec/googleauth/compute_engine_spec.rb @@ -77,6 +77,15 @@ def make_auth_stubs opts @client.fetch_access_token! expect(@client.universe_domain).to eq("googleapis.com") end + + it "returns a consistent expiry using cached data" do + make_auth_stubs access_token: "1/abcde" + @client.fetch_access_token! + expiry = @client.expires_at + sleep 1 + @client.fetch_access_token! + expect(@client.expires_at.to_f).to be_within(0.1).of(expiry.to_f) + end end context "custom universe" do diff --git a/spec/googleauth/external_account_spec.rb b/spec/googleauth/external_account_spec.rb index 676aec1e..6cb83d97 100644 --- a/spec/googleauth/external_account_spec.rb +++ b/spec/googleauth/external_account_spec.rb @@ -20,7 +20,7 @@ describe Google::Auth::ExternalAccount::Credentials do - describe "universe_domain checks", :focus do + describe "universe_domain checks" do before :example do @tempfile = Tempfile.new("aws") end