Skip to content

Commit e1caab1

Browse files
introduce TokenHandlerFactory
1 parent 4c9394f commit e1caab1

File tree

10 files changed

+326
-25
lines changed

10 files changed

+326
-25
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\AccessToken;
13+
14+
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
15+
use Symfony\Component\DependencyInjection\ChildDefinition;
16+
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
18+
/**
19+
* Configure a token handler from a service id.
20+
*
21+
* @see \Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Security\Factory\AccessTokenFactoryTest
22+
*
23+
* @author Vincent Chalamon <vincentchalamon@gmail.com>
24+
*/
25+
class IdTokenHandlerFactory implements TokenHandlerFactoryInterface
26+
{
27+
public function create(ContainerBuilder $container, string $id, array|string $config): void
28+
{
29+
$container->setDefinition($id, new ChildDefinition($config));
30+
}
31+
32+
public function getKey(): string
33+
{
34+
return 'id';
35+
}
36+
37+
public function addConfiguration(NodeBuilder $node): void
38+
{
39+
$node->scalarNode($this->getKey())->end();
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\AccessToken;
13+
14+
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
15+
use Symfony\Component\DependencyInjection\ChildDefinition;
16+
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
use Symfony\Component\DependencyInjection\Reference;
18+
use Symfony\Component\HttpClient\HttpClient;
19+
20+
/**
21+
* Configure a token handler for an OIDC server.
22+
*
23+
* @see \Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Security\Factory\AccessTokenFactoryTest
24+
*
25+
* @author Vincent Chalamon <vincentchalamon@gmail.com>
26+
*/
27+
class OidcUserInfoTokenHandlerFactory implements TokenHandlerFactoryInterface
28+
{
29+
public function create(ContainerBuilder $container, string $id, array|string $config): void
30+
{
31+
$tokenHandlerDefinition = $container->setDefinition($id, new ChildDefinition('security.access_token_handler.oidc_user_info'));
32+
$tokenHandlerDefinition->setArgument(2, $config['claim']);
33+
34+
// Create the client service
35+
if (!isset($config['client']['id'])) {
36+
$clientDefinitionId = 'http_client.security.access_token_handler.oidc_user_info';
37+
$container->register($clientDefinitionId, HttpClient::class)
38+
->setFactory([HttpClient::class, 'create'])
39+
->setArguments([$config['client']])
40+
->addTag('http_client.client')
41+
;
42+
$config['client'] = ['id' => $clientDefinitionId];
43+
}
44+
45+
$tokenHandlerDefinition->setArgument(0, new Reference($config['client']['id']));
46+
}
47+
48+
public function getKey(): string
49+
{
50+
return 'oidc_user_info';
51+
}
52+
53+
public function addConfiguration(NodeBuilder $node): void
54+
{
55+
$node
56+
->arrayNode($this->getKey())
57+
->fixXmlConfig($this->getKey())
58+
->children()
59+
->scalarNode('claim')->defaultValue('sub')->end()
60+
->arrayNode('client')
61+
->isRequired()
62+
->beforeNormalization()
63+
->ifString()
64+
->then(static function ($v): array { return ['id' => $v]; })
65+
->end()
66+
->prototype('scalar')->end()
67+
->end()
68+
->end()
69+
->end()
70+
;
71+
}
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\AccessToken;
13+
14+
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
15+
use Symfony\Component\DependencyInjection\ContainerBuilder;
16+
17+
/**
18+
* TokenHandlerFactoryInterface is the interface for all token handler factories.
19+
*
20+
* @author Vincent Chalamon <vincentchalamon@gmail.com>
21+
*/
22+
interface TokenHandlerFactoryInterface
23+
{
24+
/**
25+
* Create a generic token handler service.
26+
*/
27+
public function create(ContainerBuilder $container, string $id, array|string $config): void;
28+
29+
/**
30+
* Get a generic token handler configuration key.
31+
*/
32+
public function getKey(): string;
33+
34+
/**
35+
* Add a generic token handler configuration.
36+
*/
37+
public function addConfiguration(NodeBuilder $node): void;
38+
}

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AccessTokenFactory.php

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111

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

14+
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\AccessToken\TokenHandlerFactoryInterface;
1415
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
16+
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
1517
use Symfony\Component\DependencyInjection\ChildDefinition;
1618
use Symfony\Component\DependencyInjection\ContainerBuilder;
1719
use Symfony\Component\DependencyInjection\Reference;
@@ -20,14 +22,18 @@
2022
* AccessTokenFactory creates services for Access Token authentication.
2123
*
2224
* @author Florent Morselli <florent.morselli@spomky-labs.com>
25+
* @author Vincent Chalamon <vincentchalamon@gmail.com>
2326
*
2427
* @internal
2528
*/
2629
final class AccessTokenFactory extends AbstractFactory
2730
{
2831
private const PRIORITY = -40;
2932

30-
public function __construct()
33+
/**
34+
* @param array<array-key, TokenHandlerFactoryInterface> $tokenHandlerFactories
35+
*/
36+
public function __construct(private readonly array $tokenHandlerFactories)
3137
{
3238
$this->options = [];
3339
$this->defaultFailureHandlerOptions = [];
@@ -39,7 +45,6 @@ public function addConfiguration(NodeDefinition $node): void
3945
$builder = $node->children();
4046

4147
$builder
42-
->scalarNode('token_handler')->isRequired()->end()
4348
->scalarNode('user_provider')->defaultNull()->end()
4449
->scalarNode('realm')->defaultNull()->end()
4550
->scalarNode('success_handler')->defaultNull()->end()
@@ -57,6 +62,38 @@ public function addConfiguration(NodeDefinition $node): void
5762
->scalarPrototype()->end()
5863
->end()
5964
;
65+
66+
$tokenHandlerNodeBuilder = $builder
67+
->arrayNode('token_handler')
68+
->example([
69+
'id' => 'App\Security\CustomTokenHandler',
70+
])
71+
72+
->beforeNormalization()
73+
->ifString()
74+
->then(static function (string $v): array { return ['id' => $v]; })
75+
->end()
76+
77+
->beforeNormalization()
78+
->ifTrue(static function ($v) { return \is_array($v) && 1 < \count($v); })
79+
->then(static function () { throw new InvalidConfigurationException('You cannot configure multiple token handlers.'); })
80+
->end()
81+
82+
// "isRequired" must be set otherwise the following custom validation is not called
83+
->isRequired()
84+
->beforeNormalization()
85+
->ifTrue(static function ($v) { return \is_array($v) && 1 > \count($v); })
86+
->then(static function () { throw new InvalidConfigurationException('You must set a token handler.'); })
87+
->end()
88+
89+
->children()
90+
;
91+
92+
foreach ($this->tokenHandlerFactories as $factory) {
93+
$factory->addConfiguration($tokenHandlerNodeBuilder);
94+
}
95+
96+
$tokenHandlerNodeBuilder->end();
6097
}
6198

6299
public function getPriority(): int
@@ -76,10 +113,11 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal
76113
$failureHandler = isset($config['failure_handler']) ? new Reference($this->createAuthenticationFailureHandler($container, $firewallName, $config)) : null;
77114
$authenticatorId = sprintf('security.authenticator.access_token.%s', $firewallName);
78115
$extractorId = $this->createExtractor($container, $firewallName, $config['token_extractors']);
116+
$tokenHandlerId = $this->createTokenHandler($container, $firewallName, $config['token_handler']);
79117

80118
$container
81119
->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.access_token'))
82-
->replaceArgument(0, new Reference($config['token_handler']))
120+
->replaceArgument(0, new Reference($tokenHandlerId))
83121
->replaceArgument(1, new Reference($extractorId))
84122
->replaceArgument(2, $userProvider)
85123
->replaceArgument(3, $successHandler)
@@ -115,4 +153,20 @@ private function createExtractor(ContainerBuilder $container, string $firewallNa
115153

116154
return $extractorId;
117155
}
156+
157+
private function createTokenHandler(ContainerBuilder $container, string $firewallName, array $config): string
158+
{
159+
$key = array_keys($config)[0];
160+
$id = sprintf('security.access_token_handler.%s', $firewallName);
161+
162+
foreach ($this->tokenHandlerFactories as $factory) {
163+
if ($key !== $factory->getKey()) {
164+
continue;
165+
}
166+
167+
$factory->create($container, $id, $config[$key]);
168+
}
169+
170+
return $id;
171+
}
118172
}

src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_access_token.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
abstract_arg('access token extractors'),
4343
])
4444

45+
// OIDC
4546
->set('security.access_token_handler.oidc_user_info', OidcUserInfoTokenHandler::class)
47+
->abstract()
4648
;
4749
};

src/Symfony/Bundle/SecurityBundle/SecurityBundle.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\RegisterTokenUsageTrackingPass;
2323
use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\ReplaceDecoratedRememberMeHandlerPass;
2424
use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\SortFirewallListenersPass;
25+
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\AccessToken\IdTokenHandlerFactory;
26+
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\AccessToken\OidcUserInfoTokenHandlerFactory;
2527
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AccessTokenFactory;
2628
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\CustomAuthenticatorFactory;
2729
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginFactory;
@@ -70,7 +72,10 @@ public function build(ContainerBuilder $container)
7072
$extension->addAuthenticatorFactory(new CustomAuthenticatorFactory());
7173
$extension->addAuthenticatorFactory(new LoginThrottlingFactory());
7274
$extension->addAuthenticatorFactory(new LoginLinkFactory());
73-
$extension->addAuthenticatorFactory(new AccessTokenFactory());
75+
$extension->addAuthenticatorFactory(new AccessTokenFactory([
76+
new IdTokenHandlerFactory(),
77+
new OidcUserInfoTokenHandlerFactory(),
78+
]));
7479

7580
$extension->addUserProviderFactory(new InMemoryFactory());
7681
$extension->addUserProviderFactory(new LdapFactory());

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy