googleauth 0.12.0 → 0.13.0

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.
@@ -104,7 +104,7 @@ module Google
104
104
  # authenticate instead.
105
105
  def apply! a_hash, opts = {}
106
106
  # Use the base implementation if scopes are set
107
- unless scope.nil?
107
+ unless scope.nil? && target_audience.nil?
108
108
  super
109
109
  return
110
110
  end
@@ -31,6 +31,6 @@ module Google
31
31
  # Module Auth provides classes that provide Google-specific authorization
32
32
  # used to access Google APIs.
33
33
  module Auth
34
- VERSION = "0.12.0".freeze
34
+ VERSION = "0.13.0".freeze
35
35
  end
36
36
  end
@@ -90,6 +90,18 @@ shared_examples "apply/apply! are OK" do
90
90
  expect(md).to eq(want)
91
91
  expect(stub).to have_been_requested
92
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
93
105
  end
94
106
 
95
107
  describe "updater_proc" do
@@ -0,0 +1,33 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Redistribution and use in source and binary forms, with or without
4
+ # modification, are permitted provided that the following conditions are
5
+ # met:
6
+ #
7
+ # * Redistributions of source code must retain the above copyright
8
+ # notice, this list of conditions and the following disclaimer.
9
+ # * Redistributions in binary form must reproduce the above
10
+ # copyright notice, this list of conditions and the following disclaimer
11
+ # in the documentation and/or other materials provided with the
12
+ # distribution.
13
+ # * Neither the name of Google Inc. nor the names of its
14
+ # contributors may be used to endorse or promote products derived from
15
+ # this software without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+ require "minitest/autorun"
30
+ require "minitest/focus"
31
+ require "webmock/minitest"
32
+
33
+ require "googleauth"
@@ -0,0 +1,240 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Redistribution and use in source and binary forms, with or without
4
+ # modification, are permitted provided that the following conditions are
5
+ # met:
6
+ #
7
+ # * Redistributions of source code must retain the above copyright
8
+ # notice, this list of conditions and the following disclaimer.
9
+ # * Redistributions in binary form must reproduce the above
10
+ # copyright notice, this list of conditions and the following disclaimer
11
+ # in the documentation and/or other materials provided with the
12
+ # distribution.
13
+ # * Neither the name of Google Inc. nor the names of its
14
+ # contributors may be used to endorse or promote products derived from
15
+ # this software without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+ require "helper"
30
+
31
+ require "openssl"
32
+
33
+ describe Google::Auth::IDTokens do
34
+ describe "StaticKeySource" do
35
+ let(:key1) { Google::Auth::IDTokens::KeyInfo.new id: "1234", key: :key1, algorithm: "RS256" }
36
+ let(:key2) { Google::Auth::IDTokens::KeyInfo.new id: "5678", key: :key2, algorithm: "ES256" }
37
+ let(:keys) { [key1, key2] }
38
+ let(:source) { Google::Auth::IDTokens::StaticKeySource.new keys }
39
+
40
+ it "returns a static set of keys" do
41
+ assert_equal keys, source.current_keys
42
+ end
43
+
44
+ it "does not change on refresh" do
45
+ assert_equal keys, source.refresh_keys
46
+ end
47
+ end
48
+
49
+ describe "HttpKeySource" do
50
+ let(:certs_uri) { "https://example.com/my-certs" }
51
+ let(:certs_body) { "{}" }
52
+
53
+ it "raises an error when failing to parse json from the site" do
54
+ source = Google::Auth::IDTokens::HttpKeySource.new certs_uri
55
+ stub = stub_request(:get, certs_uri).to_return(body: "whoops")
56
+ error = assert_raises Google::Auth::IDTokens::KeySourceError do
57
+ source.refresh_keys
58
+ end
59
+ assert_equal "Unable to parse JSON", error.message
60
+ assert_requested stub
61
+ end
62
+
63
+ it "downloads data but gets no keys" do
64
+ source = Google::Auth::IDTokens::HttpKeySource.new certs_uri
65
+ stub = stub_request(:get, certs_uri).to_return(body: certs_body)
66
+ keys = source.refresh_keys
67
+ assert_empty keys
68
+ assert_requested stub
69
+ end
70
+ end
71
+
72
+ describe "X509CertHttpKeySource" do
73
+ let(:certs_uri) { "https://example.com/my-certs" }
74
+ let(:key1) { OpenSSL::PKey::RSA.new 2048 }
75
+ let(:key2) { OpenSSL::PKey::RSA.new 2048 }
76
+ let(:cert1) { generate_cert key1 }
77
+ let(:cert2) { generate_cert key2 }
78
+ let(:id1) { "1234" }
79
+ let(:id2) { "5678" }
80
+ let(:certs_body) { JSON.dump({ id1 => cert1.to_pem, id2 => cert2.to_pem }) }
81
+
82
+ after do
83
+ WebMock.reset!
84
+ end
85
+
86
+ def generate_cert key
87
+ cert = OpenSSL::X509::Certificate.new
88
+ cert.subject = cert.issuer = OpenSSL::X509::Name.parse "/C=BE/O=Test/OU=Test/CN=Test"
89
+ cert.not_before = Time.now
90
+ cert.not_after = Time.now + 365 * 24 * 60 * 60
91
+ cert.public_key = key.public_key
92
+ cert.serial = 0x0
93
+ cert.version = 2
94
+ cert.sign key, OpenSSL::Digest::SHA1.new
95
+ cert
96
+ end
97
+
98
+ it "raises an error when failing to reach the site" do
99
+ source = Google::Auth::IDTokens::X509CertHttpKeySource.new certs_uri
100
+ stub = stub_request(:get, certs_uri).to_return(body: "whoops", status: 404)
101
+ error = assert_raises Google::Auth::IDTokens::KeySourceError do
102
+ source.refresh_keys
103
+ end
104
+ assert_equal "Unable to retrieve data from #{certs_uri}", error.message
105
+ assert_requested stub
106
+ end
107
+
108
+ it "raises an error when failing to parse json from the site" do
109
+ source = Google::Auth::IDTokens::X509CertHttpKeySource.new certs_uri
110
+ stub = stub_request(:get, certs_uri).to_return(body: "whoops")
111
+ error = assert_raises Google::Auth::IDTokens::KeySourceError do
112
+ source.refresh_keys
113
+ end
114
+ assert_equal "Unable to parse JSON", error.message
115
+ assert_requested stub
116
+ end
117
+
118
+ it "raises an error when failing to parse x509 from the site" do
119
+ source = Google::Auth::IDTokens::X509CertHttpKeySource.new certs_uri
120
+ stub = stub_request(:get, certs_uri).to_return(body: '{"hi": "whoops"}')
121
+ error = assert_raises Google::Auth::IDTokens::KeySourceError do
122
+ source.refresh_keys
123
+ end
124
+ assert_equal "Unable to parse X509 certificates", error.message
125
+ assert_requested stub
126
+ end
127
+
128
+ it "gets the right certificates" do
129
+ source = Google::Auth::IDTokens::X509CertHttpKeySource.new certs_uri
130
+ stub = stub_request(:get, certs_uri).to_return(body: certs_body)
131
+ keys = source.refresh_keys
132
+ assert_equal id1, keys[0].id
133
+ assert_equal id2, keys[1].id
134
+ assert_equal key1.public_key.to_pem, keys[0].key.to_pem
135
+ assert_equal key2.public_key.to_pem, keys[1].key.to_pem
136
+ assert_equal "RS256", keys[0].algorithm
137
+ assert_equal "RS256", keys[1].algorithm
138
+ assert_requested stub
139
+ end
140
+ end
141
+
142
+ describe "JwkHttpKeySource" do
143
+ let(:jwk_uri) { "https://example.com/my-jwk" }
144
+ let(:id1) { "fb8ca5b7d8d9a5c6c6788071e866c6c40f3fc1f9" }
145
+ let(:id2) { "LYyP2g" }
146
+ let(:jwk1) {
147
+ {
148
+ alg: "RS256",
149
+ e: "AQAB",
150
+ kid: id1,
151
+ kty: "RSA",
152
+ n: "zK8PHf_6V3G5rU-viUOL1HvAYn7q--dxMoUkt7x1rSWX6fimla-lpoYAKhFTLU" \
153
+ "ELkRKy_6UDzfybz0P9eItqS2UxVWYpKYmKTQ08HgUBUde4GtO_B0SkSk8iLtGh" \
154
+ "653UBBjgXmfzdfQEz_DsaWn7BMtuAhY9hpMtJye8LQlwaS8ibQrsC0j0GZM5KX" \
155
+ "RITHwfx06_T1qqC_MOZRA6iJs-J2HNlgeyFuoQVBTY6pRqGXa-qaVsSG3iU-vq" \
156
+ "NIciFquIq-xydwxLqZNksRRer5VAsSHf0eD3g2DX-cf6paSy1aM40svO9EfSvG" \
157
+ "_07MuHafEE44RFvSZZ4ubEN9U7ALSjdw",
158
+ use: "sig"
159
+ }
160
+ }
161
+ let(:jwk2) {
162
+ {
163
+ alg: "ES256",
164
+ crv: "P-256",
165
+ kid: id2,
166
+ kty: "EC",
167
+ use: "sig",
168
+ x: "SlXFFkJ3JxMsXyXNrqzE3ozl_0913PmNbccLLWfeQFU",
169
+ y: "GLSahrZfBErmMUcHP0MGaeVnJdBwquhrhQ8eP05NfCI"
170
+ }
171
+ }
172
+ let(:bad_type_jwk) {
173
+ {
174
+ alg: "RS256",
175
+ kid: "hello",
176
+ kty: "blah",
177
+ use: "sig"
178
+ }
179
+ }
180
+ let(:jwk_body) { JSON.dump({ keys: [jwk1, jwk2] }) }
181
+ let(:bad_type_body) { JSON.dump({ keys: [bad_type_jwk] }) }
182
+
183
+ after do
184
+ WebMock.reset!
185
+ end
186
+
187
+ it "raises an error when failing to reach the site" do
188
+ source = Google::Auth::IDTokens::JwkHttpKeySource.new jwk_uri
189
+ stub = stub_request(:get, jwk_uri).to_return(body: "whoops", status: 404)
190
+ error = assert_raises Google::Auth::IDTokens::KeySourceError do
191
+ source.refresh_keys
192
+ end
193
+ assert_equal "Unable to retrieve data from #{jwk_uri}", error.message
194
+ assert_requested stub
195
+ end
196
+
197
+ it "raises an error when failing to parse json from the site" do
198
+ source = Google::Auth::IDTokens::JwkHttpKeySource.new jwk_uri
199
+ stub = stub_request(:get, jwk_uri).to_return(body: "whoops")
200
+ error = assert_raises Google::Auth::IDTokens::KeySourceError do
201
+ source.refresh_keys
202
+ end
203
+ assert_equal "Unable to parse JSON", error.message
204
+ assert_requested stub
205
+ end
206
+
207
+ it "raises an error when the json structure is malformed" do
208
+ source = Google::Auth::IDTokens::JwkHttpKeySource.new jwk_uri
209
+ stub = stub_request(:get, jwk_uri).to_return(body: '{"hi": "whoops"}')
210
+ error = assert_raises Google::Auth::IDTokens::KeySourceError do
211
+ source.refresh_keys
212
+ end
213
+ assert_equal "No keys found in jwk set", error.message
214
+ assert_requested stub
215
+ end
216
+
217
+ it "raises an error when an unrecognized key type is encountered" do
218
+ source = Google::Auth::IDTokens::JwkHttpKeySource.new jwk_uri
219
+ stub = stub_request(:get, jwk_uri).to_return(body: bad_type_body)
220
+ error = assert_raises Google::Auth::IDTokens::KeySourceError do
221
+ source.refresh_keys
222
+ end
223
+ assert_equal "Cannot use key type blah", error.message
224
+ assert_requested stub
225
+ end
226
+
227
+ it "gets the right keys" do
228
+ source = Google::Auth::IDTokens::JwkHttpKeySource.new jwk_uri
229
+ stub = stub_request(:get, jwk_uri).to_return(body: jwk_body)
230
+ keys = source.refresh_keys
231
+ assert_equal id1, keys[0].id
232
+ assert_equal id2, keys[1].id
233
+ assert_kind_of OpenSSL::PKey::RSA, keys[0].key
234
+ assert_kind_of OpenSSL::PKey::EC, keys[1].key
235
+ assert_equal "RS256", keys[0].algorithm
236
+ assert_equal "ES256", keys[1].algorithm
237
+ assert_requested stub
238
+ end
239
+ end
240
+ end
@@ -0,0 +1,269 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Redistribution and use in source and binary forms, with or without
4
+ # modification, are permitted provided that the following conditions are
5
+ # met:
6
+ #
7
+ # * Redistributions of source code must retain the above copyright
8
+ # notice, this list of conditions and the following disclaimer.
9
+ # * Redistributions in binary form must reproduce the above
10
+ # copyright notice, this list of conditions and the following disclaimer
11
+ # in the documentation and/or other materials provided with the
12
+ # distribution.
13
+ # * Neither the name of Google Inc. nor the names of its
14
+ # contributors may be used to endorse or promote products derived from
15
+ # this software without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+ require "helper"
30
+
31
+ describe Google::Auth::IDTokens::Verifier do
32
+ describe "verify_oidc" do
33
+ let(:oidc_token) {
34
+ "eyJhbGciOiJSUzI1NiIsImtpZCI6IjQ5MjcxMGE3ZmNkYjE1Mzk2MGNlMDFmNzYwNTIwY" \
35
+ "TMyYzg0NTVkZmYiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwOi8vZXhhbXBsZS5jb20" \
36
+ "iLCJhenAiOiI1NDIzMzkzNTc2MzgtY3IwZHNlcnIyZXZnN3N2MW1lZ2hxZXU3MDMyNzRm" \
37
+ "M2hAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJlbWFpbCI6IjU0MjMzOTM1N" \
38
+ "zYzOC1jcjBkc2VycjJldmc3c3YxbWVnaHFldTcwMzI3NGYzaEBkZXZlbG9wZXIuZ3Nlcn" \
39
+ "ZpY2VhY2NvdW50LmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJleHAiOjE1OTEzNDI" \
40
+ "3NzYsImlhdCI6MTU5MTMzOTE3NiwiaXNzIjoiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUu" \
41
+ "Y29tIiwic3ViIjoiMTA0MzQxNDczMTMxODI1OTU3NjAzIn0.GGDE_5HoLacyqdufdxnAC" \
42
+ "rXxYySKQYAzSQ5qfGjSUriuO3uLm2-rwSPFfLzzBeflEHdVX7XRFFszpxKajuZklF4dXd" \
43
+ "0evB1u5i3QeCJ8MSZKKx6qus_ETJv4rtuPNEuyhaRcShB7BwI8RY0IZ4_EDrhYqYInrO2" \
44
+ "wQyJGYvc41JcmoKzRoNnEVydN0Qppt9bqevq_lJg-9UjJkJ2QHjPfTgMjwhLIgNptKgtR" \
45
+ "qdoRpJmleFlbuUqyPPJfAzv3Tc6h3kw88tEcI8R3n04xmHOSMwERFFQYJdQDMd2F9SSDe" \
46
+ "rh40codO_GuPZ7bEUiKq9Lkx2LH5TuhythfsMzIwJpaEA"
47
+ }
48
+ let(:oidc_jwk_body) {
49
+ <<~JWK
50
+ {
51
+ "keys": [
52
+ {
53
+ "kid": "fb8ca5b7d8d9a5c6c6788071e866c6c40f3fc1f9",
54
+ "e": "AQAB",
55
+ "alg": "RS256",
56
+ "use": "sig",
57
+ "n": "zK8PHf_6V3G5rU-viUOL1HvAYn7q--dxMoUkt7x1rSWX6fimla-lpoYAKhFTLUELkRKy_6UDzfybz0P9eItqS2UxVWYpKYmKTQ08HgUBUde4GtO_B0SkSk8iLtGh653UBBjgXmfzdfQEz_DsaWn7BMtuAhY9hpMtJye8LQlwaS8ibQrsC0j0GZM5KXRITHwfx06_T1qqC_MOZRA6iJs-J2HNlgeyFuoQVBTY6pRqGXa-qaVsSG3iU-vqNIciFquIq-xydwxLqZNksRRer5VAsSHf0eD3g2DX-cf6paSy1aM40svO9EfSvG_07MuHafEE44RFvSZZ4ubEN9U7ALSjdw",
58
+ "kty": "RSA"
59
+ },
60
+ {
61
+ "kty": "RSA",
62
+ "kid": "492710a7fcdb153960ce01f760520a32c8455dff",
63
+ "e": "AQAB",
64
+ "alg": "RS256",
65
+ "use": "sig",
66
+ "n": "wl6TaY_3dsuLczYH_hioeQ5JjcLKLGYb--WImN9_IKMkOj49dgs25wkjsdI9XGJYhhPJLlvfjIfXH49ZGA_XKLx7fggNaBRZcj1y-I3_77tVa9N7An5JLq3HT9XVt0PNTq0mtX009z1Hva4IWZ5IhENx2rWlZOfFAXiMUqhnDc8VY3lG7vr8_VG3cw3XRKvlZQKbb6p2YIMFsUwaDGL2tVF4SkxpxIazUYfOY5lijyVugNTslOBhlEMq_43MZlkznSrbFx8ToQ2bQX4Shj-r9pLyofbo6A7K9mgWnQXGY5rQVLPYYRzUg0ThWDzwHdgxYC5MNxKyQH4RC2LPv3U0LQ"
67
+ }
68
+ ]
69
+ }
70
+ JWK
71
+ }
72
+ let(:expected_aud) { "http://example.com" }
73
+ let(:expected_azp) { "542339357638-cr0dserr2evg7sv1meghqeu703274f3h@developer.gserviceaccount.com" }
74
+ let(:unexpired_test_time) { Time.at 1591339181 }
75
+ let(:expired_test_time) { unexpired_test_time + 86400 }
76
+
77
+ after do
78
+ WebMock.reset!
79
+ Google::Auth::IDTokens.forget_sources!
80
+ end
81
+
82
+ it "verifies a good token with iss, aud, and azp checks" do
83
+ stub_request(:get, Google::Auth::IDTokens::OAUTH2_V3_CERTS_URL).to_return(body: oidc_jwk_body)
84
+ Time.stub :now, unexpired_test_time do
85
+ Google::Auth::IDTokens.verify_oidc oidc_token, aud: expected_aud, azp: expected_azp
86
+ end
87
+ end
88
+
89
+ it "fails to verify a bad token" do
90
+ stub_request(:get, Google::Auth::IDTokens::OAUTH2_V3_CERTS_URL).to_return(body: oidc_jwk_body)
91
+ Time.stub :now, unexpired_test_time do
92
+ assert_raises Google::Auth::IDTokens::SignatureError do
93
+ Google::Auth::IDTokens.verify_oidc "#{oidc_token}x"
94
+ end
95
+ end
96
+ end
97
+
98
+ it "fails to verify a token with the wrong aud" do
99
+ stub_request(:get, Google::Auth::IDTokens::OAUTH2_V3_CERTS_URL).to_return(body: oidc_jwk_body)
100
+ Time.stub :now, unexpired_test_time do
101
+ assert_raises Google::Auth::IDTokens::AudienceMismatchError do
102
+ Google::Auth::IDTokens.verify_oidc oidc_token, aud: ["hello", "world"]
103
+ end
104
+ end
105
+ end
106
+
107
+ it "fails to verify a token with the wrong azp" do
108
+ stub_request(:get, Google::Auth::IDTokens::OAUTH2_V3_CERTS_URL).to_return(body: oidc_jwk_body)
109
+ Time.stub :now, unexpired_test_time do
110
+ assert_raises Google::Auth::IDTokens::AuthorizedPartyMismatchError do
111
+ Google::Auth::IDTokens.verify_oidc oidc_token, azp: "hello"
112
+ end
113
+ end
114
+ end
115
+
116
+ it "fails to verify a token with the wrong issuer" do
117
+ stub_request(:get, Google::Auth::IDTokens::OAUTH2_V3_CERTS_URL).to_return(body: oidc_jwk_body)
118
+ Time.stub :now, unexpired_test_time do
119
+ assert_raises Google::Auth::IDTokens::IssuerMismatchError do
120
+ Google::Auth::IDTokens.verify_oidc oidc_token, iss: "hello"
121
+ end
122
+ end
123
+ end
124
+
125
+ it "fails to verify an expired token" do
126
+ stub_request(:get, Google::Auth::IDTokens::OAUTH2_V3_CERTS_URL).to_return(body: oidc_jwk_body)
127
+ Time.stub :now, expired_test_time do
128
+ assert_raises Google::Auth::IDTokens::ExpiredTokenError do
129
+ Google::Auth::IDTokens.verify_oidc oidc_token
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ describe "verify_iap" do
136
+ let(:iap_token) {
137
+ "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjBvZUxjUSJ9.eyJhdWQiOiIvcH" \
138
+ "JvamVjdHMvNjUyNTYyNzc2Nzk4L2FwcHMvY2xvdWQtc2FtcGxlcy10ZXN0cy1waHAtaWFwI" \
139
+ "iwiZW1haWwiOiJkYXp1bWFAZ29vZ2xlLmNvbSIsImV4cCI6MTU5MTMzNTcyNCwiZ29vZ2xl" \
140
+ "Ijp7ImFjY2Vzc19sZXZlbHMiOlsiYWNjZXNzUG9saWNpZXMvNTE4NTUxMjgwOTI0L2FjY2V" \
141
+ "zc0xldmVscy9yZWNlbnRTZWN1cmVDb25uZWN0RGF0YSIsImFjY2Vzc1BvbGljaWVzLzUxOD" \
142
+ "U1MTI4MDkyNC9hY2Nlc3NMZXZlbHMvdGVzdE5vT3AiLCJhY2Nlc3NQb2xpY2llcy81MTg1N" \
143
+ "TEyODA5MjQvYWNjZXNzTGV2ZWxzL2V2YXBvcmF0aW9uUWFEYXRhRnVsbHlUcnVzdGVkIiwi" \
144
+ "YWNjZXNzUG9saWNpZXMvNTE4NTUxMjgwOTI0L2FjY2Vzc0xldmVscy9jYWFfZGlzYWJsZWQ" \
145
+ "iLCJhY2Nlc3NQb2xpY2llcy81MTg1NTEyODA5MjQvYWNjZXNzTGV2ZWxzL3JlY2VudE5vbk" \
146
+ "1vYmlsZVNlY3VyZUNvbm5lY3REYXRhIiwiYWNjZXNzUG9saWNpZXMvNTE4NTUxMjgwOTI0L" \
147
+ "2FjY2Vzc0xldmVscy9jb25jb3JkIiwiYWNjZXNzUG9saWNpZXMvNTE4NTUxMjgwOTI0L2Fj" \
148
+ "Y2Vzc0xldmVscy9mdWxseVRydXN0ZWRfY2FuYXJ5RGF0YSIsImFjY2Vzc1BvbGljaWVzLzU" \
149
+ "xODU1MTI4MDkyNC9hY2Nlc3NMZXZlbHMvZnVsbHlUcnVzdGVkX3Byb2REYXRhIl19LCJoZC" \
150
+ "I6Imdvb2dsZS5jb20iLCJpYXQiOjE1OTEzMzUxMjQsImlzcyI6Imh0dHBzOi8vY2xvdWQuZ" \
151
+ "29vZ2xlLmNvbS9pYXAiLCJzdWIiOiJhY2NvdW50cy5nb29nbGUuY29tOjExMzc3OTI1ODA4" \
152
+ "MTE5ODAwNDY5NCJ9.2BlagZOoonmX35rNY-KPbONiVzFAdNXKRGkX45uGFXeHryjKgv--K6" \
153
+ "siL8syeCFXzHvgmWpJk31sEt4YLxPKvQ"
154
+ }
155
+ let(:iap_jwk_body) {
156
+ <<~JWK
157
+ {
158
+ "keys" : [
159
+ {
160
+ "alg" : "ES256",
161
+ "crv" : "P-256",
162
+ "kid" : "LYyP2g",
163
+ "kty" : "EC",
164
+ "use" : "sig",
165
+ "x" : "SlXFFkJ3JxMsXyXNrqzE3ozl_0913PmNbccLLWfeQFU",
166
+ "y" : "GLSahrZfBErmMUcHP0MGaeVnJdBwquhrhQ8eP05NfCI"
167
+ },
168
+ {
169
+ "alg" : "ES256",
170
+ "crv" : "P-256",
171
+ "kid" : "mpf0DA",
172
+ "kty" : "EC",
173
+ "use" : "sig",
174
+ "x" : "fHEdeT3a6KaC1kbwov73ZwB_SiUHEyKQwUUtMCEn0aI",
175
+ "y" : "QWOjwPhInNuPlqjxLQyhveXpWqOFcQPhZ3t-koMNbZI"
176
+ },
177
+ {
178
+ "alg" : "ES256",
179
+ "crv" : "P-256",
180
+ "kid" : "b9vTLA",
181
+ "kty" : "EC",
182
+ "use" : "sig",
183
+ "x" : "qCByTAvci-jRAD7uQSEhTdOs8iA714IbcY2L--YzynI",
184
+ "y" : "WQY0uCoQyPSozWKGQ0anmFeOH5JNXiZa9i6SNqOcm7w"
185
+ },
186
+ {
187
+ "alg" : "ES256",
188
+ "crv" : "P-256",
189
+ "kid" : "0oeLcQ",
190
+ "kty" : "EC",
191
+ "use" : "sig",
192
+ "x" : "MdhRXGEoGJLtBjQEIjnYLPkeci9rXnca2TffkI0Kac0",
193
+ "y" : "9BoREHfX7g5OK8ELpA_4RcOnFCGSjfR4SGZpBo7juEY"
194
+ },
195
+ {
196
+ "alg" : "ES256",
197
+ "crv" : "P-256",
198
+ "kid" : "g5X6ig",
199
+ "kty" : "EC",
200
+ "use" : "sig",
201
+ "x" : "115LSuaFVzVROJiGfdPN1kT14Hv3P4RIjthfslZ010s",
202
+ "y" : "-FAaRtO4yvrN4uJ89xwGWOEJcSwpLmFOtb0SDJxEAuc"
203
+ }
204
+ ]
205
+ }
206
+ JWK
207
+ }
208
+ let(:expected_aud) { "/projects/652562776798/apps/cloud-samples-tests-php-iap" }
209
+ let(:unexpired_test_time) { Time.at 1591335143 }
210
+ let(:expired_test_time) { unexpired_test_time + 86400 }
211
+
212
+ after do
213
+ WebMock.reset!
214
+ Google::Auth::IDTokens.forget_sources!
215
+ end
216
+
217
+ it "verifies a good token with iss and aud checks" do
218
+ stub_request(:get, Google::Auth::IDTokens::IAP_JWK_URL).to_return(body: iap_jwk_body)
219
+ Time.stub :now, unexpired_test_time do
220
+ Google::Auth::IDTokens.verify_iap iap_token, aud: expected_aud
221
+ end
222
+ end
223
+
224
+ it "fails to verify a bad token" do
225
+ stub_request(:get, Google::Auth::IDTokens::IAP_JWK_URL).to_return(body: iap_jwk_body)
226
+ Time.stub :now, unexpired_test_time do
227
+ assert_raises Google::Auth::IDTokens::SignatureError do
228
+ Google::Auth::IDTokens.verify_iap "#{iap_token}x"
229
+ end
230
+ end
231
+ end
232
+
233
+ it "fails to verify a token with the wrong aud" do
234
+ stub_request(:get, Google::Auth::IDTokens::IAP_JWK_URL).to_return(body: iap_jwk_body)
235
+ Time.stub :now, unexpired_test_time do
236
+ assert_raises Google::Auth::IDTokens::AudienceMismatchError do
237
+ Google::Auth::IDTokens.verify_iap iap_token, aud: ["hello", "world"]
238
+ end
239
+ end
240
+ end
241
+
242
+ it "fails to verify a token with the wrong azp" do
243
+ stub_request(:get, Google::Auth::IDTokens::IAP_JWK_URL).to_return(body: iap_jwk_body)
244
+ Time.stub :now, unexpired_test_time do
245
+ assert_raises Google::Auth::IDTokens::AuthorizedPartyMismatchError do
246
+ Google::Auth::IDTokens.verify_iap iap_token, azp: "hello"
247
+ end
248
+ end
249
+ end
250
+
251
+ it "fails to verify a token with the wrong issuer" do
252
+ stub_request(:get, Google::Auth::IDTokens::IAP_JWK_URL).to_return(body: iap_jwk_body)
253
+ Time.stub :now, unexpired_test_time do
254
+ assert_raises Google::Auth::IDTokens::IssuerMismatchError do
255
+ Google::Auth::IDTokens.verify_iap iap_token, iss: "hello"
256
+ end
257
+ end
258
+ end
259
+
260
+ it "fails to verify an expired token" do
261
+ stub_request(:get, Google::Auth::IDTokens::IAP_JWK_URL).to_return(body: iap_jwk_body)
262
+ Time.stub :now, expired_test_time do
263
+ assert_raises Google::Auth::IDTokens::ExpiredTokenError do
264
+ Google::Auth::IDTokens.verify_iap iap_token
265
+ end
266
+ end
267
+ end
268
+ end
269
+ end