googleauth 0.9.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.kokoro/continuous/linux.cfg +12 -2
  3. data/.kokoro/continuous/osx.cfg +5 -0
  4. data/.kokoro/continuous/post.cfg +30 -0
  5. data/.kokoro/continuous/windows.cfg +10 -0
  6. data/.kokoro/presubmit/linux.cfg +11 -1
  7. data/.kokoro/presubmit/osx.cfg +5 -0
  8. data/.kokoro/presubmit/windows.cfg +10 -0
  9. data/.kokoro/release.cfg +42 -1
  10. data/.repo-metadata.json +5 -0
  11. data/.rubocop.yml +12 -35
  12. data/CHANGELOG.md +32 -0
  13. data/Gemfile +8 -3
  14. data/README.md +7 -11
  15. data/Rakefile +48 -5
  16. data/googleauth.gemspec +7 -4
  17. data/integration/helper.rb +31 -0
  18. data/integration/id_tokens/key_source_test.rb +74 -0
  19. data/lib/googleauth.rb +1 -0
  20. data/lib/googleauth/application_default.rb +9 -9
  21. data/lib/googleauth/compute_engine.rb +30 -27
  22. data/lib/googleauth/credentials.rb +92 -22
  23. data/lib/googleauth/credentials_loader.rb +14 -15
  24. data/lib/googleauth/id_tokens.rb +233 -0
  25. data/lib/googleauth/id_tokens/errors.rb +71 -0
  26. data/lib/googleauth/id_tokens/key_sources.rb +394 -0
  27. data/lib/googleauth/id_tokens/verifier.rb +144 -0
  28. data/lib/googleauth/json_key_reader.rb +6 -2
  29. data/lib/googleauth/service_account.rb +16 -7
  30. data/lib/googleauth/signet.rb +8 -6
  31. data/lib/googleauth/user_authorizer.rb +8 -3
  32. data/lib/googleauth/user_refresh.rb +1 -1
  33. data/lib/googleauth/version.rb +1 -1
  34. data/lib/googleauth/web_user_authorizer.rb +1 -1
  35. data/rakelib/devsite_builder.rb +45 -0
  36. data/rakelib/link_checker.rb +64 -0
  37. data/rakelib/repo_metadata.rb +59 -0
  38. data/spec/googleauth/apply_auth_examples.rb +28 -5
  39. data/spec/googleauth/compute_engine_spec.rb +37 -13
  40. data/spec/googleauth/credentials_spec.rb +25 -6
  41. data/spec/googleauth/service_account_spec.rb +23 -16
  42. data/spec/googleauth/signet_spec.rb +15 -7
  43. data/spec/googleauth/user_authorizer_spec.rb +21 -1
  44. data/spec/googleauth/user_refresh_spec.rb +1 -1
  45. data/test/helper.rb +33 -0
  46. data/test/id_tokens/key_sources_test.rb +240 -0
  47. data/test/id_tokens/verifier_test.rb +269 -0
  48. metadata +45 -12
@@ -0,0 +1,59 @@
1
+ require "json"
2
+
3
+ class RepoMetadata
4
+ attr_reader :data
5
+
6
+ def initialize data
7
+ @data = data
8
+ normalize_data!
9
+ end
10
+
11
+ def allowed_fields
12
+ [
13
+ "name", "version", "language", "distribution-name",
14
+ "product-page", "github-repository", "issue-tracker"
15
+ ]
16
+ end
17
+
18
+ def build output_directory
19
+ fields = @data.to_a.map { |kv| "--#{kv[0]} #{kv[1]}" }
20
+ Dir.chdir output_directory do
21
+ cmd "python3 -m docuploader create-metadata #{fields.join ' '}"
22
+ end
23
+ end
24
+
25
+ def normalize_data!
26
+ require_relative "../lib/googleauth/version.rb"
27
+
28
+ @data.delete_if { |k, _| !allowed_fields.include?(k) }
29
+ @data["version"] = "v#{Google::Auth::VERSION}"
30
+ end
31
+
32
+ def [] key
33
+ data[key]
34
+ end
35
+
36
+ def []= key, value
37
+ @data[key] = value
38
+ end
39
+
40
+ def cmd line
41
+ puts line
42
+ output = `#{line}`
43
+ puts output
44
+ output
45
+ end
46
+
47
+ def self.from_source source
48
+ if source.is_a? RepoMetadata
49
+ data = source.data
50
+ elsif source.is_a? Hash
51
+ data = source
52
+ elsif File.file? source
53
+ data = JSON.parse File.read(source)
54
+ else
55
+ raise "Source must be a path, hash, or RepoMetadata instance"
56
+ end
57
+ RepoMetadata.new data
58
+ end
59
+ end
@@ -45,26 +45,37 @@ shared_examples "apply/apply! are OK" do
45
45
  # auth client
