Completed
Push — master ( aaba5a...c6baf0 )
by Florent
04:07
created

getMethodWithEncryptionSupport()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 33
rs 9.392
c 0
b 0
f 0
cc 1
nc 1
nop 1
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\ClientAuthentication\Tests;
15
16
use Http\Message\MessageFactory\DiactorosMessageFactory;
17
use Jose\Component\Checker\ClaimCheckerManager;
18
use Jose\Component\Checker\HeaderCheckerManager;
19
use Jose\Component\Core\AlgorithmManager;
20
use Jose\Component\Core\Converter\StandardConverter;
21
use Jose\Component\Core\JWK;
22
use Jose\Component\Core\JWKSet;
23
use Jose\Component\Encryption\Algorithm\ContentEncryption\A256CBCHS512;
24
use Jose\Component\Encryption\Algorithm\KeyEncryption\RSAOAEP256;
25
use Jose\Component\Encryption\Compression\CompressionMethodManager;
26
use Jose\Component\Encryption\Compression\Deflate;
27
use Jose\Component\Encryption\JWEBuilder;
28
use Jose\Component\Encryption\JWEDecrypter;
29
use Jose\Component\Encryption\JWELoader;
30
use Jose\Component\Encryption\JWETokenSupport;
31
use Jose\Component\Encryption\Serializer\JWESerializerManager;
32
use Jose\Component\KeyManagement\JKUFactory;
33
use Jose\Component\Signature\Algorithm\HS256;
34
use Jose\Component\Signature\Algorithm\RS256;
35
use Jose\Component\Signature\JWS;
36
use Jose\Component\Signature\JWSBuilder;
37
use Jose\Component\Signature\JWSTokenSupport;
38
use Jose\Component\Signature\JWSVerifier;
39
use Jose\Component\Signature\Serializer\CompactSerializer;
40
use OAuth2Framework\Component\Core\Client\Client;
41
use OAuth2Framework\Component\Core\Client\ClientId;
42
use OAuth2Framework\Component\Core\DataBag\DataBag;
43
use OAuth2Framework\Component\Core\Message\OAuth2Message;
44
use OAuth2Framework\Component\Core\UserAccount\UserAccountId;
45
use OAuth2Framework\Component\ClientAuthentication\AuthenticationMethodManager;
46
use OAuth2Framework\Component\ClientAuthentication\ClientAssertionJwt;
47
use OAuth2Framework\Component\Core\TrustedIssuer\TrustedIssuer;
48
use OAuth2Framework\Component\Core\TrustedIssuer\TrustedIssuerRepository;
49
use PHPUnit\Framework\TestCase;
50
use Prophecy\Prophecy\ObjectProphecy;
51
use Psr\Http\Message\ServerRequestInterface;
52
use Psr\Http\Message\StreamInterface;
53
54
/**
55
 * @group TokenEndpoint
56
 * @group ClientAuthentication
57
 */
