1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
/* |
6
|
|
|
* The MIT License (MIT) |
7
|
|
|
* |
8
|
|
|
* Copyright (c) 2014-2018 Spomky-Labs |
9
|
|
|
* |
10
|
|
|
* This software may be modified and distributed under the terms |
11
|
|
|
* of the MIT license. See the LICENSE file for details. |
12
|
|
|
*/ |
13
|
|
|
|
14
|
|
|
namespace OAuth2Framework\Component\AuthorizationEndpoint\Tests\AuthorizationRequest; |
15
|
|
|
|
16
|
|
|
use Http\Mock\Client; |
17
|
|
|
use Jose\Component\Checker\ClaimCheckerManager; |
18
|
|
|
use Jose\Component\Core\AlgorithmManager; |
19
|
|
|
use Jose\Component\Core\JWKSet; |
20
|
|
|
use Jose\Component\Signature\JWS; |
21
|
|
|
use Jose\Component\Signature\JWSVerifier; |
22
|
|
|
use OAuth2Framework\Component\AuthorizationEndpoint\AuthorizationRequest\AuthorizationRequestLoader; |
23
|
|
|
use OAuth2Framework\Component\Core\Client\ClientRepository; |
24
|
|
|
use OAuth2Framework\Component\Core\Message\OAuth2Error; |
25
|
|
|
use PHPUnit\Framework\TestCase; |
26
|
|
|
use Prophecy\Argument; |
27
|
|
|
use Psr\Http\Message\RequestInterface; |
28
|
|
|
use Psr\Http\Message\ServerRequestInterface; |
29
|
|
|
use Zend\Diactoros\Response; |
30
|
|
|
use Zend\Diactoros\Uri; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @group AuthorizationEndpoint |
34
|
|
|
* @group AuthorizationRequest |
35
|
|
|
*/ |
36
|
|
|
final class AuthorizationRequestLoaderTest extends TestCase |
37
|
|
|
{ |
38
|
|
|
/** |
39
|
|
|
* @test |
40
|
|
|
*/ |
41
|
|
|
public function basicCalls() |
42
|
|
|
{ |
43
|
|
|
$clientRepository = $this->prophesize(ClientRepository::class); |
44
|
|
|
$loader = $this->getAuthorizationRequestLoader( |
45
|
|
|
$clientRepository->reveal() |
46
|
|
|
); |
47
|
|
|
static::assertEquals([], $loader->getSupportedKeyEncryptionAlgorithms()); |
48
|
|
|
static::assertEquals([], $loader->getSupportedContentEncryptionAlgorithms()); |
49
|
|
|
static::assertEquals(['RS256'], $loader->getSupportedSignatureAlgorithms()); |
50
|
|
|
static::assertTrue($loader->isRequestObjectSupportEnabled()); |
51
|
|
|
static::assertTrue($loader->isRequestUriRegistrationRequired()); |
52
|
|
|
static::assertTrue($loader->isRequestObjectReferenceSupportEnabled()); |
53
|
|
|
static::assertFalse($loader->isEncryptedRequestSupportEnabled()); |
54
|
|
|
|
55
|
|
|
//$this->authorizationRequestLoader->enableEncryptedRequestObjectSupport($jweLoader, $keyset, false); |
|
|
|
|
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @test |
60
|
|
|
*/ |
61
|
|
|
public function theAuthorizationRequestMustContainAClientId() |
62
|
|
|
{ |
63
|
|
|
$clientRepository = $this->prophesize(ClientRepository::class); |
64
|
|
|
$request = $this->prophesize(ServerRequestInterface::class); |
65
|
|
|
$request->getQueryParams() |
66
|
|
|
->willReturn([]) |
67
|
|
|
->shouldBeCalled(); |
68
|
|
|
|
69
|
|
|
try { |
70
|
|
|
$this->getAuthorizationRequestLoader($clientRepository->reveal())->load( |
71
|
|
|
$request->reveal() |
72
|
|
|
); |
73
|
|
|
static::fail('The expected exception has not been thrown.'); |
74
|
|
|
} catch (OAuth2Error $e) { |
75
|
|
|
static::assertEquals(400, $e->getCode()); |
76
|
|
|
static::assertEquals('invalid_request', $e->getMessage()); |
77
|
|
|
static::assertEquals('Parameter "client_id" missing or invalid.', $e->getErrorDescription()); |
78
|
|
|
} |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* @test |
83
|
|
|
*/ |
84
|
|
|
public function theClientDoesNotExist() |
85
|
|
|
{ |
86
|
|
|
$clientRepository = $this->prophesize(ClientRepository::class); |
87
|
|
|
$clientRepository->find(Argument::any())->willReturn(null)->shouldBeCalled(); |
88
|
|
|
$request = $this->prophesize(ServerRequestInterface::class); |
89
|
|
|
$request->getQueryParams() |
90
|
|
|
->willReturn([ |
91
|
|
|
'client_id' => 'BAD_CLIENT_ID', |
92
|
|
|
]) |
93
|
|
|
->shouldBeCalled(); |
94
|
|
|
|
95
|
|
|
try { |
96
|
|
|
$this->getAuthorizationRequestLoader($clientRepository->reveal())->load( |
97
|
|
|
$request->reveal() |
98
|
|
|
); |
99
|
|
|
static::fail('The expected exception has not been thrown.'); |
100
|
|
|
} catch (OAuth2Error $e) { |
101
|
|
|
static::assertEquals(400, $e->getCode()); |
102
|
|
|
static::assertEquals('invalid_request', $e->getMessage()); |
103
|
|
|
static::assertEquals('Parameter "client_id" missing or invalid.', $e->getErrorDescription()); |
104
|
|
|
} |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* @test |
109
|
|
|
*/ |
110
|
|
|
public function theClientIsDeleted() |
111
|
|
|
{ |
112
|
|
|
$client = $this->prophesize(\OAuth2Framework\Component\Core\Client\Client::class); |
113
|
|
|
$client->isDeleted()->willReturn(true)->shouldBeCalled(); |
114
|
|
|
|
115
|
|
|
$clientRepository = $this->prophesize(ClientRepository::class); |
116
|
|
|
$clientRepository->find(Argument::any())->willReturn($client->reveal())->shouldBeCalled(); |
117
|
|
|
$request = $this->prophesize(ServerRequestInterface::class); |
118
|
|
|
$request->getQueryParams() |
119
|
|
|
->willReturn([ |
120
|
|
|
'client_id' => 'DELETED_CLIENT_ID', |
121
|
|
|
]) |
122
|
|
|
->shouldBeCalled(); |
123
|
|
|
|
124
|
|
|
try { |
125
|
|
|
$this->getAuthorizationRequestLoader($clientRepository->reveal())->load( |
126
|
|
|
$request->reveal() |
127
|
|
|
); |
128
|
|
|
static::fail('The expected exception has not been thrown.'); |
129
|
|
|
} catch (OAuth2Error $e) { |
130
|
|
|
static::assertEquals(400, $e->getCode()); |
131
|
|
|
static::assertEquals('invalid_request', $e->getMessage()); |
132
|
|
|
static::assertEquals('Parameter "client_id" missing or invalid.', $e->getErrorDescription()); |
133
|
|
|
} |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* @test |
138
|
|
|
*/ |
139
|
|
|
public function theClientExistsAndTheParametersAreReturned() |
140
|
|
|
{ |
141
|
|
|
$client = $this->prophesize(\OAuth2Framework\Component\Core\Client\Client::class); |
142
|
|
|
$client->isDeleted()->willReturn(false)->shouldBeCalled(); |
143
|
|
|
|
144
|
|
|
$clientRepository = $this->prophesize(ClientRepository::class); |
145
|
|
|
$clientRepository->find(Argument::any())->willReturn($client->reveal())->shouldBeCalled(); |
146
|
|
|
$request = $this->prophesize(ServerRequestInterface::class); |
147
|
|
|
$request->getQueryParams() |
148
|
|
|
->willReturn([ |
149
|
|
|
'client_id' => 'CLIENT_ID', |
150
|
|
|
]) |
151
|
|
|
->shouldBeCalled(); |
152
|
|
|
|
153
|
|
|
$authorization = $this->getAuthorizationRequestLoader($clientRepository->reveal())->load( |
154
|
|
|
$request->reveal() |
155
|
|
|
); |
156
|
|
|
|
157
|
|
|
static::assertTrue($authorization->hasQueryParam('client_id')); |
158
|
|
|
static::assertEquals('CLIENT_ID', $authorization->getQueryParam('client_id')); |
159
|
|
|
static::assertInstanceOf(\OAuth2Framework\Component\Core\Client\Client::class, $authorization->getClient()); |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* @test |
164
|
|
|
*/ |
165
|
|
|
public function theRequestObjectIsNotAValidAssertion() |
166
|
|
|
{ |
167
|
|
|
$clientRepository = $this->prophesize(ClientRepository::class); |
168
|
|
|
$request = $this->prophesize(ServerRequestInterface::class); |
169
|
|
|
$request->getQueryParams() |
170
|
|
|
->willReturn([ |
171
|
|
|
'request' => 'INVALID_ASSERTION', |
172
|
|
|
]) |
173
|
|
|
->shouldBeCalled(); |
174
|
|
|
|
175
|
|
|
try { |
176
|
|
|
$this->getAuthorizationRequestLoader($clientRepository->reveal())->load( |
177
|
|
|
$request->reveal() |
178
|
|
|
); |
179
|
|
|
static::fail('The expected exception has not been thrown.'); |
180
|
|
|
} catch (OAuth2Error $e) { |
181
|
|
|
static::assertEquals(400, $e->getCode()); |
182
|
|
|
static::assertEquals('invalid_request_object', $e->getMessage()); |
183
|
|
|
static::assertEquals('Unsupported input', $e->getErrorDescription()); |
184
|
|
|
} |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
/** |
188
|
|
|
* @test |
189
|
|
|
*/ |
190
|
|
|
public function theRequestObjectPayloadDoesNotContainClaims() |
191
|
|
|
{ |
192
|
|
|
$clientRepository = $this->prophesize(ClientRepository::class); |
193
|
|
|
$request = $this->prophesize(ServerRequestInterface::class); |
194
|
|
|
$request->getQueryParams() |
195
|
|
|
->willReturn([ |
196
|
|
|
'request' => 'eyJhbGciOiJub25lIn0.SEVMTE8gV09STEQh.', |
197
|
|
|
]) |
198
|
|
|
->shouldBeCalled(); |
199
|
|
|
|
200
|
|
|
try { |
201
|
|
|
$this->getAuthorizationRequestLoader($clientRepository->reveal())->load( |
202
|
|
|
$request->reveal() |
203
|
|
|
); |
204
|
|
|
static::fail('The expected exception has not been thrown.'); |
205
|
|
|
} catch (OAuth2Error $e) { |
206
|
|
|
static::assertEquals(400, $e->getCode()); |
207
|
|
|
static::assertEquals('invalid_request_object', $e->getMessage()); |
208
|
|
|
static::assertEquals('Invalid assertion. The payload must contain claims.', $e->getErrorDescription()); |
209
|
|
|
} |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* @test |
214
|
|
|
*/ |
215
|
|
|
public function theRequestObjectParametersDoesNotContainAClientId() |
216
|
|
|
{ |
217
|
|
|
$clientRepository = $this->prophesize(ClientRepository::class); |
218
|
|
|
$request = $this->prophesize(ServerRequestInterface::class); |
219
|
|
|
$request->getQueryParams() |
220
|
|
|
->willReturn([ |
221
|
|
|
'request' => 'eyJhbGciOiJub25lIn0.e30.', |
222
|
|
|
]) |
223
|
|
|
->shouldBeCalled(); |
224
|
|
|
|
225
|
|
|
try { |
226
|
|
|
$this->getAuthorizationRequestLoader($clientRepository->reveal())->load( |
227
|
|
|
$request->reveal() |
228
|
|
|
); |
229
|
|
|
static::fail('The expected exception has not been thrown.'); |
230
|
|
|
} catch (OAuth2Error $e) { |
231
|
|
|
static::assertEquals(400, $e->getCode()); |
232
|
|
|
static::assertEquals('invalid_request', $e->getMessage()); |
233
|
|
|
static::assertEquals('Parameter "client_id" missing or invalid.', $e->getErrorDescription()); |
234
|
|
|
} |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
/** |
238
|
|
|
* @test |
239
|
|
|
*/ |
240
|
|
|
public function theClientHasNoPublicKeysAndCannotUseTheRequestObjectParameters() |
241
|
|
|
{ |
242
|
|
|
$client = $this->prophesize(\OAuth2Framework\Component\Core\Client\Client::class); |
243
|
|
|
$client->isDeleted()->willReturn(false)->shouldBeCalled(); |
244
|
|
|
$client->has('jwks')->willReturn(false)->shouldBeCalled(); |
245
|
|
|
$client->has('jwks_uri')->willReturn(false)->shouldBeCalled(); |
246
|
|
|
$client->has('client_secret')->willReturn(false)->shouldBeCalled(); |
247
|
|
|
$client->has('request_object_signing_alg')->willReturn(false)->shouldNotBeCalled(); |
248
|
|
|
|
249
|
|
|
$clientRepository = $this->prophesize(ClientRepository::class); |
250
|
|
|
$clientRepository->find(Argument::any())->willReturn($client->reveal())->shouldBeCalled(); |
251
|
|
|
$request = $this->prophesize(ServerRequestInterface::class); |
252
|
|
|
$request->getQueryParams() |
253
|
|
|
->willReturn([ |
254
|
|
|
'request' => 'eyJhbGciOiJub25lIn0.eyJjbGllbnRfaWQiOiJOT19LRVlfQ0xJRU5UX0lEIn0.', |
255
|
|
|
]) |
256
|
|
|
->shouldBeCalled(); |
257
|
|
|
|
258
|
|
|
try { |
259
|
|
|
$this->getAuthorizationRequestLoader($clientRepository->reveal())->load( |
260
|
|
|
$request->reveal() |
261
|
|
|
); |
262
|
|
|
static::fail('The expected exception has not been thrown.'); |
263
|
|
|
} catch (OAuth2Error $e) { |
264
|
|
|
static::assertEquals(400, $e->getCode()); |
265
|
|
|
static::assertEquals('invalid_request_object', $e->getMessage()); |
266
|
|
|
static::assertEquals('The client has no key or key set.', $e->getErrorDescription()); |
267
|
|
|
} |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
/** |
271
|
|
|
* @test |
272
|
|
|
*/ |
273
|
|
|
public function theClientDidNotReferencedAnySignatureAlgorithmAndTheUsedAlgorithmIsNotSupported() |
274
|
|
|
{ |
275
|
|
|
$client = $this->prophesize(\OAuth2Framework\Component\Core\Client\Client::class); |
276
|
|
|
$client->isDeleted()->willReturn(false)->shouldBeCalled(); |
277
|
|
|
$client->has('jwks')->willReturn(false)->shouldBeCalled(); |
278
|
|
|
$client->has('jwks_uri')->willReturn(false)->shouldBeCalled(); |
279
|
|
|
$client->has('client_secret')->willReturn(true)->shouldBeCalled(); |
280
|
|
|
$client->get('client_secret')->willReturn('SECRET')->shouldBeCalled(); |
281
|
|
|
$client->has('request_object_signing_alg')->willReturn(false); |
282
|
|
|
$client->getTokenEndpointAuthenticationMethod()->willReturn('client_secret_jwt'); |
283
|
|
|
|
284
|
|
|
$clientRepository = $this->prophesize(ClientRepository::class); |
285
|
|
|
$clientRepository->find(Argument::any())->willReturn($client->reveal())->shouldBeCalled(); |
286
|
|
|
$request = $this->prophesize(ServerRequestInterface::class); |
287
|
|
|
$request->getQueryParams() |
288
|
|
|
->willReturn([ |
289
|
|
|
'request' => 'eyJhbGciOiJub25lIn0.eyJjbGllbnRfaWQiOiJDTElFTlRfSUQifQ.', |
290
|
|
|
]) |
291
|
|
|
->shouldBeCalled(); |
292
|
|
|
|
293
|
|
|
try { |
294
|
|
|
$this->getAuthorizationRequestLoader($clientRepository->reveal())->load( |
295
|
|
|
$request->reveal() |
296
|
|
|
); |
297
|
|
|
static::fail('The expected exception has not been thrown.'); |
298
|
|
|
} catch (OAuth2Error $e) { |
299
|
|
|
static::assertEquals(400, $e->getCode()); |
300
|
|
|
static::assertEquals('invalid_request_object', $e->getMessage()); |
301
|
|
|
static::assertEquals('The algorithm "none" is not allowed for request object signatures. Please use one of the following algorithm(s): RS256', $e->getErrorDescription()); |
302
|
|
|
} |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
/** |
306
|
|
|
* @test |
307
|
|
|
*/ |
308
|
|
|
public function theSignatureAlgorithmIsNotAllowedForThatClient() |
309
|
|
|
{ |
310
|
|
|
$client = $this->prophesize(\OAuth2Framework\Component\Core\Client\Client::class); |
311
|
|
|
$client->isDeleted()->willReturn(false)->shouldBeCalled(); |
312
|
|
|
$client->has('jwks')->willReturn(false)->shouldBeCalled(); |
313
|
|
|
$client->has('jwks_uri')->willReturn(false)->shouldBeCalled(); |
314
|
|
|
$client->has('client_secret')->willReturn(true)->shouldBeCalled(); |
315
|
|
|
$client->get('client_secret')->willReturn('SECRET')->shouldBeCalled(); |
316
|
|
|
$client->has('request_object_signing_alg')->willReturn(true)->shouldBeCalled(); |
317
|
|
|
$client->get('request_object_signing_alg')->willReturn('RS256')->shouldBeCalled(); |
318
|
|
|
$client->getTokenEndpointAuthenticationMethod()->willReturn('client_secret_jwt'); |
319
|
|
|
|
320
|
|
|
$clientRepository = $this->prophesize(ClientRepository::class); |
321
|
|
|
$clientRepository->find(Argument::any())->willReturn($client->reveal())->shouldBeCalled(); |
322
|
|
|
$request = $this->prophesize(ServerRequestInterface::class); |
323
|
|
|
$request->getQueryParams() |
324
|
|
|
->willReturn([ |
325
|
|
|
'request' => 'eyJhbGciOiJub25lIn0.eyJjbGllbnRfaWQiOiJDTElFTlRfSUQifQ.', |
326
|
|
|
]) |
327
|
|
|
->shouldBeCalled(); |
328
|
|
|
|
329
|
|
|
try { |
330
|
|
|
$this->getAuthorizationRequestLoader($clientRepository->reveal())->load( |
331
|
|
|
$request->reveal() |
332
|
|
|
); |
333
|
|
|
static::fail('The expected exception has not been thrown.'); |
334
|
|
|
} catch (OAuth2Error $e) { |
335
|
|
|
static::assertEquals(400, $e->getCode()); |
336
|
|
|
static::assertEquals('invalid_request_object', $e->getMessage()); |
337
|
|
|
static::assertEquals('Request Object signature algorithm not allowed for the client.', $e->getErrorDescription()); |
338
|
|
|
} |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
/** |
342
|
|
|
* @test |
343
|
|
|
*/ |
344
|
|
|
public function theSignatureVerificationFailed() |
345
|
|
|
{ |
346
|
|
|
$client = $this->prophesize(\OAuth2Framework\Component\Core\Client\Client::class); |
347
|
|
|
$client->isDeleted()->willReturn(false)->shouldBeCalled(); |
348
|
|
|
$client->has('jwks')->willReturn(false)->shouldBeCalled(); |
349
|
|
|
$client->has('jwks_uri')->willReturn(false)->shouldBeCalled(); |
350
|
|
|
$client->has('client_secret')->willReturn(true)->shouldBeCalled(); |
351
|
|
|
$client->get('client_secret')->willReturn('SECRET')->shouldBeCalled(); |
352
|
|
|
$client->has('request_object_signing_alg')->willReturn(true)->shouldBeCalled(); |
353
|
|
|
$client->get('request_object_signing_alg')->willReturn('RS256')->shouldBeCalled(); |
354
|
|
|
$client->getTokenEndpointAuthenticationMethod()->willReturn('client_secret_jwt'); |
355
|
|
|
|
356
|
|
|
$clientRepository = $this->prophesize(ClientRepository::class); |
357
|
|
|
$clientRepository->find(Argument::any())->willReturn($client->reveal())->shouldBeCalled(); |
358
|
|
|
$request = $this->prophesize(ServerRequestInterface::class); |
359
|
|
|
$request->getQueryParams() |
360
|
|
|
->willReturn([ |
361
|
|
|
'request' => 'eyJhbGciOiJSUzI1NiJ9.eyJjbGllbnRfaWQiOiJDTElFTlRfSUQifQ.', |
362
|
|
|
]) |
363
|
|
|
->shouldBeCalled(); |
364
|
|
|
|
365
|
|
|
try { |
366
|
|
|
$this->getAuthorizationRequestLoader($clientRepository->reveal())->load( |
367
|
|
|
$request->reveal() |
368
|
|
|
); |
369
|
|
|
static::fail('The expected exception has not been thrown.'); |
370
|
|
|
} catch (OAuth2Error $e) { |
371
|
|
|
static::assertEquals(400, $e->getCode()); |
372
|
|
|
static::assertEquals('invalid_request_object', $e->getMessage()); |
373
|
|
|
static::assertEquals('The verification of the request object failed.', $e->getErrorDescription()); |
374
|
|
|
} |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
/** |
378
|
|
|
* @test |
379
|
|
|
*/ |
380
|
|
|
public function theAssertionIsVerified() |
381
|
|
|
{ |
382
|
|
|
$client = $this->prophesize(\OAuth2Framework\Component\Core\Client\Client::class); |
383
|
|
|
$client->isDeleted()->willReturn(false)->shouldBeCalled(); |
384
|
|
|
$client->has('jwks')->willReturn(false)->shouldBeCalled(); |
385
|
|
|
$client->has('jwks_uri')->willReturn(false)->shouldBeCalled(); |
386
|
|
|
$client->has('client_secret')->willReturn(true)->shouldBeCalled(); |
387
|
|
|
$client->get('client_secret')->willReturn('SECRET')->shouldBeCalled(); |
388
|
|
|
$client->has('request_object_signing_alg')->willReturn(true)->shouldBeCalled(); |
389
|
|
|
$client->get('request_object_signing_alg')->willReturn('RS256')->shouldBeCalled(); |
390
|
|
|
$client->getTokenEndpointAuthenticationMethod()->willReturn('client_secret_jwt'); |
391
|
|
|
|
392
|
|
|
$clientRepository = $this->prophesize(ClientRepository::class); |
393
|
|
|
$clientRepository->find(Argument::any())->willReturn($client->reveal())->shouldBeCalled(); |
394
|
|
|
$request = $this->prophesize(ServerRequestInterface::class); |
395
|
|
|
$request->getQueryParams() |
396
|
|
|
->willReturn([ |
397
|
|
|
'request' => 'eyJhbGciOiJSUzI1NiJ9.eyJjbGllbnRfaWQiOiJDTElFTlRfSUQifQ.R09PRF9TSUdOQVRVUkU', |
398
|
|
|
]) |
399
|
|
|
->shouldBeCalled(); |
400
|
|
|
|
401
|
|
|
$authorization = $this->getAuthorizationRequestLoader($clientRepository->reveal())->load( |
402
|
|
|
$request->reveal() |
403
|
|
|
); |
404
|
|
|
|
405
|
|
|
static::assertTrue($authorization->hasQueryParam('client_id')); |
406
|
|
|
static::assertTrue($authorization->hasQueryParam('request')); |
407
|
|
|
static::assertEquals('CLIENT_ID', $authorization->getQueryParam('client_id')); |
408
|
|
|
static::assertEquals('eyJhbGciOiJSUzI1NiJ9.eyJjbGllbnRfaWQiOiJDTElFTlRfSUQifQ.R09PRF9TSUdOQVRVUkU', $authorization->getQueryParam('request')); |
409
|
|
|
static::assertInstanceOf(\OAuth2Framework\Component\Core\Client\Client::class, $authorization->getClient()); |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
/** |
413
|
|
|
* @test |
414
|
|
|
*/ |
415
|
|
|
public function theRequestObjectUriIsVerified() |
416
|
|
|
{ |
417
|
|
|
$client = $this->prophesize(\OAuth2Framework\Component\Core\Client\Client::class); |
418
|
|
|
$client->isDeleted()->willReturn(false)->shouldBeCalled(); |
419
|
|
|
$client->has('jwks')->willReturn(false)->shouldBeCalled(); |
420
|
|
|
$client->has('jwks_uri')->willReturn(false)->shouldBeCalled(); |
421
|
|
|
$client->has('client_secret')->willReturn(true)->shouldBeCalled(); |
422
|
|
|
$client->get('client_secret')->willReturn('SECRET')->shouldBeCalled(); |
423
|
|
|
$client->has('request_object_signing_alg')->willReturn(true)->shouldBeCalled(); |
424
|
|
|
$client->get('request_object_signing_alg')->willReturn('RS256')->shouldBeCalled(); |
425
|
|
|
$client->has('request_uris')->willReturn(true)->shouldBeCalled(); |
426
|
|
|
$client->get('request_uris')->willReturn(['https://www.foo.bar/'])->shouldBeCalled(); |
427
|
|
|
$client->getTokenEndpointAuthenticationMethod()->willReturn('client_secret_jwt'); |
428
|
|
|
|
429
|
|
|
$clientRepository = $this->prophesize(ClientRepository::class); |
430
|
|
|
$clientRepository->find(Argument::any())->willReturn($client->reveal())->shouldBeCalled(); |
431
|
|
|
$request = $this->prophesize(ServerRequestInterface::class); |
432
|
|
|
$request->getQueryParams() |
433
|
|
|
->willReturn([ |
434
|
|
|
'request_uri' => 'https://www.foo.bar/eyJhbGciOiJSUzI1NiJ9.eyJjbGllbnRfaWQiOiJDTElFTlRfSUQifQ.R09PRF9TSUdOQVRVUkU', |
435
|
|
|
]) |
436
|
|
|
->shouldBeCalled(); |
437
|
|
|
|
438
|
|
|
$authorization = $this->getAuthorizationRequestLoader($clientRepository->reveal())->load( |
439
|
|
|
$request->reveal() |
440
|
|
|
); |
441
|
|
|
|
442
|
|
|
static::assertTrue($authorization->hasQueryParam('client_id')); |
443
|
|
|
static::assertTrue($authorization->hasQueryParam('request_uri')); |
444
|
|
|
static::assertEquals('CLIENT_ID', $authorization->getQueryParam('client_id')); |
445
|
|
|
static::assertEquals('https://www.foo.bar/eyJhbGciOiJSUzI1NiJ9.eyJjbGllbnRfaWQiOiJDTElFTlRfSUQifQ.R09PRF9TSUdOQVRVUkU', $authorization->getQueryParam('request_uri')); |
446
|
|
|
static::assertInstanceOf(\OAuth2Framework\Component\Core\Client\Client::class, $authorization->getClient()); |
447
|
|
|
} |
448
|
|
|
|
449
|
|
|
/** |
450
|
|
|
* @test |
451
|
|
|
*/ |
452
|
|
|
public function theRequestUriIsNotAllowedForTheClient() |
453
|
|
|
{ |
454
|
|
|
$client = $this->prophesize(\OAuth2Framework\Component\Core\Client\Client::class); |
455
|
|
|
$client->isDeleted()->willReturn(false)->shouldBeCalled(); |
456
|
|
|
$client->has('jwks')->willReturn(false)->shouldBeCalled(); |
457
|
|
|
$client->has('jwks_uri')->willReturn(false)->shouldBeCalled(); |
458
|
|
|
$client->has('client_secret')->willReturn(true)->shouldBeCalled(); |
459
|
|
|
$client->get('client_secret')->willReturn('SECRET')->shouldBeCalled(); |
460
|
|
|
$client->has('request_object_signing_alg')->willReturn(true)->shouldBeCalled(); |
461
|
|
|
$client->get('request_object_signing_alg')->willReturn('RS256')->shouldBeCalled(); |
462
|
|
|
$client->has('request_uris')->willReturn(true)->shouldBeCalled(); |
463
|
|
|
$client->get('request_uris')->willReturn(['https://www.foo.bar/'])->shouldBeCalled(); |
464
|
|
|
$client->getTokenEndpointAuthenticationMethod()->willReturn('client_secret_jwt'); |
465
|
|
|
|
466
|
|
|
$clientRepository = $this->prophesize(ClientRepository::class); |
467
|
|
|
$clientRepository->find(Argument::any())->willReturn($client->reveal())->shouldBeCalled(); |
468
|
|
|
$request = $this->prophesize(ServerRequestInterface::class); |
469
|
|
|
$request->getQueryParams() |
470
|
|
|
->willReturn([ |
471
|
|
|
'request_uri' => 'https://www.bad.host/eyJhbGciOiJSUzI1NiJ9.eyJjbGllbnRfaWQiOiJDTElFTlRfSUQifQ.R09PRF9TSUdOQVRVUkU', |
472
|
|
|
]) |
473
|
|
|
->shouldBeCalled(); |
474
|
|
|
|
475
|
|
|
try { |
476
|
|
|
$this->getAuthorizationRequestLoader($clientRepository->reveal())->load( |
477
|
|
|
$request->reveal() |
478
|
|
|
); |
479
|
|
|
static::fail('The expected exception has not been thrown.'); |
480
|
|
|
} catch (OAuth2Error $e) { |
481
|
|
|
static::assertEquals(400, $e->getCode()); |
482
|
|
|
static::assertEquals('invalid_request_uri', $e->getMessage()); |
483
|
|
|
static::assertEquals('The request Uri is not allowed.', $e->getErrorDescription()); |
484
|
|
|
} |
485
|
|
|
} |
486
|
|
|
|
487
|
|
|
/** |
488
|
|
|
* @test |
489
|
|
|
*/ |
490
|
|
|
public function theRequestUriMustNotContainPathTraversal() |
491
|
|
|
{ |
492
|
|
|
$clientRepository = $this->prophesize(ClientRepository::class); |
493
|
|
|
$request = $this->prophesize(ServerRequestInterface::class); |
494
|
|
|
$request->getQueryParams() |
495
|
|
|
->willReturn([ |
496
|
|
|
'request_uri' => 'https://www.foo.bar/../eyJhbGciOiJSUzI1NiJ9.eyJjbGllbnRfaWQiOiJDTElFTlRfSUQifQ.R09PRF9TSUdOQVRVUkU', |
497
|
|
|
]) |
498
|
|
|
->shouldBeCalled(); |
499
|
|
|
|
500
|
|
|
try { |
501
|
|
|
$this->getAuthorizationRequestLoader($clientRepository->reveal())->load( |
502
|
|
|
$request->reveal() |
503
|
|
|
); |
504
|
|
|
static::fail('The expected exception has not been thrown.'); |
505
|
|
|
} catch (OAuth2Error $e) { |
506
|
|
|
static::assertEquals(400, $e->getCode()); |
507
|
|
|
static::assertEquals('invalid_request_uri', $e->getMessage()); |
508
|
|
|
static::assertEquals('The request Uri is not allowed.', $e->getErrorDescription()); |
509
|
|
|
} |
510
|
|
|
} |
511
|
|
|
|
512
|
|
|
/** |
513
|
|
|
* @var null|AuthorizationRequestLoader |
514
|
|
|
*/ |
515
|
|
|
private $authorizationRequestLoader; |
516
|
|
|
|
517
|
|
|
private function getAuthorizationRequestLoader(ClientRepository $clientRepository): AuthorizationRequestLoader |
518
|
|
|
{ |
519
|
|
|
if (null === $this->authorizationRequestLoader) { |
520
|
|
|
$this->authorizationRequestLoader = new AuthorizationRequestLoader($clientRepository); |
521
|
|
|
$this->authorizationRequestLoader->enableSignedRequestObjectSupport( |
522
|
|
|
$this->getJWSVerifier(), |
523
|
|
|
$this->getClaimCheckerManager() |
524
|
|
|
); |
525
|
|
|
$this->authorizationRequestLoader->enableRequestObjectReferenceSupport( |
526
|
|
|
$this->getHttpClient(), |
527
|
|
|
true |
528
|
|
|
); |
529
|
|
|
} |
530
|
|
|
|
531
|
|
|
return $this->authorizationRequestLoader; |
532
|
|
|
} |
533
|
|
|
|
534
|
|
|
private function getJWSVerifier(): JWSVerifier |
535
|
|
|
{ |
536
|
|
|
$verifier = $this->prophesize(JWSVerifier::class); |
537
|
|
|
$verifier->getSignatureAlgorithmManager()->willReturn( |
538
|
|
|
$this->getSignatureAlgorithmManager() |
539
|
|
|
); |
540
|
|
|
$verifier->verifyWithKeySet(Argument::type(JWS::class), Argument::type(JWKSet::class), 0, null)->will(function (array $args) { |
541
|
|
|
return |
542
|
|
|
'RS256' === ($args[0])->getSignature(0)->getProtectedHeaderParameter('alg') && |
543
|
|
|
'' !== ($args[0])->getSignature(0)->getSignature(); |
544
|
|
|
}); |
545
|
|
|
|
546
|
|
|
return $verifier->reveal(); |
547
|
|
|
} |
548
|
|
|
|
549
|
|
|
private function getSignatureAlgorithmManager(): AlgorithmManager |
550
|
|
|
{ |
551
|
|
|
$manager = $this->prophesize(AlgorithmManager::class); |
552
|
|
|
$manager->list()->willReturn(['RS256']); |
553
|
|
|
|
554
|
|
|
return $manager->reveal(); |
555
|
|
|
} |
556
|
|
|
|
557
|
|
|
private function getClaimCheckerManager(): ClaimCheckerManager |
558
|
|
|
{ |
559
|
|
|
$manager = $this->prophesize(ClaimCheckerManager::class); |
560
|
|
|
$manager->check(Argument::any())->will(function (array $args) { |
561
|
|
|
return $args[0]; |
562
|
|
|
}); |
563
|
|
|
|
564
|
|
|
return $manager->reveal(); |
565
|
|
|
} |
566
|
|
|
|
567
|
|
|
private function getHttpClient(): Client |
568
|
|
|
{ |
569
|
|
|
$client = $this->prophesize(Client::class); |
570
|
|
|
$client->sendRequest(Argument::type(RequestInterface::class))->will(function (array $args) { |
571
|
|
|
/** @var Uri $uri */ |
572
|
|
|
$uri = ($args[0])->getUri(); |
573
|
|
|
switch ($uri->getPath()) { |
574
|
|
|
case '/eyJhbGciOiJSUzI1NiJ9.eyJjbGllbnRfaWQiOiJDTElFTlRfSUQifQ.R09PRF9TSUdOQVRVUkU': |
575
|
|
|
$response = new Response(); |
576
|
|
|
$response->getBody()->write('eyJhbGciOiJSUzI1NiJ9.eyJjbGllbnRfaWQiOiJDTElFTlRfSUQifQ.R09PRF9TSUdOQVRVUkU'); |
577
|
|
|
$response->getBody()->rewind(); |
578
|
|
|
|
579
|
|
|
return $response; |
580
|
|
|
} |
581
|
|
|
}); |
582
|
|
|
|
583
|
|
|
return $client->reveal(); |
584
|
|
|
} |
585
|
|
|
} |
586
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.