46
46
  describe "#fetch_access_token" do
47
47
  let(:token) { "1/abcdef1234567890" }
48
- let :stub do
48
+ let :access_stub do
49
49
  make_auth_stubs access_token: token
50
50
  end
51
+ let :id_stub do
52
+ make_auth_stubs id_token: token
53
+ end
51
54
 
52
55
  it "should set access_token to the fetched value" do
53
- stub
56
+ access_stub
54
57
  @client.fetch_access_token!
55
58
  expect(@client.access_token).to eq(token)
56
- expect(stub).to have_been_requested
59
+ expect(access_stub).to have_been_requested
60
+ end
61
+
62
+ it "should set id_token to the fetched value" do
63
+ skip unless @id_client
64
+ id_stub
65
+ @id_client.fetch_access_token!
66
+ expect(@id_client.id_token).to eq(token)
67
+ expect(id_stub).to have_been_requested
57
68
  end
58
69
 
59
70
  it "should notify refresh listeners after updating" do
60
- stub
71
+ access_stub
61
72
  expect do |b|
62
73
  @client.on_refresh(&b)
63
74
  @client.fetch_access_token!
64
75
  end.to yield_with_args(have_attributes(
65
76
  access_token: "1/abcdef1234567890"
66
77
  ))
67
- expect(stub).to have_been_requested
78
+ expect(access_stub).to have_been_requested
68
79
  end
69
80
  end
70
81
 
@@ -79,6 +90,18 @@ shared_examples "apply/apply! are OK" do
79
90
  expect(md).to eq(want)
80
91
  expect(stub).to have_been_requested
81
92
  end
93
+
94
+ it "should update the target hash with fetched ID token" do
95
+ skip unless @id_client
96
+ token = "1/abcdef1234567890"
97
+ stub = make_auth_stubs id_token: token
98
+
99
+ md = { foo: "bar" }
100
+ @id_client.apply! md
101
+ want = { :foo => "bar", auth_key => "Bearer #{token}" }
102
+ expect(md).to eq(want)
103
+ expect(stub).to have_been_requested
104
+ end
82
105
  end
83
106
 
84
107
  describe "updater_proc" do
@@ -37,31 +37,52 @@ require "googleauth/compute_engine"
37
37
  require "spec_helper"
38
38
 
39
39
  describe Google::Auth::GCECredentials do
40
- MD_URI = "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token".freeze
40
+ MD_ACCESS_URI = "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token".freeze
41
+ MD_ID_URI = "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://pubsub.googleapis.com/&format=full".freeze
41
42
  GCECredentials = Google::Auth::GCECredentials
42
43
 
43
44
  before :example do
44
45
  @client = GCECredentials.new
46
+ @id_client = GCECredentials.new target_audience: "https://pubsub.googleapis.com/"
45
47
  end
46
48
 
