13
13
14
14
use Jose \Component \Core \AlgorithmManager ;
15
15
use Jose \Component \Core \JWK ;
16
+ use Jose \Component \Encryption \Algorithm \ContentEncryption \A128GCM ;
17
+ use Jose \Component \Encryption \Algorithm \KeyEncryption \ECDHES ;
18
+ use Jose \Component \Encryption \JWEBuilder ;
16
19
use Jose \Component \Signature \Algorithm \ES256 ;
17
20
use Jose \Component \Signature \JWSBuilder ;
18
- use Jose \Component \Signature \Serializer \CompactSerializer ;
21
+ use Jose \Component \Signature \Serializer \CompactSerializer as JwsCompactSerializer ;
22
+ use Jose \Component \Encryption \Serializer \CompactSerializer as JweCompactSerializer ;
19
23
use Symfony \Component \Config \Definition \Exception \InvalidConfigurationException ;
20
24
use Symfony \Component \HttpClient \MockHttpClient ;
21
25
use Symfony \Component \HttpClient \Response \MockResponse ;
@@ -349,34 +353,10 @@ public function testCustomUserLoader()
349
353
350
354
/**
351
355
* @requires extension openssl
356
+ * @dataProvider validAccessTokens
352
357
*/
353
- public function testOidcSuccess ()
358
+ public function testOidcSuccess (string $ token )
354
359
{
355
- $ time = time ();
356
- $ claims = [
357
- 'iat ' => $ time ,
358
- 'nbf ' => $ time ,
359
- 'exp ' => $ time + 3600 ,
360
- 'iss ' => 'https://www.example.com ' ,
361
- 'aud ' => 'Symfony OIDC ' ,
362
- 'sub ' => 'e21bf182-1538-406e-8ccb-e25a17aba39f ' ,
363
- 'username ' => 'dunglas ' ,
364
- ];
365
- $ token = (new CompactSerializer ())->serialize ((new JWSBuilder (new AlgorithmManager ([
366
- new ES256 (),
367
- ])))->create ()
368
- ->withPayload (json_encode ($ claims ))
369
- // tip: use https://mkjwk.org/ to generate a JWK
370
- ->addSignature (new JWK ([
371
- 'kty ' => 'EC ' ,
372
- 'crv ' => 'P-256 ' ,
373
- 'x ' => '0QEAsI1wGI-dmYatdUZoWSRWggLEpyzopuhwk-YUnA4 ' ,
374
- 'y ' => 'KYl-qyZ26HobuYwlQh-r0iHX61thfP82qqEku7i0woo ' ,
375
- 'd ' => 'iA_TV2zvftni_9aFAQwFO_9aypfJFCSpcCyevDvz220 ' ,
376
- ]), ['alg ' => 'ES256 ' ])
377
- ->build ()
378
- );
379
-
380
360
$ client = $ this ->createClient (['test_case ' => 'AccessToken ' , 'root_config ' => 'config_oidc.yml ' ]);
381
361
$ client ->request ('GET ' , '/foo ' , [], [], ['HTTP_AUTHORIZATION ' => \sprintf ('Bearer %s ' , $ token )]);
382
362
$ response = $ client ->getResponse ();
@@ -386,6 +366,21 @@ public function testOidcSuccess()
386
366
$ this ->assertSame (['message ' => 'Welcome @dunglas! ' ], json_decode ($ response ->getContent (), true ));
387
367
}
388
368
369
+ /**
370
+ * @requires extension openssl
371
+ * @dataProvider invalidAccessTokens
372
+ */
373
+ public function testOidcFailure (string $ token )
374
+ {
375
+ $ client = $ this ->createClient (['test_case ' => 'AccessToken ' , 'root_config ' => 'config_oidc.yml ' ]);
376
+ $ client ->request ('GET ' , '/foo ' , [], [], ['HTTP_AUTHORIZATION ' => \sprintf ('Bearer %s ' , $ token )]);
377
+ $ response = $ client ->getResponse ();
378
+
379
+ $ this ->assertInstanceOf (Response::class, $ response );
380
+ $ this ->assertSame (401 , $ response ->getStatusCode ());
381
+ $ this ->assertSame ('Bearer realm="My API",error="invalid_token",error_description="Invalid credentials." ' , $ response ->headers ->get ('WWW-Authenticate ' ));
382
+ }
383
+
389
384
public function testCasSuccess ()
390
385
{
391
386
$ casResponse = new MockResponse (<<<BODY
@@ -408,4 +403,90 @@ public function testCasSuccess()
408
403
$ this ->assertSame (200 , $ response ->getStatusCode ());
409
404
$ this ->assertSame (['message ' => 'Welcome @dunglas! ' ], json_decode ($ response ->getContent (), true ));
410
405
}
406
+
407
+ public function validAccessTokens (): array
408
+ {
409
+ $ time = time ();
410
+ $ claims = [
411
+ 'iat ' => $ time ,
412
+ 'nbf ' => $ time ,
413
+ 'exp ' => $ time + 3600 ,
414
+ 'iss ' => 'https://www.example.com ' ,
415
+ 'aud ' => 'Symfony OIDC ' ,
416
+ 'sub ' => 'e21bf182-1538-406e-8ccb-e25a17aba39f ' ,
417
+ 'username ' => 'dunglas ' ,
418
+ ];
419
+ $ jws = $ this ->createJws ($ claims );
420
+ $ jwe = $ this ->createJwe ($ jws );
421
+
422
+ return [
423
+ [$ jws ],
424
+ [$ jwe ],
425
+ ];
426
+ }
427
+
428
+ public function invalidAccessTokens (): array
429
+ {
430
+ $ time = time ();
431
+ $ claims = [
432
+ 'iat ' => $ time ,
433
+ 'nbf ' => $ time ,
434
+ 'exp ' => $ time + 3600 ,
435
+ 'iss ' => 'https://www.example.com ' ,
436
+ 'aud ' => 'Symfony OIDC ' ,
437
+ 'sub ' => 'e21bf182-1538-406e-8ccb-e25a17aba39f ' ,
438
+ 'username ' => 'dunglas ' ,
439
+ ];
440
+
441
+ return [
442
+ [$ this ->createJws ([...$ claims , 'aud ' => 'Invalid Audience ' ])],
443
+ [$ this ->createJws ([...$ claims , 'iss ' => 'Invalid Issuer ' ])],
444
+ [$ this ->createJws ([...$ claims , 'exp ' => $ time - 3600 ])],
445
+ [$ this ->createJws ([...$ claims , 'nbf ' => $ time + 3600 ])],
446
+ [$ this ->createJws ([...$ claims , 'iat ' => $ time + 3600 ])],
447
+ [$ this ->createJws ([...$ claims , 'username ' => 'Invalid Username ' ])],
448
+ [$ this ->createJwe ($ this ->createJws ($ claims ), ['exp ' => $ time - 3600 ])],
449
+ [$ this ->createJwe ($ this ->createJws ($ claims ), ['cty ' => 'x-specific ' ])],
450
+ ];
451
+ }
452
+
453
+ private function createJws (array $ claims , array $ header = []): string
454
+ {
455
+ return (new JwsCompactSerializer ())->serialize ((new JWSBuilder (new AlgorithmManager ([
456
+ new ES256 (),
457
+ ])))->create ()
458
+ ->withPayload (json_encode ($ claims ))
459
+ // tip: use https://mkjwk.org/ to generate a JWK
460
+ ->addSignature (new JWK ([
461
+ 'kty ' => 'EC ' ,
462
+ 'crv ' => 'P-256 ' ,
463
+ 'x ' => '0QEAsI1wGI-dmYatdUZoWSRWggLEpyzopuhwk-YUnA4 ' ,
464
+ 'y ' => 'KYl-qyZ26HobuYwlQh-r0iHX61thfP82qqEku7i0woo ' ,
465
+ 'd ' => 'iA_TV2zvftni_9aFAQwFO_9aypfJFCSpcCyevDvz220 ' ,
466
+ ]), [...$ header , 'alg ' => 'ES256 ' ])
467
+ ->build ()
468
+ );
469
+ }
470
+
471
+ private function createJwe (string $ input , array $ header = []): string
472
+ {
473
+ $ jwk = new JWK ([
474
+ 'kty ' => 'EC ' ,
475
+ 'use ' => 'enc ' ,
476
+ 'crv ' => 'P-256 ' ,
477
+ 'kid ' => 'enc-1720876375 ' ,
478
+ 'x ' => '4P27-OB2s5ZP3Zt5ExxQ9uFrgnGaMK6wT1oqd5bJozQ ' ,
479
+ 'y ' => 'CNh-ZbKJBvz6hJ8JOulXclACP2OuoO2PtqT6WC8tLcU ' ,
480
+ ]);
481
+ return (new JweCompactSerializer ())->serialize (
482
+ (new JWEBuilder (new AlgorithmManager ([
483
+ new ECDHES (), new A128GCM (),
484
+ ])))->create ()
485
+ ->withPayload ($ input )
486
+ ->withSharedProtectedHeader (['alg ' => 'ECDH-ES ' , 'enc ' => 'A128GCM ' , ...$ header ])
487
+ // tip: use https://mkjwk.org/ to generate a JWK
488
+ ->addRecipient ($ jwk )
489
+ ->build ()
490
+ );
491
+ }
411
492
}
0 commit comments