Skip to content
This repository was archived by the owner on Aug 7, 2023. It is now read-only.

Commit 8b6135d

Browse files
Merge pull request #4 from huyta888/add-support-symfony-6
Add support symfony 6
2 parents 99cd228 + 2c270ca commit 8b6135d

14 files changed

+250
-168
lines changed

CHANGELOG.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Changelog
2+
3+
## 1.0.2
4+
5+
* Fix: add child node `rules` array config to avoid empty array when using arrayPrototype.
6+
7+
## 1.0.1
8+
9+
* Fix: auto add authenticator when not config.

README.md

+16-12
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,13 @@ security:
5555
main:
5656
stateless: true
5757
istio_jwt_authenticator:
58-
- issuer: issuer_1 # Required
59-
user_identifier_claim: sub #Default is `sub` claim
60-
origin_token_headers: [authorization] #Required at least once of `origin_token_headers`, `origin_token_query_params` or `base64_headers`. Use this option when your Istio JWTRule CRD using `forwardOriginalToken`.
61-
origin_token_query_params: [token] #Use this option when your Istio JWTRule CRD using `forwardOriginalToken` and your JWT token in query param.
62-
base64_headers: [x-istio-jwt-payload] # Use this option when your Istio JWTRule CRD using `outputPayloadToHeader`.
58+
rules:
59+
- issuer: issuer_1 # Required
60+
user_identifier_claim: sub #Default is `sub` claim
61+
origin_token_headers: [authorization] #Required at least once of `origin_token_headers`, `origin_token_query_params` or `base64_headers`. Use this option when your Istio JWTRule CRD using `forwardOriginalToken`.
62+
origin_token_query_params: [token] #Use this option when your Istio JWTRule CRD using `forwardOriginalToken` and your JWT token in query param.
63+
base64_headers: [x-istio-jwt-payload] # Use this option when your Istio JWTRule CRD using `outputPayloadToHeader`.
64+
prefix: "Bearer " #Token prefix of origin token passthrough by default blank ("") if not set.
6365
```
6466
6567
In case your application have multi issuers:
@@ -69,11 +71,13 @@ In case your application have multi issuers:
6971
main:
7072
stateless: true
7173
istio_jwt_authenticator:
72-
- issuer: issuer_1
73-
origin_token_headers: [authorization]
74-
- issuer: issuer_2
75-
user_identifier_claim: aud
76-
base64_headers: [x-istio-jwt-payload]
74+
rules:
75+
- issuer: issuer_1
76+
origin_token_headers: [authorization]
77+
prefix: "Bearer "
78+
- issuer: issuer_2
79+
user_identifier_claim: aud
80+
base64_headers: [x-istio-jwt-payload]
7781
#....
7882
```
7983