47
- def make_auth_stubs opts = {}
48
- access_token = opts[:access_token] || ""
49
- body = MultiJson.dump("access_token" => access_token,
50
- "token_type" => "Bearer",
51
- "expires_in" => 3600)
52
- stub_request(:get, MD_URI)
53
- .with(headers: { "Metadata-Flavor" => "Google" })
54
- .to_return(body: body,
55
- status: 200,
56
- headers: { "Content-Type" => "application/json" })
49
+ def make_auth_stubs opts
50
+ if opts[:access_token]
51
+ body = MultiJson.dump("access_token" => opts[:access_token],
52
+ "token_type" => "Bearer",
53
+ "expires_in" => 3600)
54
+
55
+ uri = MD_ACCESS_URI
56
+ uri += "?scopes=#{opts[:scope]}" if opts[:scope]
57
+
58
+ stub_request(:get, uri)
59
+ .with(headers: { "Metadata-Flavor" => "Google" })
60
+ .to_return(body: body,
61
+ status: 200,
62
+ headers: { "Content-Type" => "application/json" })
63
+ elsif opts[:id_token]
64
+ stub_request(:get, MD_ID_URI)
65
+ .with(headers: { "Metadata-Flavor" => "Google" })
66
+ .to_return(body: opts[:id_token],
67
+ status: 200,
68
+ headers: { "Content-Type" => "text/html" })
69
+ end
57
70
  end
58
71
 
59
72
  it_behaves_like "apply/apply! are OK"
60
73
 
61
74
  context "metadata is unavailable" do
62
75
  describe "#fetch_access_token" do
76
+ it "should pass scopes when requesting an access token" do
77
+ scope = "https://www.googleapis.com/auth/drive"
78
+ stub = make_auth_stubs access_token: "1/abcdef1234567890", scope: scope
79
+ @client = GCECredentials.new(scope: [scope])
80
+ @client.fetch_access_token!
81
+ expect(stub).to have_been_requested
82
+ end
83
+
63
84
  it "should fail if the metadata request returns a 404" do
64
- stub = stub_request(:get, MD_URI)
85
+ stub = stub_request(:get, MD_ACCESS_URI)
65
86
  .to_return(status: 404,
66
87
  headers: { "Metadata-Flavor" => "Google" })
67
88
  expect { @client.fetch_access_token! }
@@ -70,7 +91,7 @@ describe Google::Auth::GCECredentials do
70
91
  end
71
92
 
72
93
  it "should fail if the metadata request returns an unexpected code" do
73
- stub = stub_request(:get, MD_URI)
94
+ stub = stub_request(:get, MD_ACCESS_URI)
74
95
  .to_return(status: 503,
75
96
  headers: { "Metadata-Flavor" => "Google" })
76
97
  expect { @client.fetch_access_token! }
@@ -97,6 +118,7 @@ describe Google::Auth::GCECredentials do
97
118
  describe "#on_gce?" do
98
119
  it "should be true when Metadata-Flavor is Google" do
99
120
  stub = stub_request(:get, "http://169.254.169.254")
121
+ .with(headers: { "Metadata-Flavor" => "Google" })
100
122
  .to_return(status: 200,
101
123
  headers: { "Metadata-Flavor" => "Google" })
102
124
  expect(GCECredentials.on_gce?({}, true)).to eq(true)
@@ -105,6 +127,7 @@ describe Google::Auth::GCECredentials do
105
127
 
106
128
  it "should be false when Metadata-Flavor is not Google" do
107
129
  stub = stub_request(:get, "http://169.254.169.254")
130
+ .with(headers: { "Metadata-Flavor" => "Google" })
108
131
  .to_return(status: 200,
109
132
  headers: { "Metadata-Flavor" => "NotGoogle" })
110
133
  expect(GCECredentials.on_gce?({}, true)).to eq(false)
@@ -113,6 +136,7 @@ describe Google::Auth::GCECredentials do
113
136
 
114
137
  it "should be false if the response is not 200" do
115
138
  stub = stub_request(:get, "http://169.254.169.254")