58
final class ClientAssertionJwtAuthenticationMethodTest extends TestCase
59
{
60
    /**
61
     * @test
62
     */
63
    public function genericCalls()
64
    {
65
        $method = $this->getMethod();
66
67
        self::assertEquals([], $method->getSchemesParameters());
68
        self::assertEquals(['client_secret_jwt', 'private_key_jwt'], $method->getSupportedMethods());
69
        self::assertEquals(['HS256', 'RS256'], $method->getSupportedSignatureAlgorithms());
70
        self::assertEquals([], $method->getSupportedKeyEncryptionAlgorithms());
71
        self::assertEquals([], $method->getSupportedContentEncryptionAlgorithms());
72
    }
73
74
    /**
75
     * @test
76
     */
77
    public function genericCallsWithEncryptionSupport()
78
    {
79
        $method = $this->getMethodWithEncryptionSupport(false);
80
81
        self::assertEquals([], $method->getSchemesParameters());
82
        self::assertEquals(['client_secret_jwt', 'private_key_jwt'], $method->getSupportedMethods());
83
        self::assertEquals(['HS256', 'RS256'], $method->getSupportedSignatureAlgorithms());
84
        self::assertEquals(['RSA-OAEP-256'], $method->getSupportedKeyEncryptionAlgorithms());
85
        self::assertEquals(['A256CBC-HS512'], $method->getSupportedContentEncryptionAlgorithms());
86
    }
87
88
    /**
89
     * @test
90
     */
91
    public function theClientIdCannotBeFoundInTheRequest()
92
    {
93
        $method = $this->getMethod();
94
        $request = $this->buildRequest([]);
95
        $request->getParsedBody()->willReturn([]);
96
        $request->getHeader('Authorization')->willReturn([]);
97
98
        $clientId = $method->findClientIdAndCredentials($request->reveal(), $credentials);
99
        self::assertNull($clientId);
100
        self::assertNull($credentials);
101
    }
102
103
    /**
104
     * @test
105
     */
106
    public function theClientAssertionTypeIsNotSupported()
107
    {
108
        $method = $this->getMethod();
109
        $request = $this->buildRequest([
110
            'client_assertion_type' => 'foo',
111
        ]);
112
113
        $request->getHeader('Authorization')->willReturn([]);
114
        $clientId = $method->findClientIdAndCredentials($request->reveal(), $credentials);
115
        self::assertNull($clientId);
116
        self::assertNull($credentials);
117
    }
118
119
    /**
120
     * @test
121
     */
122
    public function theClientAssertionIsMissing()
123
    {
124
        $method = $this->getMethod();
125
        $request = $this->buildRequest([
126
            'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
127
        ]);
128
129
        $request->getHeader('Authorization')->willReturn([]);
130
131
        try {
132
            $method->findClientIdAndCredentials($request->reveal(), $credentials);
133
            $this->fail('An OAuth2 exception should be thrown.');
134
        } catch (OAuth2Message $e) {
135
            self::assertEquals('invalid_request', $e->getMessage());
136
            self::assertEquals('Parameter "client_assertion" is missing.', $e->getErrorDescription());
137
        }
138
    }
139
140
    /**
141
     * @test
142
     */
143
    public function theClientAssertionIsInvalid()
144
    {
145
        $method = $this->getMethod();
146
        $request = $this->buildRequest([
147
            'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
148
            'client_assertion' => 'foo',
149
        ]);
150
151
        $request->getHeader('Authorization')->willReturn([]);
152
153
        try {
154
            $method->findClientIdAndCredentials($request->reveal(), $credentials);
155
            $this->fail('An OAuth2 exception should be thrown.');
156
        } catch (OAuth2Message $e) {
157
            self::assertEquals('invalid_request', $e->getMessage());
158
            self::assertEquals('Unable to load, decrypt or verify the client assertion.', $e->getErrorDescription());
159
        }
160
    }
161
162
    /**
163
     * @test
164
     */
165
    public function theClientAssertionSignedByTheClientIsInvalidBecauseOfMissingClaims()
166
    {
167
        $assertion = $this->serializeJWS(
168
            $this->createInvalidClientAssertionSignedByTheClient()
169
        );
170
        $method = $this->getMethod();
171
        $request = $this->buildRequest([
172
            'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
173
            'client_assertion' => $assertion,
174
        ]);
175
176
        $request->getHeader('Authorization')->willReturn([]);
177
178
        try {
179
            $method->findClientIdAndCredentials($request->reveal(), $credentials);
180
            $this->fail('An OAuth2 exception should be thrown.');
181
        } catch (OAuth2Message $e) {
182
            self::assertEquals('invalid_request', $e->getMessage());
183
            self::assertEquals('The following claim(s) is/are mandatory: "iss, sub, aud, exp".', $e->getErrorDescription());
184
        }
185
    }
186
187
    /**
188
     * @test
189
     */
190
    public function theClientAssertionSignedByTheClientIsRejectedBecauseEncryptionIsMandatory()
191
    {
192
        $assertion = $this->serializeJWS(
193
            $this->createInvalidClientAssertionSignedByTheClient()
194
        );
195
        $method = $this->getMethodWithEncryptionSupport(true);
196
        $request = $this->buildRequest([
197
            'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
198
            'client_assertion' => $assertion,
199
        ]);
200
201
        $request->getHeader('Authorization')->willReturn([]);
202
203
        try {
204
            $method->findClientIdAndCredentials($request->reveal(), $credentials);
205
            $this->fail('An OAuth2 exception should be thrown.');
206
        } catch (OAuth2Message $e) {
207
            self::assertEquals('invalid_request', $e->getMessage());
208
            self::assertEquals('The encryption of the assertion is mandatory but the decryption of the assertion failed.', $e->getErrorDescription());
209
        }
210
    }
211
212
    /**
213
     * @test
214
     */
215
    public function theEncryptedClientAssertionSignedAndEncryptedByTheClientIsInvalidBecauseOfMissingClaims()
216
    {
217
        $assertion = $this->encryptAssertion(
218
            $this->serializeJWS(
219
                $this->createInvalidClientAssertionSignedByTheClient()
220
            )
221
        );
222
        $method = $this->getMethodWithEncryptionSupport(false);
223
        $request = $this->buildRequest([
224
            'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
225
            'client_assertion' => $assertion,
226
        ]);
227
228
        $request->getHeader('Authorization')->willReturn([]);
229
230
        try {
231
            $method->findClientIdAndCredentials($request->reveal(), $credentials);
232
            $this->fail('An OAuth2 exception should be thrown.');
233
        } catch (OAuth2Message $e) {
234
            self::assertEquals('invalid_request', $e->getMessage());
235
            self::assertEquals('The following claim(s) is/are mandatory: "iss, sub, aud, exp".', $e->getErrorDescription());
236
        }
237
    }
238
239
    /**
240
     * @test
241
     */
242
    public function theClientAssertionIsValidAndTheClientIdIsRetrieved()
243
    {
244
        $assertion = $this->serializeJWS(
245
            $this->createValidClientAssertionSignedByTheClient()
246
        );
247
        $method = $this->getMethod();
248
        $request = $this->buildRequest([
249
            'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
250
            'client_assertion' => $assertion,
251
        ]);
252
253
        $request->getHeader('Authorization')->willReturn([]);
254
        $clientId = $method->findClientIdAndCredentials($request->reveal(), $credentials);
255
        self::assertEquals('ClientId', $clientId->getValue());
256
    }
257
258
    /**
259
     * @test
260
     */
261
    public function theEncryptedClientAssertionIsValidAndTheClientIdIsRetrieved()
262
    {
263
        $assertion = $this->encryptAssertion(
264
            $this->serializeJWS(
265
                $this->createValidClientAssertionSignedByTheClient()
266
            )
267
        );
268
        $method = $this->getMethodWithEncryptionSupport(false);
269
        $request = $this->buildRequest([
270
            'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
271
            'client_assertion' => $assertion,
272
        ]);
273
        $request->getHeader('Authorization')->willReturn([]);
274
275
        $clientId = $method->findClientIdAndCredentials($request->reveal(), $credentials);
276
        self::assertEquals('ClientId', $clientId->getValue());
277
    }
278
279
    /**
280
     * @test
281
     */
282
    public function theClientUsesAnotherAuthenticationMethod()
283
    {
284
        $jws = $this->createInvalidClientAssertionSignedByTheClient();
285
        $assertion = $this->encryptAssertion(
286
            $this->serializeJWS(
287
                $jws
288
            )
289
        );
290
        $method = $this->getMethodWithEncryptionSupport(false);
291
        $manager = new AuthenticationMethodManager();
292
        $manager->add($method);
293
        $client = Client::createEmpty();
294
        $client = $client->create(
295
            ClientId::create('CLIENT_ID'),
296
            DataBag::create([
297
                'client_secret' => 'CLIENT_SECRET',
298
                'token_endpoint_auth_method' => 'client_secret_post',
299
            ]),
300
            UserAccountId::create('USER_ACCOUNT_ID')
301
        );
302
        $request = $this->buildRequest([
303
            'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
304
            'client_assertion' => $assertion,
305
        ]);
306
307
        self::assertFalse($manager->isClientAuthenticated($request->reveal(), $client, $method, $jws));
308
    }
309
310
    /**
311
     * @test
312
     */
313
    public function theClientWithPrivateKeyIsAuthenticated()
314
    {
315
        $jws = $this->createValidClientAssertionSignedByTheClient();
316
        $assertion = $this->encryptAssertion(
317
            $this->serializeJWS(
318
                $jws
319
            )
320
        );
321
        $method = $this->getMethodWithEncryptionSupport(false);
322
        $manager = new AuthenticationMethodManager();
323
        $manager->add($method);
324
        $client = Client::createEmpty();
325
        $client = $client->create(
326
            ClientId::create('CLIENT_ID'),
327
            DataBag::create([
328
                'token_endpoint_auth_method' => 'private_key_jwt',
329
                'jwks' => \json_decode('{"keys":[{"kty":"oct","k":"U0VDUkVU"}]}', true),
330
            ]),
331
            UserAccountId::create('USER_ACCOUNT_ID')
332
        );
333
        $request = $this->buildRequest([
334
            'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
335
            'client_assertion' => $assertion,
336
        ]);
337
338
        self::assertTrue($manager->isClientAuthenticated($request->reveal(), $client, $method, $jws));
339
    }
340
341
    /**
342
     * @test
343
     */
344
    public function theClientWithClientSecretIsAuthenticated()
345
    {
346
        $jws = $this->createValidClientAssertionSignedByTheClient();
347
        $assertion = $this->encryptAssertion(
348
            $this->serializeJWS(
349
                $jws
350
            )
351
        );
352
        $method = $this->getMethodWithEncryptionSupport(false);
353
        $manager = new AuthenticationMethodManager();
354
        $manager->add($method);
355
        $client = Client::createEmpty();
356
        $client = $client->create(
357
            ClientId::create('CLIENT_ID'),
358
            DataBag::create([
359
                'token_endpoint_auth_method' => 'client_secret_jwt',
360
                'client_secret' => 'SECRET',
361
            ]),
362
            UserAccountId::create('USER_ACCOUNT_ID')
363
        );
364
        $request = $this->buildRequest([
365
            'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
366
            'client_assertion' => $assertion,
367
        ]);
368
369
        self::assertTrue($manager->isClientAuthenticated($request->reveal(), $client, $method, $jws));
370
    }
371
372
    /**
373
     * @test
374
     */
375
    public function theClientWithTrustedIssuerAssertionIsAuthenticated()
376
    {
377
        $jws = $this->createValidClientAssertionSignedByATrustedIssuer();
378
        $assertion = $this->serializeJWS(
379
            $jws
380
        );
381
        $method = $this->getMethodWithTrustedIssuerSupport();
382
        $manager = new AuthenticationMethodManager();
383
        $manager->add($method);
384
        $client = Client::createEmpty();
385
        $client = $client->create(
386
            ClientId::create('CLIENT_ID'),
387
            DataBag::create([
388
                'token_endpoint_auth_method' => 'client_secret_jwt',
389
            ]),
390
            UserAccountId::create('USER_ACCOUNT_ID')
391
        );
392
        $request = $this->buildRequest([
393
            'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
394
            'client_assertion' => $assertion,
395
        ]);
396
397
        self::assertTrue($manager->isClientAuthenticated($request->reveal(), $client, $method, $jws));
398
    }
399
400
    /**
401
     * @test
402
     */
403
    public function theClientConfigurationCanBeCheckedWithClientSecretJwt()
404
    {
405
        $method = $this->getMethod();
406
        $commandParameters = DataBag::create([
407
            'token_endpoint_auth_method' => 'client_secret_jwt',
408
        ]);
409
        $validatedParameters = $method->checkClientConfiguration($commandParameters, DataBag::create([]));
410
411
        self::assertTrue($validatedParameters->has('client_secret'));
412
        self::assertTrue($validatedParameters->has('client_secret_expires_at'));
413
    }
414
415
    /**
416
     * @test
417
     * @expectedException \InvalidArgumentException
418
     * @expectedExceptionMessage Either the parameter "jwks" or "jwks_uri" must be set.
419
     */
420
    public function theClientConfigurationCannotBeCheckedWithPrivateKeyJwtIfBothJwksAndJwksUriAreSet()
421
    {
422
        $method = $this->getMethod();
423
        $commandParameters = DataBag::create([
424
            'token_endpoint_auth_method' => 'private_key_jwt',
425
            'jwks' => 'foo',
426
            'jwks_uri' => 'bar',
427
        ]);
428
        $validatedParameters = $method->checkClientConfiguration($commandParameters, DataBag::create([]));
429
430
        self::assertTrue($validatedParameters->has('token_endpoint_auth_method'));
431
        self::assertEquals('private_key_jwt', $validatedParameters->get('token_endpoint_auth_method'));
432
    }
433
434
    /**
435
     * @test
436
     * @expectedException \InvalidArgumentException
437
     * @expectedExceptionMessage Either the parameter "jwks" or "jwks_uri" must be set.
438
     */
439
    public function theClientConfigurationCannotBeCheckedWithPrivateKeyJwtIfBothJwksAndJwksUriAreNotSetBecauseTrustedIssuerSupportIsDisabled()
440
    {
441
        $method = $this->getMethod();
442
        $commandParameters = DataBag::create([
443
            'token_endpoint_auth_method' => 'private_key_jwt',
444
        ]);
445
        $validatedParameters = $method->checkClientConfiguration($commandParameters, DataBag::create([]));
446
447
        self::assertTrue($validatedParameters->has('token_endpoint_auth_method'));
448
        self::assertEquals('private_key_jwt', $validatedParameters->get('token_endpoint_auth_method'));
449
    }
450
451
    /**
452
     * @test
453
     */
454
    public function theClientConfigurationCanBeCheckedWithPrivateKeyJwtIfBothJwksAndJwksUriAreNotSetBecauseTrustedIssuerSupportIsEnabled()
455
    {
456
        $method = $this->getMethodWithTrustedIssuerSupport();
457
        $commandParameters = DataBag::create([
458
            'token_endpoint_auth_method' => 'private_key_jwt',
459
        ]);
460
        $validatedParameters = $method->checkClientConfiguration($commandParameters, DataBag::create([]));
461
462
        self::assertFalse($validatedParameters->has('jwks'));
463
        self::assertFalse($validatedParameters->has('jwks_uri'));
464
    }
465
466
    /**
467
     * @test
468
     */
469
    public function theClientConfigurationCannotBeCheckedWithPrivateKeyJwtIfJwksIsNotAValidKeySet()
470
    {
471
        $method = $this->getMethod();
472
        $commandParameters = DataBag::create([
473
            'token_endpoint_auth_method' => 'private_key_jwt',
474
            'jwks' => 'foo',
475
        ]);
476
        $validatedParameters = $method->checkClientConfiguration($commandParameters, DataBag::create([]));
477
        self::assertTrue($validatedParameters->has('jwks'));
478
    }
479
480
    /**
481
     * @test
482
     */
483
    public function theClientConfigurationCanBeCheckedWithPrivateKeyJwtIfJwksIsValid()
484
    {
485
        $method = $this->getMethod();
486
        $commandParameters = DataBag::create([
487
            'token_endpoint_auth_method' => 'private_key_jwt',
488
            'jwks' => \json_decode('{"keys":[{"kty":"oct","k":"U0VDUkVU"}]}', true),
489
        ]);
490
        $validatedParameters = $method->checkClientConfiguration($commandParameters, DataBag::create([]));
491
492
        self::assertTrue($validatedParameters->has('jwks'));
493
        self::assertEquals(\json_decode('{"keys":[{"kty":"oct","k":"U0VDUkVU"}]}', true), $validatedParameters->get('jwks'));
494
    }
495
496
    /**
497
     * @test
498
     */
499
    public function theClientConfigurationCannotBeCheckedWithPrivateKeyJwtIfJwksUriFactoryIsNotAvailable()
500
    {
501
        $method = $this->getMethod();
502
        $commandParameters = DataBag::create([
503
            'token_endpoint_auth_method' => 'private_key_jwt',
504
            'jwks_uri' => 'foo',
505
        ]);
506
        $validatedParameters = $method->checkClientConfiguration($commandParameters, DataBag::create([]));
507
508
        self::assertTrue($validatedParameters->has('jwks_uri'));
509
    }
510
511
    /**
512
     * @var null|ClientAssertionJwt
513
     */
514
    private $method = null;
515
516
    /**
517
     * @return ClientAssertionJwt
518
     */
519
    private function getMethod(): ClientAssertionJwt
520
    {
521
        if (null === $this->method) {
522
            $this->method = new ClientAssertionJwt(
523
                new StandardConverter(),
524
                new JWSVerifier(AlgorithmManager::create([new HS256(), new RS256()])),
525
                HeaderCheckerManager::create([], [new JWSTokenSupport()]),
526
                ClaimCheckerManager::create([]),
527
                3600
528
            );
529
        }
530
531
        return $this->method;
532
    }
533
534
    /**
535
     * @param bool $isRequired
536
     *
537
     * @return ClientAssertionJwt
538
     */
539
    private function getMethodWithEncryptionSupport(bool $isRequired): ClientAssertionJwt
540
    {
541
        $method = clone $this->getMethod();
542
543
        $method->enableEncryptedAssertions(
544
            new JWELoader(
545
                JWESerializerManager::create([new \Jose\Component\Encryption\Serializer\CompactSerializer(new StandardConverter())]),
546
                new JWEDecrypter(
547
                    AlgorithmManager::create([new RSAOAEP256()]),
548
                    AlgorithmManager::create([new A256CBCHS512()]),
549
                    CompressionMethodManager::create([new Deflate()])
550
                ),
551
                HeaderCheckerManager::create([], [new JWETokenSupport()])
552
            ),
553
            JWKSet::createFromKeys([JWK::create([
554
                'kty' => 'RSA',
555
                'kid' => '[email protected]',
556
                'use' => 'enc',
557
                'n' => 'wbdxI55VaanZXPY29Lg5hdmv2XhvqAhoxUkanfzf2-5zVUxa6prHRrI4pP1AhoqJRlZfYtWWd5mmHRG2pAHIlh0ySJ9wi0BioZBl1XP2e-C-FyXJGcTy0HdKQWlrfhTm42EW7Vv04r4gfao6uxjLGwfpGrZLarohiWCPnkNrg71S2CuNZSQBIPGjXfkmIy2tl_VWgGnL22GplyXj5YlBLdxXp3XeStsqo571utNfoUTU8E4qdzJ3U1DItoVkPGsMwlmmnJiwA7sXRItBCivR4M5qnZtdw-7v4WuR4779ubDuJ5nalMv2S66-RPcnFAzWSKxtBDnFJJDGIUe7Tzizjg1nms0Xq_yPub_UOlWn0ec85FCft1hACpWG8schrOBeNqHBODFskYpUc2LC5JA2TaPF2dA67dg1TTsC_FupfQ2kNGcE1LgprxKHcVWYQb86B-HozjHZcqtauBzFNV5tbTuB-TpkcvJfNcFLlH3b8mb-H_ox35FjqBSAjLKyoeqfKTpVjvXhd09knwgJf6VKq6UC418_TOljMVfFTWXUxlnfhOOnzW6HSSzD1c9WrCuVzsUMv54szidQ9wf1cYWf3g5qFDxDQKis99gcDaiCAwM3yEBIzuNeeCa5dartHDb1xEB_HcHSeYbghbMjGfasvKn0aZRsnTyC0xhWBlsolZE',
558
                'e' => 'AQAB',
559
                'alg' => 'RSA-OAEP-256',
560
                'd' => 'n7fzJc3_WG59VEOBTkayzuSMM780OJQuZjN_KbH8lOZG25ZoA7T4Bxcc0xQn5oZE5uSCIwg91oCt0JvxPcpmqzaJZg1nirjcWZ-oBtVk7gCAWq-B3qhfF3izlbkosrzjHajIcY33HBhsy4_WerrXg4MDNE4HYojy68TcxT2LYQRxUOCf5TtJXvM8olexlSGtVnQnDRutxEUCwiewfmmrfveEogLx9EA-KMgAjTiISXxqIXQhWUQX1G7v_mV_Hr2YuImYcNcHkRvp9E7ook0876DhkO8v4UOZLwA1OlUX98mkoqwc58A_Y2lBYbVx1_s5lpPsEqbbH-nqIjh1fL0gdNfihLxnclWtW7pCztLnImZAyeCWAG7ZIfv-Rn9fLIv9jZ6r7r-MSH9sqbuziHN2grGjD_jfRluMHa0l84fFKl6bcqN1JWxPVhzNZo01yDF-1LiQnqUYSepPf6X3a2SOdkqBRiquE6EvLuSYIDpJq3jDIsgoL8Mo1LoomgiJxUwL_GWEOGu28gplyzm-9Q0U0nyhEf1uhSR8aJAQWAiFImWH5W_IQT9I7-yrindr_2fWQ_i1UgMsGzA7aOGzZfPljRy6z-tY_KuBG00-28S_aWvjyUc-Alp8AUyKjBZ-7CWH32fGWK48j1t-zomrwjL_mnhsPbGs0c9WsWgRzI-K8gE',
561
                'p' => '7_2v3OQZzlPFcHyYfLABQ3XP85Es4hCdwCkbDeltaUXgVy9l9etKghvM4hRkOvbb01kYVuLFmxIkCDtpi-zLCYAdXKrAK3PtSbtzld_XZ9nlsYa_QZWpXB_IrtFjVfdKUdMz94pHUhFGFj7nr6NNxfpiHSHWFE1zD_AC3mY46J961Y2LRnreVwAGNw53p07Db8yD_92pDa97vqcZOdgtybH9q6uma-RFNhO1AoiJhYZj69hjmMRXx-x56HO9cnXNbmzNSCFCKnQmn4GQLmRj9sfbZRqL94bbtE4_e0Zrpo8RNo8vxRLqQNwIy85fc6BRgBJomt8QdQvIgPgWCv5HoQ',
562
                'q' => 'zqOHk1P6WN_rHuM7ZF1cXH0x6RuOHq67WuHiSknqQeefGBA9PWs6ZyKQCO-O6mKXtcgE8_Q_hA2kMRcKOcvHil1hqMCNSXlflM7WPRPZu2qCDcqssd_uMbP-DqYthH_EzwL9KnYoH7JQFxxmcv5An8oXUtTwk4knKjkIYGRuUwfQTus0w1NfjFAyxOOiAQ37ussIcE6C6ZSsM3n41UlbJ7TCqewzVJaPJN5cxjySPZPD3Vp01a9YgAD6a3IIaKJdIxJS1ImnfPevSJQBE79-EXe2kSwVgOzvt-gsmM29QQ8veHy4uAqca5dZzMs7hkkHtw1z0jHV90epQJJlXXnH8Q',
563
                'dp' => '19oDkBh1AXelMIxQFm2zZTqUhAzCIr4xNIGEPNoDt1jK83_FJA-xnx5kA7-1erdHdms_Ef67HsONNv5A60JaR7w8LHnDiBGnjdaUmmuO8XAxQJ_ia5mxjxNjS6E2yD44USo2JmHvzeeNczq25elqbTPLhUpGo1IZuG72FZQ5gTjXoTXC2-xtCDEUZfaUNh4IeAipfLugbpe0JAFlFfrTDAMUFpC3iXjxqzbEanflwPvj6V9iDSgjj8SozSM0dLtxvu0LIeIQAeEgT_yXcrKGmpKdSO08kLBx8VUjkbv_3Pn20Gyu2YEuwpFlM_H1NikuxJNKFGmnAq9LcnwwT0jvoQ',
564
                'dq' => 'S6p59KrlmzGzaQYQM3o0XfHCGvfqHLYjCO557HYQf72O9kLMCfd_1VBEqeD-1jjwELKDjck8kOBl5UvohK1oDfSP1DleAy-cnmL29DqWmhgwM1ip0CCNmkmsmDSlqkUXDi6sAaZuntyukyflI-qSQ3C_BafPyFaKrt1fgdyEwYa08pESKwwWisy7KnmoUvaJ3SaHmohFS78TJ25cfc10wZ9hQNOrIChZlkiOdFCtxDqdmCqNacnhgE3bZQjGp3n83ODSz9zwJcSUvODlXBPc2AycH6Ci5yjbxt4Ppox_5pjm6xnQkiPgj01GpsUssMmBN7iHVsrE7N2iznBNCeOUIQ',
565
                'qi' => 'FZhClBMywVVjnuUud-05qd5CYU0dK79akAgy9oX6RX6I3IIIPckCciRrokxglZn-omAY5CnCe4KdrnjFOT5YUZE7G_Pg44XgCXaarLQf4hl80oPEf6-jJ5Iy6wPRx7G2e8qLxnh9cOdf-kRqgOS3F48Ucvw3ma5V6KGMwQqWFeV31XtZ8l5cVI-I3NzBS7qltpUVgz2Ju021eyc7IlqgzR98qKONl27DuEES0aK0WE97jnsyO27Yp88Wa2RiBrEocM89QZI1seJiGDizHRUP4UZxw9zsXww46wy0P6f9grnYp7t8LkyDDk8eoI4KX6SNMNVcyVS9IWjlq8EzqZEKIA',
566
            ])]),
567
            $isRequired
568
        );
569
570
        return $method;
571
    }
572
573
    /**
574
     * @return ClientAssertionJwt
575
     */
576
    private function getMethodWithTrustedIssuerSupport(): ClientAssertionJwt
577
    {
578
        $method = clone $this->getMethod();
579
580
        $trustedIssuer = $this->prophesize(TrustedIssuer::class);
581
        $trustedIssuer->name()->willReturn('TRUSTED_ISSUER');
582
        $trustedIssuer->getAllowedAssertionTypes()->willReturn(['urn:ietf:params:oauth:client-assertion-type:jwt-bearer']);
583
        $trustedIssuer->getAllowedSignatureAlgorithms()->willReturn(['RS256']);
584
        $trustedIssuer->getJWKSet()->willReturn(JWKSet::createFromKeys([JWK::create([
585
            'kty' => 'RSA',
586
            'n' => '33WRDEG5rN7daMgI2N5H8cPwTeQPOnz34uG2fe0yKyHjJDGE2XoESRpu5LelSPdYM_r4AWMFWoDWPd-7xaq7uFEkM8c6zaQIgj4uEiq-pBMvH-e805SFbYOKYqfQe4eeXAk4OrQwcUkSrlGskf6YUaw_3IwbPgzEDTgTZFVtQlE',
587
            'e' => 'AQAB',
588
            'alg' => 'RS256',
589
            'use' => 'sig',
590
        ])]));
591
592
        $trustedIssuerRepository = $this->prophesize(TrustedIssuerRepository::class);
593
        $trustedIssuerRepository->find('TRUSTED_ISSUER')->willReturn($trustedIssuer->reveal());
594
595
        $method->enableTrustedIssuerSupport($trustedIssuerRepository->reveal());
596
597
        return $method;
598
    }
599
600
    /**
601
     * @param \Http\Mock\Client $client
602
     *
603
     * @return JKUFactory
604
     */
605
    private function getJkuFactory(\Http\Mock\Client $client): JKUFactory
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
606
    {
607
        return new JKUFactory(
608
            new StandardConverter(),
609
            $client,
610
            new DiactorosMessageFactory()
611
        );
612
    }
613
614
    /**
615
     * @return \Http\Mock\Client
616
     */
617
    private function getHttpClient(): \Http\Mock\Client
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
618
    {
619
        return new \Http\Mock\Client(
620
            new DiactorosMessageFactory()
621
        );
622
    }
623
624
    /**
625
     * @param JWS $jws
626
     *
627
     * @return string
628
     *
629
     * @throws \Exception
630
     */
631
    private function serializeJWS(JWS $jws): string
632
    {
633
        $jsonConverter = new StandardConverter();
634
        $serializer = new CompactSerializer($jsonConverter);
635
636
        return $serializer->serialize($jws, 0);
637
    }
638
639
    /**
640
     * @return JWS
641
     */
642
    private function createValidClientAssertionSignedByTheClient(): JWS
643
    {
644
        $jsonConverter = new StandardConverter();
645
        $jwsBuilder = new JWSBuilder(
646
            $jsonConverter,
647
            AlgorithmManager::create([
648
                new HS256(),
649
            ])
650
        );
651
652
        return $jwsBuilder
653
            ->create()
654
            ->withPayload($jsonConverter->encode([
655
                'iss' => 'ClientId',
656
                'sub' => 'ClientId',
657
                'aud' => 'My Server',
658
                'exp' => \time() + 3600,
659
            ]))
660
            ->addSignature(
661
                JWK::createFromJson('{"kty":"oct","k":"U0VDUkVU"}'),
662
                ['alg' => 'HS256']
663
            )
664
            ->build();
665
    }
666
667
    /**
668
     * @return JWS
669
     */
670
    private function createValidClientAssertionSignedByATrustedIssuer(): JWS
671
    {
672
        $jsonConverter = new StandardConverter();
673
        $jwsBuilder = new JWSBuilder(
674
            $jsonConverter,
675
            AlgorithmManager::create([
676
                new RS256(),
677
            ])
678
        );
679
680
        return $jwsBuilder
681
            ->create()
682
            ->withPayload($jsonConverter->encode([
683
                'iss' => 'TRUSTED_ISSUER',
684
                'sub' => 'ClientId',
685
                'aud' => 'My Server',
686
                'exp' => \time() + 3600,
687
            ]))
688
            ->addSignature(
689
                JWK::create([
690
                    'kty' => 'RSA',
691
                    'n' => '33WRDEG5rN7daMgI2N5H8cPwTeQPOnz34uG2fe0yKyHjJDGE2XoESRpu5LelSPdYM_r4AWMFWoDWPd-7xaq7uFEkM8c6zaQIgj4uEiq-pBMvH-e805SFbYOKYqfQe4eeXAk4OrQwcUkSrlGskf6YUaw_3IwbPgzEDTgTZFVtQlE',
692
                    'e' => 'AQAB',
693
                    'p' => '9Vovb8pySyOZUoTrNMD6JmTsDa12u9y4_HImQuKD0rerVo2y5y7D_r00i1MhGHkBrI3W2PsubIiZgKp1f0oQfQ',
694
                    'd' => 'jrDrO3Fo2GvD5Jn_lER0mnxtIb_kvYt5WyaYutbRN1u_SKhaVeklfWzkrSZb5DkV2LOE1JXfoEgvBnms1O9OSJXwqDrFF7NDebw95g6JzI-SbkIHw0Cb-_E9K92FjvW3Bi8j9PKIa8c_dpwIAIirc_q8uhSTf4WoIOHSFbSaQPE',
695
                    'q' => '6Sgna9gQw4dXN0jBSjOZSjl4S2_H3wHatclrvlYfbJVU6GlIlqWGaUkdFvCuEr9iXJAY4zpEQ4P370EZtsyVZQ',
696
                    'dp' => '5m79fpE1Jz0YE1ijT7ivOMAws-fnTCnR08eiB8-W36GBWplbHaXejrJFV1WMD-AWomnVD5VZ1LW29hEiqZp2QQ',
697
                    'dq' => 'JV2pC7CB50QeZx7C02h3jZyuObC9YHEEoxOXr9ZPjPBVvjV5S6NVajQsdEu4Kgr_8YOqaWgiHovcxTwyqcgZvQ',
698
                    'qi' => 'VZykPj-ugKQxuWTSE-hA-nJqkl7FzjfzHte4QYUSHLHFq6oLlHhgUoJ_4oFLaBmCvgZLAFRDDD6pnd5Fgzt9ow',
699
                ]),
700
                ['alg' => 'RS256']
701
            )
702
            ->build();
703
    }
704
705
    /**
706
     * @return JWS
707
     *
708
     * @throws \Exception
709
     */
710
    private function createInvalidClientAssertionSignedByTheClient(): JWS
711
    {
712
        $jsonConverter = new StandardConverter();
713
        $jwsBuilder = new JWSBuilder(
714
            $jsonConverter,
715
            AlgorithmManager::create([
716
                new HS256(),
717
            ])
718
        );
719
720
        return $jwsBuilder
721
            ->create()
722
            ->withPayload($jsonConverter->encode([]))
723
            ->addSignature(
724
                JWK::createFromJson('{"kty":"oct","k":"U0VDUkVU"}'),
725
                ['alg' => 'HS256']
726
            )
727
            ->build();
728
    }
729
730
    /**
731
     * @return JWS
732
     */
733
    private function createInvalidClientAssertionSignedByATrustedIssuer(): JWS
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
734
    {
735
        $jsonConverter = new StandardConverter();
736
        $jwsBuilder = new JWSBuilder(
737
            $jsonConverter,
738
            AlgorithmManager::create([
739
                new RS256(),
740
            ])
741
        );
742
743
        return $jwsBuilder
744
            ->create()
745
            ->withPayload($jsonConverter->encode([
746
            ]))
747
            ->addSignature(
748
                JWK::create([
749
                    'kty' => 'RSA',
750
                    'n' => '33WRDEG5rN7daMgI2N5H8cPwTeQPOnz34uG2fe0yKyHjJDGE2XoESRpu5LelSPdYM_r4AWMFWoDWPd-7xaq7uFEkM8c6zaQIgj4uEiq-pBMvH-e805SFbYOKYqfQe4eeXAk4OrQwcUkSrlGskf6YUaw_3IwbPgzEDTgTZFVtQlE',
751
                    'e' => 'AQAB',
752
                    'p' => '9Vovb8pySyOZUoTrNMD6JmTsDa12u9y4_HImQuKD0rerVo2y5y7D_r00i1MhGHkBrI3W2PsubIiZgKp1f0oQfQ',
753
                    'd' => 'jrDrO3Fo2GvD5Jn_lER0mnxtIb_kvYt5WyaYutbRN1u_SKhaVeklfWzkrSZb5DkV2LOE1JXfoEgvBnms1O9OSJXwqDrFF7NDebw95g6JzI-SbkIHw0Cb-_E9K92FjvW3Bi8j9PKIa8c_dpwIAIirc_q8uhSTf4WoIOHSFbSaQPE',
754
                    'q' => '6Sgna9gQw4dXN0jBSjOZSjl4S2_H3wHatclrvlYfbJVU6GlIlqWGaUkdFvCuEr9iXJAY4zpEQ4P370EZtsyVZQ',
755
                    'dp' => '5m79fpE1Jz0YE1ijT7ivOMAws-fnTCnR08eiB8-W36GBWplbHaXejrJFV1WMD-AWomnVD5VZ1LW29hEiqZp2QQ',
756
                    'dq' => 'JV2pC7CB50QeZx7C02h3jZyuObC9YHEEoxOXr9ZPjPBVvjV5S6NVajQsdEu4Kgr_8YOqaWgiHovcxTwyqcgZvQ',
757
                    'qi' => 'VZykPj-ugKQxuWTSE-hA-nJqkl7FzjfzHte4QYUSHLHFq6oLlHhgUoJ_4oFLaBmCvgZLAFRDDD6pnd5Fgzt9ow',
758
                ]),
759
                ['alg' => 'RS256']
760
            )
761
            ->build();
762
    }
763
764
    /**
765
     * @param string $assertion
766
     *
767
     * @return string
768
     *
769
     * @throws \Exception
770
     */
771
    private function encryptAssertion(string $assertion): string
772
    {
773
        $jsonConverter = new StandardConverter();
774
        $jweBuilder = new JWEBuilder(
775
            $jsonConverter,
776
            AlgorithmManager::create([new RSAOAEP256()]),
777
            AlgorithmManager::create([new A256CBCHS512()]),
778
            CompressionMethodManager::create([new Deflate()])
779
        );
780
        $jwe = $jweBuilder->create()
781
            ->withPayload($assertion)
782
            ->withSharedProtectedHeader(['alg' => 'RSA-OAEP-256', 'enc' => 'A256CBC-HS512'])
783
            ->addRecipient(JWK::create([
784
                'kty' => 'RSA',
785
                'kid' => '[email protected]',
786
                'use' => 'enc',
787
                'n' => 'wbdxI55VaanZXPY29Lg5hdmv2XhvqAhoxUkanfzf2-5zVUxa6prHRrI4pP1AhoqJRlZfYtWWd5mmHRG2pAHIlh0ySJ9wi0BioZBl1XP2e-C-FyXJGcTy0HdKQWlrfhTm42EW7Vv04r4gfao6uxjLGwfpGrZLarohiWCPnkNrg71S2CuNZSQBIPGjXfkmIy2tl_VWgGnL22GplyXj5YlBLdxXp3XeStsqo571utNfoUTU8E4qdzJ3U1DItoVkPGsMwlmmnJiwA7sXRItBCivR4M5qnZtdw-7v4WuR4779ubDuJ5nalMv2S66-RPcnFAzWSKxtBDnFJJDGIUe7Tzizjg1nms0Xq_yPub_UOlWn0ec85FCft1hACpWG8schrOBeNqHBODFskYpUc2LC5JA2TaPF2dA67dg1TTsC_FupfQ2kNGcE1LgprxKHcVWYQb86B-HozjHZcqtauBzFNV5tbTuB-TpkcvJfNcFLlH3b8mb-H_ox35FjqBSAjLKyoeqfKTpVjvXhd09knwgJf6VKq6UC418_TOljMVfFTWXUxlnfhOOnzW6HSSzD1c9WrCuVzsUMv54szidQ9wf1cYWf3g5qFDxDQKis99gcDaiCAwM3yEBIzuNeeCa5dartHDb1xEB_HcHSeYbghbMjGfasvKn0aZRsnTyC0xhWBlsolZE',
788
                'e' => 'AQAB',
789
                'alg' => 'RSA-OAEP-256',
790
            ]))
791
            ->build();
792
793
        $serializer = new \Jose\Component\Encryption\Serializer\CompactSerializer($jsonConverter);
794
795
        return $serializer->serialize($jwe, 0);
796
    }
797
798
    private function buildRequest(array $data): ObjectProphecy
799
    {
800
        $body = $this->prophesize(StreamInterface::class);
801
        $body->getContents()->willReturn(\http_build_query($data));
802
        $request = $this->prophesize(ServerRequestInterface::class);
803
        $request->hasHeader('Content-Type')->willReturn(true);
804
        $request->getHeader('Content-Type')->willReturn(['application/x-www-form-urlencoded']);
805
        $request->getBody()->willReturn($body->reveal());
806
        $request->getParsedBody()->willReturn([]);
807
808
        return $request;
809
    }
810
}
811