diff --git a/lib/googleauth/compute_engine.rb b/lib/googleauth/compute_engine.rb index d7318e1..d9c3cb7 100644 --- a/lib/googleauth/compute_engine.rb +++ b/lib/googleauth/compute_engine.rb @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +require "base64" +require "json" require "google-cloud-env" require "googleauth/signet" @@ -123,7 +125,7 @@ def fetch_access_token _options = {} def build_token_hash body, content_type, retrieval_time hash = if ["text/html", "application/text"].include? content_type - { token_type.to_s => body } + parse_encoded_token body else Signet::OAuth2.parse_credentials body, content_type end @@ -143,6 +145,18 @@ def build_token_hash body, content_type, retrieval_time end hash end + + def parse_encoded_token body + hash = { token_type.to_s => body } + if token_type == :id_token && body =~ /^[\w=-]+\.([\w=-]+)\.[\w=-]+$/ + base64 = Base64.urlsafe_decode64 Regexp.last_match[1] + json = JSON.parse base64 rescue nil + if json.respond_to?(:key?) && json.key?("exp") + hash["expires_at"] = Time.at json["exp"].to_i + end + end + hash + end end end end diff --git a/spec/googleauth/compute_engine_spec.rb b/spec/googleauth/compute_engine_spec.rb index e0b46f0..ad308ec 100644 --- a/spec/googleauth/compute_engine_spec.rb +++ b/spec/googleauth/compute_engine_spec.rb @@ -84,7 +84,7 @@ def make_auth_stubs opts 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) + expect(@client.expires_at.to_f).to be_within(0.2).of(expiry.to_f) end end @@ -107,7 +107,7 @@ def make_auth_stubs opts 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) + expect(@client.expires_at.to_f).to be_within(0.2).of(expiry.to_f) end end @@ -152,16 +152,46 @@ def make_auth_stubs opts end end - context "metadata is unavailable" do + context "metadata is available" do describe "#fetch_access_token" do - it "should pass scopes when requesting an access token" do + it "should pass scopes" do scopes = ["https://www.googleapis.com/auth/drive", "https://www.googleapis.com/auth/bigtable.data"] stub = make_auth_stubs access_token: "1/abcdef1234567890", scope: scopes @client = GCECredentials.new(scope: scopes) @client.fetch_access_token! expect(stub).to have_been_requested end + end + + describe "Fetch ID tokens" do + it "should parse out expiration time" do + expiry_time = 1608886800 + header = { + alg: "RS256", + kid: "1234567890123456789012345678901234567890", + typ: "JWT" + } + payload = { + aud: "http://www.example.com", + azp: "67890", + email: "googleapis-test@developer.gserviceaccount.com", + email_verified: true, + exp: expiry_time, + iat: expiry_time - 3600, + iss: "https://accounts.google.com", + sub: "12345" + } + token = "#{Base64.urlsafe_encode64 JSON.dump header}.#{Base64.urlsafe_encode64 JSON.dump payload}.xxxxx" + stub = make_auth_stubs id_token: token + @id_client.fetch_access_token! + expect(stub).to have_been_requested + expect(@id_client.expires_at.to_i).to eq(expiry_time) + end + end + end + context "metadata is unavailable" do + describe "#fetch_access_token" do it "should fail if the metadata request returns a 404" do stub = stub_request(:get, MD_ACCESS_URI) .to_return(status: 404, @@ -214,13 +244,6 @@ def make_auth_stubs opts end describe "Fetch ID tokens" do - it "should pass scopes when requesting an ID token" do - scopes = ["https://www.googleapis.com/auth/drive", "https://www.googleapis.com/auth/bigtable.data"] - stub = make_auth_stubs id_token: "1/abcdef1234567890", scope: scopes - @id_client.fetch_access_token! - expect(stub).to have_been_requested - end - it "should fail if the metadata request returns a 404" do stub = stub_request(:get, MD_ID_URI) .to_return(status: 404,