139
+ .with(headers: { "Metadata-Flavor" => "Google" })
116
140
  .to_return(status: 404,
117
141
  headers: { "Metadata-Flavor" => "NotGoogle" })
118
142
  expect(GCECredentials.on_gce?({}, true)).to eq(false)
@@ -36,12 +36,13 @@ require "googleauth"
36
36
  describe Google::Auth::Credentials, :private do
37
37
  let :default_keyfile_hash do
38
38
  {
39
- "private_key_id" => "testabc1234567890xyz",
40
- "private_key" => "-----BEGIN RSA PRIVATE KEY-----\nMIIBOwIBAAJBAOyi0Hy1l4Ym2m2o71Q0TF4O9E81isZEsX0bb+Bqz1SXEaSxLiXM\nUZE8wu0eEXivXuZg6QVCW/5l+f2+9UPrdNUCAwEAAQJAJkqubA/Chj3RSL92guy3\nktzeodarLyw8gF8pOmpuRGSiEo/OLTeRUMKKD1/kX4f9sxf3qDhB4e7dulXR1co/\nIQIhAPx8kMW4XTTL6lJYd2K5GrH8uBMp8qL5ya3/XHrBgw3dAiEA7+3Iw3ULTn2I\n1J34WlJ2D5fbzMzB4FAHUNEV7Ys3f1kCIQDtUahCMChrl7+H5t9QS+xrn77lRGhs\nB50pjvy95WXpgQIhAI2joW6JzTfz8fAapb+kiJ/h9Vcs1ZN3iyoRlNFb61JZAiA8\nNy5NyNrMVwtB/lfJf1dAK/p/Bwd8LZLtgM6PapRfgw==\n-----END RSA PRIVATE KEY-----\n",
41
- "client_email" => "credz-testabc1234567890xyz@developer.gserviceaccount.com",
42
- "client_id" => "credz-testabc1234567890xyz.apps.googleusercontent.com",
43
- "type" => "service_account",
44
- "project_id" => "a_project_id"
39
+ "private_key_id" => "testabc1234567890xyz",
40
+ "private_key" => "-----BEGIN RSA PRIVATE KEY-----\nMIIBOwIBAAJBAOyi0Hy1l4Ym2m2o71Q0TF4O9E81isZEsX0bb+Bqz1SXEaSxLiXM\nUZE8wu0eEXivXuZg6QVCW/5l+f2+9UPrdNUCAwEAAQJAJkqubA/Chj3RSL92guy3\nktzeodarLyw8gF8pOmpuRGSiEo/OLTeRUMKKD1/kX4f9sxf3qDhB4e7dulXR1co/\nIQIhAPx8kMW4XTTL6lJYd2K5GrH8uBMp8qL5ya3/XHrBgw3dAiEA7+3Iw3ULTn2I\n1J34WlJ2D5fbzMzB4FAHUNEV7Ys3f1kCIQDtUahCMChrl7+H5t9QS+xrn77lRGhs\nB50pjvy95WXpgQIhAI2joW6JzTfz8fAapb+kiJ/h9Vcs1ZN3iyoRlNFb61JZAiA8\nNy5NyNrMVwtB/lfJf1dAK/p/Bwd8LZLtgM6PapRfgw==\n-----END RSA PRIVATE KEY-----\n",
41
+ "client_email" => "credz-testabc1234567890xyz@developer.gserviceaccount.com",
42
+ "client_id" => "credz-testabc1234567890xyz.apps.googleusercontent.com",
43
+ "type" => "service_account",
44
+ "project_id" => "a_project_id",
45
+ "quota_project_id" => "b_project_id"
45
46
  }
46
47
  end
47
48
 
@@ -118,6 +119,7 @@ describe Google::Auth::Credentials, :private do
118
119
  expect(creds).to be_a_kind_of(TestCredentials1)
119
120
  expect(creds.client).to eq(mocked_signet)
120
121
  expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
122
+ expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
121
123
  end
122
124
 
