diff --git a/src/ruby/lib/grpc/auth/compute_engine.rb b/src/ruby/lib/grpc/auth/compute_engine.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9004bef46e5d8e0b44bffbcd2db03d46b81cf525
--- /dev/null
+++ b/src/ruby/lib/grpc/auth/compute_engine.rb
@@ -0,0 +1,69 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+require 'faraday'
+require 'grpc/auth/signet'
+
+module Google
+  module RPC
+    # Module Auth provides classes that provide Google-specific authentication
+    # used to access Google gRPC services.
+    module Auth
+      # Extends Signet::OAuth2::Client so that the auth token is obtained from
+      # the GCE metadata server.
+      class GCECredentials < Signet::OAuth2::Client
+        COMPUTE_AUTH_TOKEN_URI = 'http://metadata/computeMetadata/v1/'\
+                                 'instance/service-accounts/default/token'
+        COMPUTE_CHECK_URI = 'http://metadata.google.internal'
+
+        # Detect if this appear to be a GCE instance, by checking if metadata
+        # is available
+        def self.on_gce?(options = {})
+          c = options[:connection] || Faraday.default_connection
+          resp = c.get(COMPUTE_CHECK_URI)
+          return false unless resp.status == 200
+          return false unless resp.headers.key?('Metadata-Flavor')
+          return resp.headers['Metadata-Flavor'] == 'Google'
+        rescue Faraday::ConnectionFailed
+          return false
+        end
+
+        # Overrides the super class method to change how access tokens are
+        # fetched.
+        def fetch_access_token(options = {})
+          c = options[:connection] || Faraday.default_connection
+          c.headers = { 'Metadata-Flavor' => 'Google' }
+          resp = c.get(COMPUTE_AUTH_TOKEN_URI)
+          Signet::OAuth2.parse_credentials(resp.body,
+                                           resp.headers['content-type'])
+        end
+      end
+    end
+  end
+end
diff --git a/src/ruby/lib/grpc/auth/signet.rb b/src/ruby/lib/grpc/auth/signet.rb
index b46af1696afb75e6f1eb5f50beaadc0b152b4ebd..ba1a3a8d8fd7fddc8a5880846f458ae30ef5a089 100644
--- a/src/ruby/lib/grpc/auth/signet.rb
+++ b/src/ruby/lib/grpc/auth/signet.rb
@@ -46,7 +46,7 @@ module Signet
         # fetch the access token there is currently not one, or if the client
         # has expired
         fetch_access_token!(opts) if access_token.nil? || expired?
-        a_hash[AUTH_METADATA_KEY] = "Bearer: #{access_token}"
+        a_hash[AUTH_METADATA_KEY] = "Bearer #{access_token}"
       end
 
       # Returns a clone of a_hash updated with the authentication token
diff --git a/src/ruby/spec/auth/apply_auth_examples.rb b/src/ruby/spec/auth/apply_auth_examples.rb
index af1f6df04ae4ba7dacc66c86d8ddd3992651d016..f626c54410ad669c5b6290792c35df491502b5a3 100644
--- a/src/ruby/spec/auth/apply_auth_examples.rb
+++ b/src/ruby/spec/auth/apply_auth_examples.rb
@@ -74,12 +74,29 @@ shared_examples 'apply/apply! are OK' do
 
       md = { foo: 'bar' }
       @client.apply!(md, connection: c)
-      want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer: #{token}" }
+      want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{token}" }
       expect(md).to eq(want)
       stubs.verify_stubbed_calls
     end
   end
 
+  describe 'updater_proc' do
+    it 'should provide a proc that updates a hash with the access token' do
+      token = '1/abcdef1234567890'
+      stubs = make_auth_stubs with_access_token: token
+      c = Faraday.new do |b|
+        b.adapter(:test, stubs)
+      end
+
+      md = { foo: 'bar' }
+      the_proc = @client.updater_proc
+      got = the_proc.call(md, connection: c)
+      want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{token}" }
+      expect(got).to eq(want)
+      stubs.verify_stubbed_calls
+    end
+  end
+
   describe '#apply' do
     it 'should not update the original hash with the access token' do
       token = '1/abcdef1234567890'