@@ -82,15 +86,15 @@ In case your application have multi issuers:
8286
```shell
8387
#!/bin/bash
8488

85-
# Generate mock JWT token forwarded by Istio sidecar
89+
#Generate mock JWT token forwarded by Istio sidecar
8690

8791
payload='{"issuer":"issuer_1", "sub": "test"}';
8892
base64_payload=$(echo -n $payload | base64 -);
8993
origin_token=$(echo "header.$base64_payload.signature");
9094

9195
#You can test authenticate origin token with curl:
9296

93-
curl -H "Authorization: $origin_token" http://localhost/
97+
curl -H "Authorization: Bearer $origin_token" http://localhost/
9498

9599
#Or authenticate base64 payload header:
96100

composer.json

+6-6
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
"minimum-stability": "stable",
1616
"require": {
1717
"php": ">=8.0",
18-
"php-istio/jwt-payload-extractor": "^1.0",
18+
"php-istio/jwt-payload-extractor": "^v1.1.1",
1919
"symfony/psr7-pack": "^1.0",
20-
"symfony/security-bundle": "^5.3"
20+
"symfony/security-bundle": "^6.0"
2121
},
2222
"autoload": {
2323
"psr-4": {
@@ -30,9 +30,9 @@
3030
}
3131
},
3232
"require-dev": {
33-
"symfony/browser-kit": "^5.3",
34-
"symfony/console": "^5.3",
35-
"symfony/framework-bundle": "^5.3",
36-
"symfony/phpunit-bridge": "^5.3"
33+
"symfony/browser-kit": "^6.0",
34+
"symfony/console": "^6.0",
35+
"symfony/framework-bundle": "^6.0",
36+
"symfony/phpunit-bridge": "^6.0"
3737
}
3838
}

src/Authenticator/Authenticator.php

+5-5
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
use Symfony\Component\Security\Core\User\UserProviderInterface;
2020
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
2121
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
22-
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
22+
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
2323
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
2424
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
2525

@@ -56,7 +56,7 @@ public function supports(Request $request): ?bool
5656
return false;
5757
}
5858

59-
public function authenticate(Request $request): PassportInterface
59+
public function authenticate(Request $request): Passport
6060
{
6161
[$userIdentifierClaim, $payload] = $request->attributes->get('_user_identifier_claim_and_payload');
6262
$request->attributes->remove('_user_identifier_claim_and_payload');
@@ -88,17 +88,17 @@ public function onAuthenticationFailure(Request $request, AuthenticationExceptio
8888
throw $exception;
8989
}
9090

91-
public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface
91+
public function createToken(Passport $passport, string $firewallName): TokenInterface
9292
{
9393
/** @var SelfValidatingPassport $passport */
9494
$payload = $passport->getAttribute('_payload');
95-
$token = parent::createAuthenticatedToken($passport, $firewallName);
95+
$token = parent::createToken($passport, $firewallName);
9696
$token->setAttribute('jwt_payload', $payload);
9797

9898
return $token;
9999
}
100100

101-
public function start(Request $request, AuthenticationException $authException = null)
101+
public function start(Request $request, AuthenticationException $authException = null): Response
102102
{
103103
return new Response('Istio JWT in request\'s missing or invalid.', Response::HTTP_UNAUTHORIZED);
104104
}

src/DependencyInjection/Security/AuthenticatorFactory.php

+75-59
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
use Istio\Symfony\JWTAuthentication\Authenticator\UserIdentifierClaimMapping;
1414
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AuthenticatorFactoryInterface;
15-
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
1615
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
1716
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
1817
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
@@ -21,74 +20,78 @@
2120
use Symfony\Component\DependencyInjection\Definition;
2221
use Symfony\Component\DependencyInjection\Reference;
2322

24-
final class AuthenticatorFactory implements SecurityFactoryInterface, AuthenticatorFactoryInterface
23+
final class AuthenticatorFactory implements AuthenticatorFactoryInterface
2524
{
25+
public const PRIORITY = -40;
26+
2627
public function createAuthenticator(
2728
ContainerBuilder $container,
2829
string $firewallName,
2930
array $config,
3031
string $userProviderId
31-
) {
32+
): string|array {
3233
$authenticator = sprintf('security.authenticator.istio_jwt_authenticator.%s', $firewallName);
3334
$definition = new ChildDefinition('istio.jwt_authentication.authenticator');
34-
$definition->replaceArgument(0, $this->createUserIdentifierClaimMappings($container, $authenticator, $config));
35+
$definition->replaceArgument(
36+
0,
37+
$this->createUserIdentifierClaimMappings($container, $authenticator, $config['rules'])
38+
);
3539
$definition->replaceArgument(1, new Reference($userProviderId));
3640
$container->setDefinition($authenticator, $definition);
3741

3842
return $authenticator;
3943
}
4044

41-
public function create(
42-
ContainerBuilder $container,
43-
string $id,
44-
array $config,
45-
string $userProviderId,
46-
?string $defaultEntryPointId
47-
) {
48-
throw new \LogicException('Istio JWT Authentication is not supported when "security.enable_authenticator_manager" is not set to true.');
49-
}
50-
5145
public function getPosition()
5246
{
5347
return 'pre_auth';
5448
}
5549

56-
public function getKey()
50+
public function getKey(): string
5751
{
5852
return 'istio_jwt_authenticator';
5953
}
6054

6155
public function addConfiguration(NodeDefinition $builder)
6256
{
6357
$builder
64-
->cannotBeEmpty()
65-
->fixXmlConfig('origin_token_header')
66-
->fixXmlConfig('origin_token_query_param')
67-
->fixXmlConfig('base64_header')
68-
->arrayPrototype()
69-
->addDefaultsIfNotSet()
70-
->children()
71-
->scalarNode('issuer')
72-
->cannotBeEmpty()
73-
->isRequired()
74-
->end()
75-
->scalarNode('user_identifier_claim')
76-
->cannotBeEmpty()
77-
->defaultValue('sub')
78-
->end()
79-
->arrayNode('origin_token_headers')
80-
->scalarPrototype()
81-
->cannotBeEmpty()
82-
->end()
83-
->end()
84-
->arrayNode('origin_token_query_params')
85-
->scalarPrototype()
86-
->cannotBeEmpty()
87-
->end()
88-
->end()
89-
->arrayNode('base64_headers')
90-
->scalarPrototype()
91-
->cannotBeEmpty()
58+
->fixXmlConfig('rule')
59+
->children()
60+
->arrayNode('rules')
61+
->isRequired()
62+
->cannotBeEmpty()
63+
->arrayPrototype()
64+
->fixXmlConfig('origin_token_header')
65+
->fixXmlConfig('origin_token_query_param')
66+
->fixXmlConfig('base64_header')
67+
->addDefaultsIfNotSet()
68+
->children()
69+
->scalarNode('issuer')
70+
->isRequired()
71+
->cannotBeEmpty()
72+
->end()
73+
->scalarNode('user_identifier_claim')
74+
->cannotBeEmpty()
75+
->defaultValue('sub')
76+
->end()
77+
->arrayNode('origin_token_headers')
78+
->scalarPrototype()
79+
->cannotBeEmpty()
80+
->end()
81+
->end()
82+
->arrayNode('origin_token_query_params')
83+
->scalarPrototype()
84+
->cannotBeEmpty()
85+
->end()
86+
->end()
87+
->arrayNode('base64_headers')
88+
->scalarPrototype()
89+
->cannotBeEmpty()
90+
->end()
91+
->end()
92+
->scalarNode('prefix')
93+
->defaultNull()
94+
->end()
9295
->end()
9396
->end()
9497
->end()
@@ -98,53 +101,55 @@ public function addConfiguration(NodeDefinition $builder)
98101

99102
private function createUserIdentifierClaimMappings(
100103
ContainerBuilder $container,
101-
string $authenticatorName,
102-
array $config,
104+
string $authenticatorId,
105+
array $rules,
103106
): IteratorArgument {
104-
$extractorIdPrefix = sprintf('%s.payload_extractor', $authenticatorName);
107+
$extractorIdPrefix = sprintf('%s.payload_extractor', $authenticatorId);
105108
$mappings = [];
106109

107-
foreach ($config as $key => $item) {
110+
foreach ($rules as $key => $rule) {
108111
$extractor = null;
109112

110-
if (!empty($item['origin_token_headers'])) {
113+
if (!empty($rule['origin_token_headers'])) {
111114
$extractor = $this->createPayloadExtractor(
112115
$container,
113116
sprintf('%s.origin_token_headers.%s', $extractorIdPrefix, $key),
114117
'istio.jwt_authentication.payload_extractor.origin_token.header',
115-
$item['issuer'],
116-
$item['origin_token_headers']
118+
$rule['issuer'],
119+
$rule['origin_token_headers'],
120+
$rule['prefix']
117121
);
118122
}
119123

120-
if (!empty($item['origin_token_query_params'])) {
124+
if (!empty($rule['origin_token_query_params'])) {
121125
$extractor = $this->createPayloadExtractor(
122126
$container,
123127
sprintf('%s.origin_token_query_params.%s', $extractorIdPrefix, $key),
124128
'istio.jwt_authentication.payload_extractor.origin_token.query_param',
125-
$item['issuer'],
126-
$item['origin_token_query_params']
129+
$rule['issuer'],
130+
$rule['origin_token_query_params'],
131+
$rule['prefix']
127132
);
128133
}
129134

130-
if (!empty($item['base64_headers'])) {
135+
if (!empty($rule['base64_headers'])) {
131136
$extractor = $this->createPayloadExtractor(
132137
$container,
133138
sprintf('%s.base64_headers.%s', $extractorIdPrefix, $key),
134139
'istio.jwt_authentication.payload_extractor.base64_header',
135-
$item['issuer'],
136-
$item['base64_headers']
140+
$rule['issuer'],
141+
$rule['base64_headers']
137142
);
138143
}
139144

140145
if (null === $extractor) {
141146
throw new InvalidConfigurationException(sprintf('`%s`: at least once `origin_token_headers`, `origin_token_query_params`, `base64_headers` should be config when using', $this->getKey()));
142147
}
143148

144-
$mappingId = sprintf('%s.user_identifier_claim_mapping.%s', $authenticatorName, $key);
149+
$mappingId = sprintf('%s.user_identifier_claim_mapping.%s', $authenticatorId, $key);
145150
$mappings[] = new Reference($mappingId);
146151
$mappingDefinition = new Definition(UserIdentifierClaimMapping::class);
147-
$mappingDefinition->setArgument(0, $item['user_identifier_claim']);
152+
$mappingDefinition->setArgument(0, $rule['user_identifier_claim']);
148153
$mappingDefinition->setArgument(1, $extractor);
149154
$container->setDefinition($mappingId, $mappingDefinition);
150155
}
@@ -157,7 +162,8 @@ private function createPayloadExtractor(
157162
string $id,
158163
string $fromAbstractId,
159164
string $issuer,
160-
array $items
165+
array $items,
166+
?string $prefix = null
161167
): Reference {
162168
$definition = new ChildDefinition('istio.jwt_authentication.payload_extractor.composite');
163169
$container->setDefinition($id, $definition);
@@ -170,11 +176,21 @@ private function createPayloadExtractor(
170176
$subDefinition = new ChildDefinition($fromAbstractId);
171177
$subDefinition->replaceArgument(0, $issuer);
172178
$subDefinition->replaceArgument(1, $item);
179+
180+
if (null !== $prefix) {
181+
$subDefinition->replaceArgument(2, $prefix);
182+
}
183+
173184
$container->setDefinition($subId, $subDefinition);
174185
}
175186

176187
$definition->setArguments($subExtractors);
177188

178189
return new Reference($id);
179190
}
191+
192+
public function getPriority(): int
193+
{
194+
return self::PRIORITY;
195+
}
180196
}

src/JWTAuthenticationBundle.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ public function build(ContainerBuilder $container)
2525
/** @var SecurityExtension $extension */
2626
$extension = $container->getExtension('security');
2727
$extension->addUserProviderFactory(new StatelessUserProviderFactory());
28-
$extension->addSecurityListenerFactory(new AuthenticatorFactory());
28+
$extension->addAuthenticatorFactory(new AuthenticatorFactory());
2929
}
3030
}

src/Resources/config/payload_extractor.php

+2
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@
2626
->factory([ExtractorFactory::class, 'fromOriginTokenHeader'])
2727
->arg(0, abstract_arg('issuer'))
2828
->arg(1, abstract_arg('header name'))
29+
->arg(2, '') // token prefix
2930

3031
->set('istio.jwt_authentication.payload_extractor.origin_token.query_param', OriginTokenExtractor::class)
3132
->abstract()
3233
->factory([ExtractorFactory::class, 'fromOriginTokenQueryParam'])
3334
->arg(0, abstract_arg('issuer'))
3435
->arg(1, abstract_arg('param name'))
36+
->arg(2, '') // token prefix
3537

3638
->set('istio.jwt_authentication.payload_extractor.base64_header', Base64HeaderExtractor::class)
3739
->abstract()

src/User/JWTPayloadAwareUserProviderInterface.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010

1111
namespace Istio\Symfony\JWTAuthentication\User;
1212

13+
use Symfony\Component\Security\Core\User\UserInterface;
1314
use Symfony\Component\Security\Core\User\UserProviderInterface;
1415

1516
interface JWTPayloadAwareUserProviderInterface extends UserProviderInterface
1617
{
17-
public function loadUserByIdentifier(string $identifier, array $payload = null);
18+
public function loadUserByIdentifier(string $identifier, array $payload = null): UserInterface;
1819
}

0 commit comments

Comments
 (0)