123
125
  it "subclasses can use PATH_ENV_VARS to get keyfile path" do
@@ -128,6 +130,7 @@ describe Google::Auth::Credentials, :private do
128
130
  DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
129
131
  end
130
132
 
133
+ allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
131
134
  allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
132
135
  allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
133
136
  allow(::ENV).to receive(:[]).with("PATH_ENV_TEST") { "/unknown/path/to/file.txt" }
@@ -152,6 +155,7 @@ describe Google::Auth::Credentials, :private do
152
155
  expect(creds).to be_a_kind_of(TestCredentials2)
153
156
  expect(creds.client).to eq(mocked_signet)
154
157
  expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
158
+ expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
155
159
  end
156
160
 
157
161
  it "subclasses can use JSON_ENV_VARS to get keyfile contents" do
@@ -164,6 +168,7 @@ describe Google::Auth::Credentials, :private do
164
168
  DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
165
169
  end
166
170
 
171
+ allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
167
172
  allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
168
173
  allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
169
174
  allow(::File).to receive(:file?).with(test_json_env_val) { false }
@@ -188,6 +193,7 @@ describe Google::Auth::Credentials, :private do
188
193
  expect(creds).to be_a_kind_of(TestCredentials3)
189
194
  expect(creds.client).to eq(mocked_signet)
190
195
  expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
196
+ expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
191
197
  end
192
198
 
193
199
  it "subclasses can use DEFAULT_PATHS to get keyfile path" do
@@ -198,6 +204,7 @@ describe Google::Auth::Credentials, :private do
198
204
  DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
199
205
  end
200
206
 
207
+ allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
201
208
  allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
202
209
  allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
203
210
  allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
@@ -222,6 +229,7 @@ describe Google::Auth::Credentials, :private do
222
229
  expect(creds).to be_a_kind_of(TestCredentials4)
223
230
  expect(creds.client).to eq(mocked_signet)
224
231
  expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
232
+ expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
225
233
  end
226
234
 
227
235
  it "subclasses that find no matches default to Google::Auth.get_application_default" do
@@ -232,6 +240,7 @@ describe Google::Auth::Credentials, :private do
232
240
  DEFAULT_PATHS = ["~/default/path/to/file.txt"].freeze
233
241
  end
234
242
 
243
+ allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
235
244
  allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
236
245
  allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
237
246
  allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
@@ -262,6 +271,7 @@ describe Google::Auth::Credentials, :private do
262
271
  expect(creds).to be_a_kind_of(TestCredentials5)
263
272
  expect(creds.client).to eq(mocked_signet)
264
273
  expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
274
+ expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
265
275
  end
266
276
  end
267
277
 
@@ -301,6 +311,7 @@ describe Google::Auth::Credentials, :private do
301
311
  expect(creds).to be_a_kind_of(TestCredentials11)
302
312
  expect(creds.client).to eq(mocked_signet)
303
313
  expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
314
+ expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
304
315
  end
305
316
 
306
317
  it "subclasses can use PATH_ENV_VARS to get keyfile path" do
@@ -310,6 +321,7 @@ describe Google::Auth::Credentials, :private do
310
321
  self.paths = ["~/default/path/to/file.txt"]
311
322
  end
312
323
 
324
+ allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
313
325
  allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
314
326
  allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
315
327
  allow(::ENV).to receive(:[]).with("PATH_ENV_TEST") { "/unknown/path/to/file.txt" }
@@ -334,6 +346,7 @@ describe Google::Auth::Credentials, :private do
334
346
  expect(creds).to be_a_kind_of(TestCredentials12)
335
347
  expect(creds.client).to eq(mocked_signet)
336
348
  expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
349
+ expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
337
350
  end
338
351
 
339
352
  it "subclasses can use JSON_ENV_VARS to get keyfile contents" do
@@ -345,6 +358,7 @@ describe Google::Auth::Credentials, :private do
345
358
  self.paths = ["~/default/path/to/file.txt"]
