Skip to content

Commit 807a9f2

Browse files
mdeboerSpomky
authored andcommitted
Symfony Serializer wrapper (#157)
* Add Symfony serializer wrappers for JWE and JWS tokens * Provide BC for NotEncodableValueException
1 parent b5fa715 commit 807a9f2

File tree

13 files changed

+1393
-11
lines changed

13 files changed

+1393
-11
lines changed

composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
"symfony/browser-kit": "^3.4|^4.0",
8080
"symfony/finder": "^3.4|^4.0",
8181
"symfony/phpunit-bridge": "^3.4|^4.0",
82+
"symfony/serializer": "^3.3|^4.0",
8283
"symfony/yaml": "^3.4|^4.0"
8384
},
8485
"replace": {
@@ -110,7 +111,8 @@
110111
"bjeavons/zxcvbn-php": "Adds key quality check for oct keys.",
111112
"php-http/httplug": "To enable JKU/X5U support.",
112113
"php-http/httplug-bundle": "To enable JKU/X5U support.",
113-
"php-http/message-factory": "To enable JKU/X5U support."
114+
"php-http/message-factory": "To enable JKU/X5U support.",
115+
"symfony/serializer": "Use the Symfony serializer to serialize/unserialize JWS and JWE tokens."
114116
},
115117
"extra": {
116118
"branch-alias": {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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 Jose\Bundle\JoseFramework\DependencyInjection\Compiler;
15+
16+
use Jose\Bundle\JoseFramework\Normalizer\JWENormalizer;
17+
use Jose\Bundle\JoseFramework\Normalizer\JWSNormalizer;
18+
use Jose\Bundle\JoseFramework\Serializer\JWEEncoder;
19+
use Jose\Bundle\JoseFramework\Serializer\JWSEncoder;
20+
use Jose\Component\Encryption\Serializer\JWESerializerManagerFactory;
21+
use Jose\Component\Signature\Serializer\JWSSerializerManagerFactory;
22+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
23+
use Symfony\Component\DependencyInjection\ContainerBuilder;
24+
25+
class SymfonySerializerCompilerPass implements CompilerPassInterface
26+
{
27+
public function process(ContainerBuilder $container)
28+
{
29+
if (!\class_exists('Symfony\Component\Serializer\Serializer')) {
30+
return;
31+
}
32+
33+
if ($container->hasDefinition(JWSSerializerManagerFactory::class)) {
34+
$container->autowire(JWSEncoder::class, JWSEncoder::class)
35+
->setPrivate(true)
36+
->addTag('serializer.encoder');
37+
$container->autowire(JWSNormalizer::class, JWSNormalizer::class)
38+
->setPrivate(true)
39+
->addTag('serializer.normalizer');
40+
}
41+
42+
if ($container->hasDefinition(JWESerializerManagerFactory::class)) {
43+
$container->autowire(JWEEncoder::class, JWEEncoder::class)
44+
->setPrivate(true)
45+
->addTag('serializer.encoder');
46+
$container->autowire(JWENormalizer::class, JWENormalizer::class)
47+
->setPrivate(true)
48+
->addTag('serializer.normalizer');
49+
}
50+
}
51+
}

src/Bundle/JoseFramework/JoseFrameworkBundle.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313

1414
namespace Jose\Bundle\JoseFramework;
1515

16+
use Jose\Bundle\JoseFramework\DependencyInjection\Compiler\SymfonySerializerCompilerPass;
1617
use Jose\Bundle\JoseFramework\DependencyInjection\JoseFrameworkExtension;
1718
use Jose\Bundle\JoseFramework\DependencyInjection\Source;
19+
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
1820
use Symfony\Component\DependencyInjection\ContainerBuilder;
1921
use Symfony\Component\HttpKernel\Bundle\Bundle;
2022

@@ -46,6 +48,7 @@ public function getContainerExtension()
4648
public function build(ContainerBuilder $container)
4749
{
4850
parent::build($container);
51+
4952
foreach ($this->sources as $source) {
5053
if ($source instanceof Source\SourceWithCompilerPasses) {
5154
$compilerPasses = $source->getCompilerPasses();
@@ -54,6 +57,8 @@ public function build(ContainerBuilder $container)
5457
}
5558
}
5659
}
60+
61+
$container->addCompilerPass(new SymfonySerializerCompilerPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 10);
5762
}
5863

5964
/**
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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 Jose\Bundle\JoseFramework\Normalizer;
15+
16+
use Jose\Component\Encryption\JWE;
17+
use Jose\Component\Encryption\Serializer\JWESerializerManager;
18+
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
19+
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
20+
21+
/**
22+
* JWE normalizer.
23+
*/
24+
class JWENormalizer implements NormalizerInterface, DenormalizerInterface
25+
{
26+
public function supportsNormalization($data, $format = null)
27+
{
28+
return $data instanceof JWE && $this->componentInstalled();
29+
}
30+
31+
public function supportsDenormalization($data, $type, $format = null)
32+
{
33+
return JWE::class === $type && $this->componentInstalled();
34+
}
35+
36+
public function normalize($object, $format = null, array $context = [])
37+
{
38+
return $object;
39+
}
40+
41+
public function denormalize($data, $class, $format = null, array $context = [])
42+
{
43+
return $data;
44+
}
45+
46+
/**
47+
* Check if encryption component is installed.
48+
*/
49+
private function componentInstalled(): bool
50+
{
51+
return class_exists(JWESerializerManager::class);
52+
}
53+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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 Jose\Bundle\JoseFramework\Normalizer;
15+
16+
use Jose\Component\Signature\JWS;
17+
use Jose\Component\Signature\Serializer\JWSSerializerManager;
18+
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
19+
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
20+
21+
/**
22+
* JWS normalizer.
23+
*/
24+
class JWSNormalizer implements NormalizerInterface, DenormalizerInterface
25+
{
26+
public function supportsNormalization($data, $format = null)
27+
{
28+
return $data instanceof JWS && $this->componentInstalled();
29+
}
30+
31+
public function supportsDenormalization($data, $type, $format = null)
32+
{
33+
return JWS::class === $type && $this->componentInstalled();
34+
}
35+
36+
public function normalize($object, $format = null, array $context = [])
37+
{
38+
return $object;
39+
}
40+
41+
public function denormalize($data, $class, $format = null, array $context = [])
42+
{
43+
return $data;
44+
}
45+
46+
/**
47+
* Check if encryption component is installed.
48+
*/
49+
private function componentInstalled(): bool
50+
{
51+
return class_exists(JWSSerializerManager::class);
52+
}
53+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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 Jose\Bundle\JoseFramework\Serializer;
15+
16+
use Jose\Component\Encryption\Serializer\JWESerializerManager;
17+
use Jose\Component\Encryption\Serializer\JWESerializerManagerFactory;
18+
use Symfony\Component\Serializer\Encoder\DecoderInterface;
19+
use Symfony\Component\Serializer\Encoder\EncoderInterface;
20+
use Symfony\Component\Serializer\Exception\NotEncodableValueException;
21+
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
22+
23+
class JWEEncoder implements EncoderInterface, DecoderInterface
24+
{
25+
/**
26+
* @var JWESerializerManager
27+
*/
28+
protected $serializerManager;
29+
30+
public function __construct(
31+
JWESerializerManagerFactory $serializerManagerFactory,
32+
?JWESerializerManager $serializerManager = null
33+
) {
34+
if (null === $serializerManager) {
35+
$serializerManager = $serializerManagerFactory->create($serializerManagerFactory->names());
36+
}
37+
38+
$this->serializerManager = $serializerManager;
39+
}
40+
41+
public function supportsEncoding($format)
42+
{
43+
return \in_array(mb_strtolower($format), $this->serializerManager->list(), true);
44+
}
45+
46+
public function supportsDecoding($format)
47+
{
48+
return $this->supportsEncoding($format);
49+
}
50+
51+
public function encode($data, $format, array $context = [])
52+
{
53+
try {
54+
return $this->serializerManager->serialize(mb_strtolower($format), $data, $this->getRecipientIndex($context));
55+
} catch (\Exception $ex) {
56+
$message = sprintf('Cannot encode JWE to %s format.', $format);
57+
58+
if (\class_exists('Symfony\Component\Serializer\Exception\NotEncodableValueException')) {
59+
throw new NotEncodableValueException($message, 0, $ex);
60+
}
61+
62+
throw new UnexpectedValueException($message, 0, $ex);
63+
}
64+
}
65+
66+
public function decode($data, $format, array $context = [])
67+
{
68+
try {
69+
return $this->serializerManager->unserialize($data);
70+
} catch (\Exception $ex) {
71+
$message = sprintf('Cannot decode JWE from %s format.', $format);
72+
73+
if (\class_exists('Symfony\Component\Serializer\Exception\NotEncodableValueException')) {
74+
throw new NotEncodableValueException($message, 0, $ex);
75+
}
76+
77+
throw new UnexpectedValueException($message, 0, $ex);
78+
}
79+
}
80+
81+
/**
82+
* Get JWE recipient index from context.
83+
*/
84+
protected function getRecipientIndex(array $context): int
85+
{
86+
$recipientIndex = 0;
87+
88+
if (isset($context['recipient_index']) && \is_int($context['recipient_index'])) {
89+
$recipientIndex = $context['recipient_index'];
90+
}
91+
92+
return $recipientIndex;
93+
}
94+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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 Jose\Bundle\JoseFramework\Serializer;
15+
16+
use Jose\Component\Signature\Serializer\JWSSerializerManager;
17+
use Jose\Component\Signature\Serializer\JWSSerializerManagerFactory;
18+
use Symfony\Component\Serializer\Encoder\DecoderInterface;
19+
use Symfony\Component\Serializer\Encoder\EncoderInterface;
20+
use Symfony\Component\Serializer\Exception\NotEncodableValueException;
21+
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
22+
23+
class JWSEncoder implements EncoderInterface, DecoderInterface
24+
{
25+
/**
26+
* @var JWSSerializerManager
27+
*/
28+
protected $serializerManager;
29+
30+
public function __construct(
31+
JWSSerializerManagerFactory $serializerManagerFactory,
32+
?JWSSerializerManager $serializerManager = null
33+
) {
34+
if (null === $serializerManager) {
35+
$serializerManager = $serializerManagerFactory->create($serializerManagerFactory->names());
36+
}
37+
38+
$this->serializerManager = $serializerManager;
39+
}
40+
41+
public function supportsEncoding($format)
42+
{
43+
return \in_array(mb_strtolower($format), $this->serializerManager->list(), true);
44+
}
45+
46+
public function supportsDecoding($format)
47+
{
48+
return $this->supportsEncoding($format);
49+
}
50+
51+
public function encode($data, $format, array $context = [])
52+
{
53+
try {
54+
return $this->serializerManager->serialize(mb_strtolower($format), $data, $this->getSignatureIndex($context));
55+
} catch (\Exception $ex) {
56+
$message = sprintf('Cannot encode JWS to %s format.', $format);
57+
58+
if (\class_exists('Symfony\Component\Serializer\Exception\NotEncodableValueException')) {
59+
throw new NotEncodableValueException($message, 0, $ex);
60+
}
61+
62+
throw new UnexpectedValueException($message, 0, $ex);
63+
}
64+
}
65+
66+
public function decode($data, $format, array $context = [])
67+
{
68+
try {
69+
return $this->serializerManager->unserialize($data);
70+
} catch (\Exception $ex) {
71+
$message = sprintf('Cannot decode JWS from %s format.', $format);
72+
73+
if (\class_exists('Symfony\Component\Serializer\Exception\NotEncodableValueException')) {
74+
throw new NotEncodableValueException($message, 0, $ex);
75+
}
76+
77+
throw new UnexpectedValueException($message, 0, $ex);
78+
}
79+
}
80+
81+
/**
82+
* Get JWS signature index from context.
83+
*/
84+
protected function getSignatureIndex(array $context): int
85+
{
86+
$signatureIndex = 0;
87+
88+
if (isset($context['signature_index']) && \is_int($context['signature_index'])) {
89+
$signatureIndex = $context['signature_index'];
90+
}
91+
92+
return $signatureIndex;
93+
}
94+
}

0 commit comments

Comments
 (0)