feat: Support for ID token credentials.
This commit is contained in:
parent
e9f8ecafb5
commit
c7f82f29f8
1
Gemfile
1
Gemfile
|
@ -24,4 +24,5 @@ platforms :jruby do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
gem "faraday", "~> 0.17"
|
||||||
gem "gems", "~> 1.2"
|
gem "gems", "~> 1.2"
|
||||||
|
|
|
@ -32,6 +32,6 @@ Gem::Specification.new do |gem|
|
||||||
gem.add_dependency "memoist", "~> 0.16"
|
gem.add_dependency "memoist", "~> 0.16"
|
||||||
gem.add_dependency "multi_json", "~> 1.11"
|
gem.add_dependency "multi_json", "~> 1.11"
|
||||||
gem.add_dependency "os", ">= 0.9", "< 2.0"
|
gem.add_dependency "os", ">= 0.9", "< 2.0"
|
||||||
gem.add_dependency "signet", "~> 0.12"
|
gem.add_dependency "signet", "~> 0.14"
|
||||||
gem.add_development_dependency "yard", "~> 0.9"
|
gem.add_development_dependency "yard", "~> 0.9"
|
||||||
end
|
end
|
||||||
|
|
|
@ -51,8 +51,10 @@ module Google
|
||||||
class GCECredentials < Signet::OAuth2::Client
|
class GCECredentials < Signet::OAuth2::Client
|
||||||
# The IP Address is used in the URIs to speed up failures on non-GCE
|
# The IP Address is used in the URIs to speed up failures on non-GCE
|
||||||
# systems.
|
# systems.
|
||||||
COMPUTE_AUTH_TOKEN_URI = "http://169.254.169.254/computeMetadata/v1/"\
|
COMPUTE_AUTH_TOKEN_URI =
|
||||||
"instance/service-accounts/default/token".freeze
|
"http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token".freeze
|
||||||
|
COMPUTE_ID_TOKEN_URI =
|
||||||
|
"http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/identity".freeze
|
||||||
COMPUTE_CHECK_URI = "http://169.254.169.254".freeze
|
COMPUTE_CHECK_URI = "http://169.254.169.254".freeze
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
@ -82,12 +84,18 @@ module Google
|
||||||
def fetch_access_token options = {}
|
def fetch_access_token options = {}
|
||||||
c = options[:connection] || Faraday.default_connection
|
c = options[:connection] || Faraday.default_connection
|
||||||
retry_with_error do
|
retry_with_error do
|
||||||
|
uri = target_audience ? COMPUTE_ID_TOKEN_URI : COMPUTE_AUTH_TOKEN_URI
|
||||||
|
query = target_audience ? { "audience" => target_audience, "format" => "full" } : nil
|
||||||
headers = { "Metadata-Flavor" => "Google" }
|
headers = { "Metadata-Flavor" => "Google" }
|
||||||
resp = c.get COMPUTE_AUTH_TOKEN_URI, nil, headers
|
resp = c.get uri, query, headers
|
||||||
case resp.status
|
case resp.status
|
||||||
when 200
|
when 200
|
||||||
Signet::OAuth2.parse_credentials(resp.body,
|
content_type = resp.headers["content-type"]
|
||||||
resp.headers["content-type"])
|
if content_type == "text/html"
|
||||||
|
{ (target_audience ? "id_token" : "access_token") => resp.body }
|
||||||
|
else
|
||||||
|
Signet::OAuth2.parse_credentials resp.body, content_type
|
||||||
|
end
|
||||||
when 404
|
when 404
|
||||||
raise Signet::AuthorizationError, NO_METADATA_SERVER_ERROR
|
raise Signet::AuthorizationError, NO_METADATA_SERVER_ERROR
|
||||||
else
|
else
|
||||||
|
|
|
@ -47,6 +47,8 @@ module Google
|
||||||
# The default target audience ID to be used when none is provided during initialization.
|
# The default target audience ID to be used when none is provided during initialization.
|
||||||
AUDIENCE = "https://oauth2.googleapis.com/token".freeze
|
AUDIENCE = "https://oauth2.googleapis.com/token".freeze
|
||||||
|
|
||||||
|
@audience = @scope = @target_audience = @env_vars = @paths = nil
|
||||||
|
|
||||||
##
|
##
|
||||||
# The default token credential URI to be used when none is provided during initialization.
|
# The default token credential URI to be used when none is provided during initialization.
|
||||||
# The URI is the authorization server's HTTP endpoint capable of issuing tokens and
|
# The URI is the authorization server's HTTP endpoint capable of issuing tokens and
|
||||||
|
@ -97,20 +99,25 @@ module Google
|
||||||
# A scope is an access range defined by the authorization server.
|
# A scope is an access range defined by the authorization server.
|
||||||
# The scope can be a single value or a list of values.
|
# The scope can be a single value or a list of values.
|
||||||
#
|
#
|
||||||
|
# Either {#scope} or {#target_audience}, but not both, should be non-nil.
|
||||||
|
# If {#scope} is set, this credential will produce access tokens.
|
||||||
|
# If {#target_audience} is set, this credential will produce ID tokens.
|
||||||
|
#
|
||||||
# @return [String, Array<String>]
|
# @return [String, Array<String>]
|
||||||
#
|
#
|
||||||
def self.scope
|
def self.scope
|
||||||
return @scope unless @scope.nil?
|
return @scope unless @scope.nil?
|
||||||
|
|
||||||
tmp_scope = []
|
Array(const_get(:SCOPE)).flatten.uniq if const_defined? :SCOPE
|
||||||
# Pull in values is the SCOPE constant exists.
|
|
||||||
tmp_scope << const_get(:SCOPE) if const_defined? :SCOPE
|
|
||||||
tmp_scope.flatten.uniq
|
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Sets the default scope to be used when none is provided during initialization.
|
# Sets the default scope to be used when none is provided during initialization.
|
||||||
#
|
#
|
||||||
|
# Either {#scope} or {#target_audience}, but not both, should be non-nil.
|
||||||
|
# If {#scope} is set, this credential will produce access tokens.
|
||||||
|
# If {#target_audience} is set, this credential will produce ID tokens.
|
||||||
|
#
|
||||||
# @param [String, Array<String>] new_scope
|
# @param [String, Array<String>] new_scope
|
||||||
# @return [String, Array<String>]
|
# @return [String, Array<String>]
|
||||||
#
|
#
|
||||||
|
@ -119,6 +126,34 @@ module Google
|
||||||
@scope = new_scope
|
@scope = new_scope
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# The default final target audience for ID tokens, to be used when none
|
||||||
|
# is provided during initialization.
|
||||||
|
#
|
||||||
|
# Either {#scope} or {#target_audience}, but not both, should be non-nil.
|
||||||
|
# If {#scope} is set, this credential will produce access tokens.
|
||||||
|
# If {#target_audience} is set, this credential will produce ID tokens.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
#
|
||||||
|
def self.target_audience
|
||||||
|
@target_audience
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Sets the default final target audience for ID tokens, to be used when none
|
||||||
|
# is provided during initialization.
|
||||||
|
#
|
||||||
|
# Either {#scope} or {#target_audience}, but not both, should be non-nil.
|
||||||
|
# If {#scope} is set, this credential will produce access tokens.
|
||||||
|
# If {#target_audience} is set, this credential will produce ID tokens.
|
||||||
|
#
|
||||||
|
# @param [String] new_target_audience
|
||||||
|
#
|
||||||
|
def self.target_audience= new_target_audience
|
||||||
|
@target_audience = new_target_audience
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# The environment variables to search for credentials. Values can either be a file path to the
|
# The environment variables to search for credentials. Values can either be a file path to the
|
||||||
# credentials file, or the JSON contents of the credentials file.
|
# credentials file, or the JSON contents of the credentials file.
|
||||||
|
@ -208,6 +243,9 @@ module Google
|
||||||
# @return [String, Array<String>] The scope for this client. A scope is an access range
|
# @return [String, Array<String>] The scope for this client. A scope is an access range
|
||||||
# defined by the authorization server. The scope can be a single value or a list of values.
|
# defined by the authorization server. The scope can be a single value or a list of values.
|
||||||
#
|
#
|
||||||
|
# @!attribute [r] target_audience
|
||||||
|
# @return [String] The final target audience for ID tokens returned by this credential.
|
||||||
|
#
|
||||||
# @!attribute [r] issuer
|
# @!attribute [r] issuer
|
||||||
# @return [String] The issuer ID associated with this client.
|
# @return [String] The issuer ID associated with this client.
|
||||||
#
|
#
|
||||||
|
@ -220,7 +258,7 @@ module Google
|
||||||
#
|
#
|
||||||
def_delegators :@client,
|
def_delegators :@client,
|
||||||
:token_credential_uri, :audience,
|
:token_credential_uri, :audience,
|
||||||
:scope, :issuer, :signing_key, :updater_proc
|
:scope, :issuer, :signing_key, :updater_proc, :target_audience
|
||||||
|
|
||||||
##
|
##
|
||||||
# Creates a new Credentials instance with the provided auth credentials, and with the default
|
# Creates a new Credentials instance with the provided auth credentials, and with the default
|
||||||
|
@ -319,7 +357,8 @@ module Google
|
||||||
# @private Lookup Credentials using Google::Auth.get_application_default.
|
# @private Lookup Credentials using Google::Auth.get_application_default.
|
||||||
def self.from_application_default options
|
def self.from_application_default options
|
||||||
scope = options[:scope] || self.scope
|
scope = options[:scope] || self.scope
|
||||||
client = Google::Auth.get_application_default scope
|
auth_opts = { target_audience: options[:target_audience] || target_audience }
|
||||||
|
client = Google::Auth.get_application_default scope, auth_opts
|
||||||
new client, options
|
new client, options
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -358,11 +397,18 @@ module Google
|
||||||
options["token_credential_uri"] ||= self.class.token_credential_uri
|
options["token_credential_uri"] ||= self.class.token_credential_uri
|
||||||
options["audience"] ||= self.class.audience
|
options["audience"] ||= self.class.audience
|
||||||
options["scope"] ||= self.class.scope
|
options["scope"] ||= self.class.scope
|
||||||
|
options["target_audience"] ||= self.class.target_audience
|
||||||
|
|
||||||
|
if !Array(options["scope"]).empty? && options["target_audience"]
|
||||||
|
raise ArgumentError, "Cannot specify both scope and target_audience"
|
||||||
|
end
|
||||||
|
|
||||||
|
needs_scope = options["target_audience"].nil?
|
||||||
# client options for initializing signet client
|
# client options for initializing signet client
|
||||||
{ token_credential_uri: options["token_credential_uri"],
|
{ token_credential_uri: options["token_credential_uri"],
|
||||||
audience: options["audience"],
|
audience: options["audience"],
|
||||||
scope: Array(options["scope"]),
|
scope: (needs_scope ? Array(options["scope"]) : nil),
|
||||||
|
target_audience: options["target_audience"],
|
||||||
issuer: options["client_email"],
|
issuer: options["client_email"],
|
||||||
signing_key: OpenSSL::PKey::RSA.new(options["private_key"]) }
|
signing_key: OpenSSL::PKey::RSA.new(options["private_key"]) }
|
||||||
end
|
end
|
||||||
|
@ -376,6 +422,7 @@ module Google
|
||||||
def update_from_hash hash, options
|
def update_from_hash hash, options
|
||||||
hash = stringify_hash_keys hash
|
hash = stringify_hash_keys hash
|
||||||
hash["scope"] ||= options[:scope]
|
hash["scope"] ||= options[:scope]
|
||||||
|
hash["target_audience"] ||= options[:target_audience]
|
||||||
@project_id ||= (hash["project_id"] || hash["project"])
|
@project_id ||= (hash["project_id"] || hash["project"])
|
||||||
@quota_project_id ||= hash["quota_project_id"]
|
@quota_project_id ||= hash["quota_project_id"]
|
||||||
@client = init_client hash, options
|
@client = init_client hash, options
|
||||||
|
@ -385,6 +432,7 @@ module Google
|
||||||
verify_keyfile_exists! path
|
verify_keyfile_exists! path
|
||||||
json = JSON.parse ::File.read(path)
|
json = JSON.parse ::File.read(path)
|
||||||
json["scope"] ||= options[:scope]
|
json["scope"] ||= options[:scope]
|
||||||
|
json["target_audience"] ||= options[:target_audience]
|
||||||
@project_id ||= (json["project_id"] || json["project"])
|
@project_id ||= (json["project_id"] || json["project"])
|
||||||
@quota_project_id ||= json["quota_project_id"]
|
@quota_project_id ||= json["quota_project_id"]
|
||||||
@client = init_client json, options
|
@client = init_client json, options
|
||||||
|
|
|
@ -58,7 +58,9 @@ module Google
|
||||||
# @param json_key_io [IO] an IO from which the JSON key can be read
|
# @param json_key_io [IO] an IO from which the JSON key can be read
|
||||||
# @param scope [string|array|nil] the scope(s) to access
|
# @param scope [string|array|nil] the scope(s) to access
|
||||||
def self.make_creds options = {}
|
def self.make_creds options = {}
|
||||||
json_key_io, scope = options.values_at :json_key_io, :scope
|
json_key_io, scope, target_audience = options.values_at :json_key_io, :scope, :target_audience
|
||||||
|
raise ArgumentError, "Cannot specify both scope and target_audience" if scope && target_audience
|
||||||
|
|
||||||
if json_key_io
|
if json_key_io
|
||||||
private_key, client_email, project_id, quota_project_id = read_json_key json_key_io
|
private_key, client_email, project_id, quota_project_id = read_json_key json_key_io
|
||||||
else
|
else
|
||||||
|
@ -72,6 +74,7 @@ module Google
|
||||||
new(token_credential_uri: TOKEN_CRED_URI,
|
new(token_credential_uri: TOKEN_CRED_URI,
|
||||||
audience: TOKEN_CRED_URI,
|
audience: TOKEN_CRED_URI,
|
||||||
scope: scope,
|
scope: scope,
|
||||||
|
target_audience: target_audience,
|
||||||
issuer: client_email,
|
issuer: client_email,
|
||||||
signing_key: OpenSSL::PKey::RSA.new(private_key),
|
signing_key: OpenSSL::PKey::RSA.new(private_key),
|
||||||
project_id: project_id,
|
project_id: project_id,
|
||||||
|
|
|
@ -48,8 +48,9 @@ module Signet
|
||||||
def apply! a_hash, opts = {}
|
def apply! a_hash, opts = {}
|
||||||
# fetch the access token there is currently not one, or if the client
|
# fetch the access token there is currently not one, or if the client
|
||||||
# has expired
|
# has expired
|
||||||
fetch_access_token! opts if access_token.nil? || expires_within?(60)
|
token_type = target_audience ? :id_token : :access_token
|
||||||
a_hash[AUTH_METADATA_KEY] = "Bearer #{access_token}"
|
fetch_access_token! opts if send(token_type).nil? || expires_within?(60)
|
||||||
|
a_hash[AUTH_METADATA_KEY] = "Bearer #{send token_type}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a clone of a_hash updated with the authentication token
|
# Returns a clone of a_hash updated with the authentication token
|
||||||
|
|
|
@ -45,26 +45,37 @@ shared_examples "apply/apply! are OK" do
|
||||||
# auth client
|
# auth client
|
||||||
describe "#fetch_access_token" do
|
describe "#fetch_access_token" do
|
||||||
let(:token) { "1/abcdef1234567890" }
|
let(:token) { "1/abcdef1234567890" }
|
||||||
let :stub do
|
let :access_stub do
|
||||||
make_auth_stubs access_token: token
|
make_auth_stubs access_token: token
|
||||||
end
|
end
|
||||||
|
let :id_stub do
|
||||||
|
make_auth_stubs id_token: token
|
||||||
|
end
|
||||||
|
|
||||||
it "should set access_token to the fetched value" do
|
it "should set access_token to the fetched value" do
|
||||||
stub
|
access_stub
|
||||||
@client.fetch_access_token!
|
@client.fetch_access_token!
|
||||||
expect(@client.access_token).to eq(token)
|
expect(@client.access_token).to eq(token)
|
||||||
expect(stub).to have_been_requested
|
expect(access_stub).to have_been_requested
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should set id_token to the fetched value" do
|
||||||
|
skip unless @id_client
|
||||||
|
id_stub
|
||||||
|
@id_client.fetch_access_token!
|
||||||
|
expect(@id_client.id_token).to eq(token)
|
||||||
|
expect(id_stub).to have_been_requested
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should notify refresh listeners after updating" do
|
it "should notify refresh listeners after updating" do
|
||||||
stub
|
access_stub
|
||||||
expect do |b|
|
expect do |b|
|
||||||
@client.on_refresh(&b)
|
@client.on_refresh(&b)
|
||||||
@client.fetch_access_token!
|
@client.fetch_access_token!
|
||||||
end.to yield_with_args(have_attributes(
|
end.to yield_with_args(have_attributes(
|
||||||
access_token: "1/abcdef1234567890"
|
access_token: "1/abcdef1234567890"
|
||||||
))
|
))
|
||||||
expect(stub).to have_been_requested
|
expect(access_stub).to have_been_requested
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -37,23 +37,32 @@ require "googleauth/compute_engine"
|
||||||
require "spec_helper"
|
require "spec_helper"
|
||||||
|
|
||||||
describe Google::Auth::GCECredentials do
|
describe Google::Auth::GCECredentials do
|
||||||
MD_URI = "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token".freeze
|
MD_ACCESS_URI = "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token".freeze
|
||||||
|
MD_ID_URI = "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://pubsub.googleapis.com/&format=full".freeze
|
||||||
GCECredentials = Google::Auth::GCECredentials
|
GCECredentials = Google::Auth::GCECredentials
|
||||||
|
|
||||||
before :example do
|
before :example do
|
||||||
@client = GCECredentials.new
|
@client = GCECredentials.new
|
||||||
|
@id_client = GCECredentials.new target_audience: "https://pubsub.googleapis.com/"
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_auth_stubs opts = {}
|
def make_auth_stubs opts
|
||||||
access_token = opts[:access_token] || ""
|
if opts[:access_token]
|
||||||
body = MultiJson.dump("access_token" => access_token,
|
body = MultiJson.dump("access_token" => opts[:access_token],
|
||||||
"token_type" => "Bearer",
|
"token_type" => "Bearer",
|
||||||
"expires_in" => 3600)
|
"expires_in" => 3600)
|
||||||
stub_request(:get, MD_URI)
|
stub_request(:get, MD_ACCESS_URI)
|
||||||
.with(headers: { "Metadata-Flavor" => "Google" })
|
.with(headers: { "Metadata-Flavor" => "Google" })
|
||||||
.to_return(body: body,
|
.to_return(body: body,
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: { "Content-Type" => "application/json" })
|
headers: { "Content-Type" => "application/json" })
|
||||||
|
elsif opts[:id_token]
|
||||||
|
stub_request(:get, MD_ID_URI)
|
||||||
|
.with(headers: { "Metadata-Flavor" => "Google" })
|
||||||
|
.to_return(body: opts[:id_token],
|
||||||
|
status: 200,
|
||||||
|
headers: { "Content-Type" => "text/html" })
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like "apply/apply! are OK"
|
it_behaves_like "apply/apply! are OK"
|
||||||
|
@ -61,7 +70,7 @@ describe Google::Auth::GCECredentials do
|
||||||
context "metadata is unavailable" do
|
context "metadata is unavailable" do
|
||||||
describe "#fetch_access_token" do
|
describe "#fetch_access_token" do
|
||||||
it "should fail if the metadata request returns a 404" do
|
it "should fail if the metadata request returns a 404" do
|
||||||
stub = stub_request(:get, MD_URI)
|
stub = stub_request(:get, MD_ACCESS_URI)
|
||||||
.to_return(status: 404,
|
.to_return(status: 404,
|
||||||
headers: { "Metadata-Flavor" => "Google" })
|
headers: { "Metadata-Flavor" => "Google" })
|
||||||
expect { @client.fetch_access_token! }
|
expect { @client.fetch_access_token! }
|
||||||
|
@ -70,7 +79,7 @@ describe Google::Auth::GCECredentials do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should fail if the metadata request returns an unexpected code" do
|
it "should fail if the metadata request returns an unexpected code" do
|
||||||
stub = stub_request(:get, MD_URI)
|
stub = stub_request(:get, MD_ACCESS_URI)
|
||||||
.to_return(status: 503,
|
.to_return(status: 503,
|
||||||
headers: { "Metadata-Flavor" => "Google" })
|
headers: { "Metadata-Flavor" => "Google" })
|
||||||
expect { @client.fetch_access_token! }
|
expect { @client.fetch_access_token! }
|
||||||
|
|
|
@ -128,24 +128,28 @@ describe Google::Auth::ServiceAccountCredentials do
|
||||||
json_key_io: StringIO.new(cred_json_text),
|
json_key_io: StringIO.new(cred_json_text),
|
||||||
scope: "https://www.googleapis.com/auth/userinfo.profile"
|
scope: "https://www.googleapis.com/auth/userinfo.profile"
|
||||||
)
|
)
|
||||||
|
@id_client = ServiceAccountCredentials.make_creds(
|
||||||
|
json_key_io: StringIO.new(cred_json_text),
|
||||||
|
target_audience: "https://pubsub.googleapis.com/"
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_auth_stubs opts = {}
|
def make_auth_stubs opts
|
||||||
access_token = opts[:access_token] || ""
|
body_fields = { "token_type" => "Bearer", "expires_in" => 3600 }
|
||||||
body = MultiJson.dump("access_token" => access_token,
|
body_fields["access_token"] = opts[:access_token] if opts[:access_token]
|
||||||
"token_type" => "Bearer",
|
body_fields["id_token"] = opts[:id_token] if opts[:id_token]
|
||||||
"expires_in" => 3600)
|
body = MultiJson.dump body_fields
|
||||||
blk = proc do |request|
|
blk = proc do |request|
|
||||||
params = Addressable::URI.form_unencode request.body
|
params = Addressable::URI.form_unencode request.body
|
||||||
_claim, _header = JWT.decode(params.assoc("assertion").last,
|
claim, _header = JWT.decode(params.assoc("assertion").last,
|
||||||
@key.public_key, true,
|
@key.public_key, true,
|
||||||
algorithm: "RS256")
|
algorithm: "RS256")
|
||||||
|
!opts[:id_token] || claim["target_audience"] == "https://pubsub.googleapis.com/"
|
||||||
end
|
end
|
||||||
stub_request(:post, "https://www.googleapis.com/oauth2/v4/token")
|
stub_request(:post, "https://www.googleapis.com/oauth2/v4/token")
|
||||||
.with(body: hash_including(
|
.with(body: hash_including(
|
||||||
"grant_type" => "urn:ietf:params:oauth:grant-type:jwt-bearer"
|
"grant_type" => "urn:ietf:params:oauth:grant-type:jwt-bearer"
|
||||||
),
|
), &blk)
|
||||||
&blk)
|
|
||||||
.to_return(body: body,
|
.to_return(body: body,
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: { "Content-Type" => "application/json" })
|
headers: { "Content-Type" => "application/json" })
|
||||||
|
|
|
@ -47,18 +47,26 @@ describe Signet::OAuth2::Client do
|
||||||
audience: "https://oauth2.googleapis.com/token",
|
audience: "https://oauth2.googleapis.com/token",
|
||||||
signing_key: @key
|
signing_key: @key
|
||||||
)
|
)
|
||||||
|
@id_client = Signet::OAuth2::Client.new(
|
||||||
|
token_credential_uri: "https://oauth2.googleapis.com/token",
|
||||||
|
target_audience: "https://pubsub.googleapis.com/",
|
||||||
|
issuer: "app@example.com",
|
||||||
|
audience: "https://oauth2.googleapis.com/token",
|
||||||
|
signing_key: @key
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_auth_stubs opts
|
def make_auth_stubs opts
|
||||||
access_token = opts[:access_token] || ""
|
body_fields = { "token_type" => "Bearer", "expires_in" => 3600 }
|
||||||
body = MultiJson.dump("access_token" => access_token,
|
body_fields["access_token"] = opts[:access_token] if opts[:access_token]
|
||||||
"token_type" => "Bearer",
|
body_fields["id_token"] = opts[:id_token] if opts[:id_token]
|
||||||
"expires_in" => 3600)
|
body = MultiJson.dump body_fields
|
||||||
blk = proc do |request|
|
blk = proc do |request|
|
||||||
params = Addressable::URI.form_unencode request.body
|
params = Addressable::URI.form_unencode request.body
|
||||||
_claim, _header = JWT.decode(params.assoc("assertion").last,
|
claim, _header = JWT.decode(params.assoc("assertion").last,
|
||||||
@key.public_key, true,
|
@key.public_key, true,
|
||||||
algorithm: "RS256")
|
algorithm: "RS256")
|
||||||
|
!opts[:id_token] || claim["target_audience"] == "https://pubsub.googleapis.com/"
|
||||||
end
|
end
|
||||||
with_params = { body: hash_including(
|
with_params = { body: hash_including(
|
||||||
"grant_type" => "urn:ietf:params:oauth:grant-type:jwt-bearer"
|
"grant_type" => "urn:ietf:params:oauth:grant-type:jwt-bearer"
|
||||||
|
|
|
@ -64,7 +64,7 @@ describe Google::Auth::UserRefreshCredentials do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_auth_stubs opts = {}
|
def make_auth_stubs opts
|
||||||
access_token = opts[:access_token] || ""
|
access_token = opts[:access_token] || ""
|
||||||
body = MultiJson.dump("access_token" => access_token,
|
body = MultiJson.dump("access_token" => access_token,
|
||||||
"token_type" => "Bearer",
|
"token_type" => "Bearer",
|
||||||
|
|
Loading…
Reference in New Issue