346
359
  end
347
360
 
361
+ allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
348
362
  allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
349
363
  allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
350
364
  allow(::File).to receive(:file?).with(test_json_env_val) { false }
@@ -369,6 +383,7 @@ describe Google::Auth::Credentials, :private do
369
383
  expect(creds).to be_a_kind_of(TestCredentials13)
370
384
  expect(creds.client).to eq(mocked_signet)
371
385
  expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
386
+ expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
372
387
  end
373
388
 
374
389
  it "subclasses can use DEFAULT_PATHS to get keyfile path" do
@@ -378,6 +393,7 @@ describe Google::Auth::Credentials, :private do
378
393
  self.paths = ["~/default/path/to/file.txt"]
379
394
  end
380
395
 
396
+ allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
381
397
  allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
382
398
  allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
383
399
  allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
@@ -402,6 +418,7 @@ describe Google::Auth::Credentials, :private do
402
418
  expect(creds).to be_a_kind_of(TestCredentials14)
403
419
  expect(creds.client).to eq(mocked_signet)
404
420
  expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
421
+ expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
405
422
  end
406
423
 
407
424
  it "subclasses that find no matches default to Google::Auth.get_application_default" do
@@ -411,6 +428,7 @@ describe Google::Auth::Credentials, :private do
411
428
  self.paths = ["~/default/path/to/file.txt"]
412
429
  end
413
430
 
431
+ allow(::ENV).to receive(:[]).with("GOOGLE_AUTH_SUPPRESS_CREDENTIALS_WARNINGS") { "true" }
414
432
  allow(::ENV).to receive(:[]).with("PATH_ENV_DUMMY") { "/fake/path/to/file.txt" }
415
433
  allow(::File).to receive(:file?).with("/fake/path/to/file.txt") { false }
416
434
  allow(::ENV).to receive(:[]).with("JSON_ENV_DUMMY") { nil }
@@ -441,6 +459,7 @@ describe Google::Auth::Credentials, :private do
441
459
  expect(creds).to be_a_kind_of(TestCredentials15)
442
460
  expect(creds.client).to eq(mocked_signet)
443
461
  expect(creds.project_id).to eq(default_keyfile_hash["project_id"])
462
+ expect(creds.quota_project_id).to eq(default_keyfile_hash["quota_project_id"])
444
463
  end
445
464
  end
446
465
 
@@ -112,12 +112,13 @@ describe Google::Auth::ServiceAccountCredentials do
112
112
  let(:client_email) { "app@developer.gserviceaccount.com" }
113
113
  let :cred_json do
114
114
  {
115
- private_key_id: "a_private_key_id",
116
- private_key: @key.to_pem,
117
- client_email: client_email,
118
- client_id: "app.apps.googleusercontent.com",
119
- type: "service_account",
120
- project_id: "a_project_id"
115
+ private_key_id: "a_private_key_id",
116
+ private_key: @key.to_pem,
117
+ client_email: client_email,
118
+ client_id: "app.apps.googleusercontent.com",
119
+ type: "service_account",
120
+ project_id: "a_project_id",
121
+ quota_project_id: "b_project_id"
121
122
  }
122
123
  end
123
124
 
@@ -127,24 +128,28 @@ describe Google::Auth::ServiceAccountCredentials do
127
128
  json_key_io: StringIO.new(cred_json_text),
128
129
  scope: "https://www.googleapis.com/auth/userinfo.profile"
129
130
  )
131
+ @id_client = ServiceAccountCredentials.make_creds(
132
+ json_key_io: StringIO.new(cred_json_text),
133
+ target_audience: "https://pubsub.googleapis.com/"
134
+ )
130
135
  end
131
136
 
