Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Security] Support RSA algorithm signature for OIDC tokens #53682

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,7 @@
"twig/cssinliner-extra": "^2.12|^3",
"twig/inky-extra": "^2.12|^3",
"twig/markdown-extra": "^2.12|^3",
"web-token/jwt-checker": "^3.1",
"web-token/jwt-signature-algorithm-ecdsa": "^3.1"
"web-token/jwt-library": "^3.3.2"
},
"conflict": {
"ext-psr": "<1.1|>=2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class UnusedTagsPass implements CompilerPassInterface
'routing.route_loader',
'scheduler.schedule_provider',
'scheduler.task',
'security.access_token_handler.oidc.signature_algorithm',
'security.authenticator.login_linker',
'security.expression_language_provider',
'security.remember_me_handler',
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Bundle/SecurityBundle/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ CHANGELOG
---

* Mark class `ExpressionCacheWarmer` as `final`
* Support multiple signature algorithms for OIDC Token
* Support JWK or JWKSet for OIDC Token

7.0
---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@

use Jose\Component\Core\Algorithm;
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Reference;

/**
* Configures a token handler for decoding and validating an OIDC token.
Expand All @@ -31,22 +31,15 @@ public function create(ContainerBuilder $container, string $id, array|string $co
->replaceArgument(4, $config['claim'])
);

if (!ContainerBuilder::willBeAvailable('web-token/jwt-core', Algorithm::class, ['symfony/security-bundle'])) {
throw new LogicException('You cannot use the "oidc" token handler since "web-token/jwt-core" is not installed. Try running "composer require web-token/jwt-core".');
if (!ContainerBuilder::willBeAvailable('web-token/jwt-library', Algorithm::class, ['symfony/security-bundle'])) {
throw new LogicException('You cannot use the "oidc" token handler since "web-token/jwt-library" is not installed. Try running "composer require web-token/jwt-library".');
}

// @see Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SignatureAlgorithmFactory
// for supported algorithms
if (\in_array($config['algorithm'], ['ES256', 'ES384', 'ES512'], true)) {
$tokenHandlerDefinition->replaceArgument(0, new Reference('security.access_token_handler.oidc.signature.'.$config['algorithm']));
} else {
$tokenHandlerDefinition->replaceArgument(0, (new ChildDefinition('security.access_token_handler.oidc.signature'))
->replaceArgument(0, $config['algorithm'])
);
}
$tokenHandlerDefinition->replaceArgument(0, (new ChildDefinition('security.access_token_handler.oidc.signature'))
->replaceArgument(0, $config['algorithms']));

$tokenHandlerDefinition->replaceArgument(1, (new ChildDefinition('security.access_token_handler.oidc.jwk'))
->replaceArgument(0, $config['key'])
$tokenHandlerDefinition->replaceArgument(1, (new ChildDefinition('security.access_token_handler.oidc.jwkset'))
->replaceArgument(0, $config['keyset'])
);
}

Expand All @@ -60,6 +53,37 @@ public function addConfiguration(NodeBuilder $node): void
$node
->arrayNode($this->getKey())
->fixXmlConfig($this->getKey())
->validate()
->ifTrue(static fn ($v) => !isset($v['algorithm']) && !isset($v['algorithms']))
->thenInvalid('You must set either "algorithm" or "algorithms".')
->end()
->validate()
->ifTrue(static fn ($v) => !isset($v['key']) && !isset($v['keyset']))
->thenInvalid('You must set either "key" or "keyset".')
->end()
->beforeNormalization()
->ifTrue(static fn ($v) => isset($v['algorithm']) && \is_string($v['algorithm']))
->then(static function ($v) {
if (isset($v['algorithms'])) {
throw new InvalidConfigurationException('You cannot use both "algorithm" and "algorithms" at the same time.');
}
$v['algorithms'] = [$v['algorithm']];
unset($v['algorithm']);

return $v;
})
->end()
->beforeNormalization()
->ifTrue(static fn ($v) => isset($v['key']) && \is_string($v['key']))
->then(static function ($v) {
if (isset($v['keyset'])) {
throw new InvalidConfigurationException('You cannot use both "key" and "keyset" at the same time.');
}
$v['keyset'] = sprintf('{"keys":[%s]}', $v['key']);

return $v;
})
->end()
->children()
->scalarNode('claim')
->info('Claim which contains the user identifier (e.g.: sub, email..).')
Expand All @@ -72,14 +96,23 @@ public function addConfiguration(NodeBuilder $node): void
->arrayNode('issuers')
->info('Issuers allowed to generate the token, for validation purpose.')
->isRequired()
->prototype('scalar')->end()
->scalarPrototype()->end()
->end()
->scalarNode('algorithm')
->arrayNode('algorithm')
->info('Algorithm used to sign the token.')
->setDeprecated('symfony/security-bundle', '7.1', 'The "%node%" option is deprecated and will be removed in 8.0. Use the "algorithms" option instead.')
->end()
->arrayNode('algorithms')
->info('Algorithms used to sign the token.')
->isRequired()
->scalarPrototype()->end()
->end()
->scalarNode('key')
->info('JSON-encoded JWK used to sign the token (must contain a "kty" key).')
->setDeprecated('symfony/security-bundle', '7.1', 'The "%node%" option is deprecated and will be removed in 8.0. Use the "keyset" option instead.')
->end()
->scalarNode('keyset')
->info('JSON-encoded JWKSet used to sign the token (must contain a list of valid keys).')
->isRequired()
->end()
->end()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +0,0 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;

use Jose\Component\Core\Algorithm as AlgorithmInterface;
use Jose\Component\Signature\Algorithm;
use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
use Symfony\Component\Security\Http\AccessToken\Oidc\OidcTokenHandler;

/**
* Creates a signature algorithm for {@see OidcTokenHandler}.
*
* @internal
*/
final class SignatureAlgorithmFactory
{
public static function create(string $algorithm): AlgorithmInterface
{
switch ($algorithm) {
case 'ES256':
case 'ES384':
case 'ES512':
if (!class_exists(Algorithm::class.'\\'.$algorithm)) {
throw new \LogicException(sprintf('You cannot use the "%s" signature algorithm since "web-token/jwt-signature-algorithm-ecdsa" is not installed. Try running "composer require web-token/jwt-signature-algorithm-ecdsa".', $algorithm));
}

$algorithm = Algorithm::class.'\\'.$algorithm;

return new $algorithm();
}

throw new InvalidArgumentException(sprintf('Unsupported signature algorithm "%s". Only ES* algorithms are supported. If you want to use another algorithm, create your TokenHandler as a service.', $algorithm));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,19 @@

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Jose\Component\Core\Algorithm;
use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Core\AlgorithmManagerFactory;
use Jose\Component\Core\JWK;
use Jose\Component\Core\JWKSet;
use Jose\Component\Signature\Algorithm\ES256;
use Jose\Component\Signature\Algorithm\ES384;
use Jose\Component\Signature\Algorithm\ES512;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SignatureAlgorithmFactory;
use Jose\Component\Signature\Algorithm\PS256;
use Jose\Component\Signature\Algorithm\PS384;
use Jose\Component\Signature\Algorithm\PS512;
use Jose\Component\Signature\Algorithm\RS256;
use Jose\Component\Signature\Algorithm\RS384;
use Jose\Component\Signature\Algorithm\RS512;
use Symfony\Component\Security\Http\AccessToken\ChainAccessTokenExtractor;
use Symfony\Component\Security\Http\AccessToken\FormEncodedBodyExtractor;
use Symfony\Component\Security\Http\AccessToken\HeaderAccessTokenExtractor;
Expand Down Expand Up @@ -77,28 +84,56 @@

->set('security.access_token_handler.oidc.jwk', JWK::class)
->abstract()
->deprecate('symfony/security-http', '7.1', 'The "%service_id%" service is deprecated. Please use "security.access_token_handler.oidc.jwkset" instead')
->factory([JWK::class, 'createFromJson'])
->args([
abstract_arg('signature key'),
])

->set('security.access_token_handler.oidc.signature', Algorithm::class)
->set('security.access_token_handler.oidc.jwkset', JWKSet::class)
->abstract()
->factory([SignatureAlgorithmFactory::class, 'create'])
->factory([JWKSet::class, 'createFromJson'])
->args([
abstract_arg('signature algorithm'),
abstract_arg('signature keyset'),
])

->set('security.access_token_handler.oidc.algorithm_manager_factory', AlgorithmManagerFactory::class)
->args([
tagged_iterator('security.access_token_handler.oidc.signature_algorithm'),
])

->set('security.access_token_handler.oidc.signature', AlgorithmManager::class)
->abstract()
->factory([service('security.access_token_handler.oidc.algorithm_manager_factory'), 'create'])
->args([
abstract_arg('signature algorithms'),
])

->set('security.access_token_handler.oidc.signature.ES256', ES256::class)
->parent('security.access_token_handler.oidc.signature')
->args(['index_0' => 'ES256'])
->tag('security.access_token_handler.oidc.signature_algorithm')

->set('security.access_token_handler.oidc.signature.ES384', ES384::class)
->parent('security.access_token_handler.oidc.signature')
->args(['index_0' => 'ES384'])
->tag('security.access_token_handler.oidc.signature_algorithm')

->set('security.access_token_handler.oidc.signature.ES512', ES512::class)
->parent('security.access_token_handler.oidc.signature')
->args(['index_0' => 'ES512'])
->tag('security.access_token_handler.oidc.signature_algorithm')

->set('security.access_token_handler.oidc.signature.RS256', RS256::class)
->tag('security.access_token_handler.oidc.signature_algorithm')

->set('security.access_token_handler.oidc.signature.RS384', RS384::class)
->tag('security.access_token_handler.oidc.signature_algorithm')

->set('security.access_token_handler.oidc.signature.RS512', RS512::class)
->tag('security.access_token_handler.oidc.signature_algorithm')

->set('security.access_token_handler.oidc.signature.PS256', PS256::class)
->tag('security.access_token_handler.oidc.signature_algorithm')

->set('security.access_token_handler.oidc.signature.PS384', PS384::class)
->tag('security.access_token_handler.oidc.signature_algorithm')

->set('security.access_token_handler.oidc.signature.PS512', PS512::class)
->tag('security.access_token_handler.oidc.signature_algorithm')
;
};
Loading