@@ -104,7 +121,7 @@ shared_examples 'apply/apply! are OK' do
 
       md = { foo: 'bar' }
       got = @client.apply(md, connection: c)
-      want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer: #{token}" }
+      want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{token}" }
       expect(got).to eq(want)
       stubs.verify_stubbed_calls
     end
@@ -120,7 +137,7 @@ shared_examples 'apply/apply! are OK' do
       n.times do |_t|
         md = { foo: 'bar' }
         got = @client.apply(md, connection: c)
-        want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer: #{token}" }
+        want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{token}" }
         expect(got).to eq(want)
       end
       stubs.verify_stubbed_calls
@@ -137,7 +154,7 @@ shared_examples 'apply/apply! are OK' do
         end
         md = { foo: 'bar' }
         got = @client.apply(md, connection: c)
-        want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer: #{t}" }
+        want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{t}" }
         expect(got).to eq(want)
         stubs.verify_stubbed_calls
         @client.expires_at -= 3601 # default is to expire in 1hr
diff --git a/src/ruby/spec/auth/compute_engine_spec.rb b/src/ruby/spec/auth/compute_engine_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9e0b4660fa5443b95197c4c7ad431e4e05a42642
--- /dev/null
+++ b/src/ruby/spec/auth/compute_engine_spec.rb
@@ -0,0 +1,108 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
+$LOAD_PATH.unshift(spec_dir)
+$LOAD_PATH.uniq!
+
+require 'apply_auth_examples'
+require 'faraday'
+require 'grpc/auth/compute_engine'
+require 'spec_helper'
+
+describe Google::RPC::Auth::GCECredentials do
+  MD_URI = '/computeMetadata/v1/instance/service-accounts/default/token'
+  GCECredentials = Google::RPC::Auth::GCECredentials
+
+  before(:example) do
+    @client = GCECredentials.new
+  end
+
+  def make_auth_stubs(with_access_token: '')
+    Faraday::Adapter::Test::Stubs.new do |stub|
+      stub.get(MD_URI) do |env|
+        headers = env[:request_headers]
+        expect(headers['Metadata-Flavor']).to eq('Google')
+        build_json_response(
+            'access_token' => with_access_token,
+            'token_type' => 'Bearer',
+            'expires_in' => 3600)
+      end
+    end
+  end
+
+  it_behaves_like 'apply/apply! are OK'
+
+  describe '#on_gce?' do
+    it 'should be true when Metadata-Flavor is Google' do
+      stubs = Faraday::Adapter::Test::Stubs.new do |stub|
+        stub.get('/') do |_env|
+          [200,
+           { 'Metadata-Flavor' => 'Google' },
+           '']
+        end
+      end
+      c = Faraday.new do |b|
+        b.adapter(:test, stubs)
+      end
+      expect(GCECredentials.on_gce?(connection: c)).to eq(true)
+      stubs.verify_stubbed_calls
+    end
+
+    it 'should be false when Metadata-Flavor is not Google' do
+      stubs = Faraday::Adapter::Test::Stubs.new do |stub|
+        stub.get('/') do |_env|
+          [200,
+           { 'Metadata-Flavor' => 'NotGoogle' },
+           '']
+        end
+      end
+      c = Faraday.new do |b|
+        b.adapter(:test, stubs)
+      end
+      expect(GCECredentials.on_gce?(connection: c)).to eq(false)
+      stubs.verify_stubbed_calls
+    end
+
+    it 'should be false if the response is not 200' do
+      stubs = Faraday::Adapter::Test::Stubs.new do |stub|
+        stub.get('/') do |_env|
+          [404,
+           { 'Metadata-Flavor' => 'Google' },
+           '']
+        end
+      end
+      c = Faraday.new do |b|
+        b.adapter(:test, stubs)
+      end
+      expect(GCECredentials.on_gce?(connection: c)).to eq(false)
+      stubs.verify_stubbed_calls
+    end
+  end
+end