132
- def make_auth_stubs opts = {}
133
- access_token = opts[:access_token] || ""
134
- body = MultiJson.dump("access_token" => access_token,
135
- "token_type" => "Bearer",
136
- "expires_in" => 3600)
137
+ def make_auth_stubs opts
138
+ body_fields = { "token_type" => "Bearer", "expires_in" => 3600 }
139
+ body_fields["access_token"] = opts[:access_token] if opts[:access_token]
140
+ body_fields["id_token"] = opts[:id_token] if opts[:id_token]
141
+ body = MultiJson.dump body_fields
137
142
  blk = proc do |request|
138
143
  params = Addressable::URI.form_unencode request.body
139
- _claim, _header = JWT.decode(params.assoc("assertion").last,
140
- @key.public_key, true,
141
- algorithm: "RS256")
144
+ claim, _header = JWT.decode(params.assoc("assertion").last,
145
+ @key.public_key, true,
146
+ algorithm: "RS256")
147
+ !opts[:id_token] || claim["target_audience"] == "https://pubsub.googleapis.com/"
142
148
  end
143
149
  stub_request(:post, "https://www.googleapis.com/oauth2/v4/token")
144
150
  .with(body: hash_including(
145
151
  "grant_type" => "urn:ietf:params:oauth:grant-type:jwt-bearer"
146
- ),
147
- &blk)
152
+ ), &blk)
148
153
  .to_return(body: body,
149
154
  status: 200,
150
155
  headers: { "Content-Type" => "application/json" })
@@ -285,6 +290,7 @@ describe Google::Auth::ServiceAccountCredentials do
285
290
  ENV["APPDATA"] = dir
286
291
  credentials = @clz.from_well_known_path @scope
287
292
  expect(credentials.project_id).to eq(cred_json[:project_id])
293
+ expect(credentials.quota_project_id).to eq(cred_json[:quota_project_id])
288
294
  end
289
295
  end
290
296
 
@@ -476,6 +482,7 @@ describe Google::Auth::ServiceAccountJwtHeaderCredentials do
476
482
  ENV["APPDATA"] = dir
477
483
  credentials = clz.from_well_known_path @scope
478
484
  expect(credentials.project_id).to eq(cred_json[:project_id])
485
+ expect(credentials.quota_project_id).to be_nil
479
486
  end
480
487
  end
481
488
  end
@@ -47,18 +47,26 @@ describe Signet::OAuth2::Client do
47
47
  audience: "https://oauth2.googleapis.com/token",
48
48
  signing_key: @key
49
49
  )
50
+ @id_client = Signet::OAuth2::Client.new(
51
+ token_credential_uri: "https://oauth2.googleapis.com/token",
52
+ target_audience: "https://pubsub.googleapis.com/",
53
+ issuer: "app@example.com",
54
+ audience: "https://oauth2.googleapis.com/token",
55
+ signing_key: @key
56
+ )
50
57
  end
51
58
 
52
59
  def make_auth_stubs opts
53
- access_token = opts[:access_token] || ""
54
- body = MultiJson.dump("access_token" => access_token,
55
- "token_type" => "Bearer",
56
- "expires_in" => 3600)
60
+ body_fields = { "token_type" => "Bearer", "expires_in" => 3600 }
61
+ body_fields["access_token"] = opts[:access_token] if opts[:access_token]
62
+ body_fields["id_token"] = opts[:id_token] if opts[:id_token]
63
+ body = MultiJson.dump body_fields
57
64
  blk = proc do |request|
58
65
  params = Addressable::URI.form_unencode request.body
59
- _claim, _header = JWT.decode(params.assoc("assertion").last,
60
- @key.public_key, true,
61
- algorithm: "RS256")
66
+ claim, _header = JWT.decode(params.assoc("assertion").last,
67
+ @key.public_key, true,
68
+ algorithm: "RS256")
69
+ !opts[:id_token] || claim["target_audience"] == "https://pubsub.googleapis.com/"
62
70
  end
63
71
  with_params = { body: hash_including(
64
72
  "grant_type" => "urn:ietf:params:oauth:grant-type:jwt-bearer"