Skip to content

Commit 1ab4da9

Browse files
mtarldchalasr
authored andcommitted
Allow user to use his own Client entity
1 parent 705f7a9 commit 1ab4da9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+681
-423
lines changed

.psalm.baseline.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<files psalm-version="4.7.1@cd53e047a58f71f646dd6bf45476076ab07b5d44">
3+
<file src="src/Persistence/Mapping/Driver.php">
4+
<ArgumentTypeCoercion occurrences="5">
5+
<code>$metadata</code>
6+
<code>$metadata</code>
7+
<code>$metadata</code>
8+
<code>$metadata</code>
9+
<code>$metadata</code>
10+
</ArgumentTypeCoercion>
11+
</file>
312
<file src="src/Resources/config/routes.php">
413
<InvalidArgument occurrences="2">
514
<code>['league.oauth2_server.controller.authorization', 'indexAction']</code>

docs/basic-setup.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Usage:
1616
league:oauth2-server:create-client [options] [--] [<identifier> [<secret>]]
1717

1818
Arguments:
19+
name The client name
1920
identifier The client identifier
2021
secret The client secret
2122

@@ -46,6 +47,7 @@ Options:
4647
--redirect-uri[=REDIRECT-URI] Sets redirect uri for client. Use this option multiple times to set multiple redirect URIs. (multiple values allowed)
4748
--grant-type[=GRANT-TYPE] Sets allowed grant type for client. Use this option multiple times to set multiple grant types. (multiple values allowed)
4849
--scope[=SCOPE] Sets allowed scope for client. Use this option multiple times to set multiple scopes. (multiple values allowed)
50+
--name=[=NAME] Sets name for client.
4951
--deactivated If provided, it will deactivate the given client.
5052
```
5153

docs/index.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ For implementation into Symfony projects, please see [bundle documentation](docs
9393
9494
# Set a custom prefix that replaces the default 'ROLE_OAUTH2_' role prefix
9595
role_prefix: ROLE_OAUTH2_
96+
97+
client:
98+
# Set a custom client class. Must be a League\Bundle\OAuth2ServerBundle\Model\Client
99+
classname: League\Bundle\OAuth2ServerBundle\Model\Client
96100
```
97101

98102
1. Enable the bundle in `config/bundles.php` by adding it to the array:
@@ -138,6 +142,7 @@ security:
138142
* [Basic setup](basic-setup.md)
139143
* [Controlling token scopes](controlling-token-scopes.md)
140144
* [Implementing custom grant type](implementing-custom-grant-type.md)
145+
* [Using custom client](using-custom-client.md)
141146
142147
## Contributing
143148

docs/using-custom-client.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Using custom client
2+
3+
1. Create a class that extends the `\League\Bundle\OAuth2ServerBundle\Model\AbstractClient` class.
4+
5+
Example:
6+
7+
```php
8+
<?php
9+
10+
declare(strict_types=1);
11+
12+
namespace App\Entity;
13+
14+
use Doctrine\ORM\Mapping as ORM;
15+
use League\Bundle\OAuth2ServerBundle\Model\AbstractClient;
16+
17+
/**
18+
* @ORM\Entity
19+
*/
20+
class Client extends AbstractClient
21+
{
22+
/**
23+
* @ORM\Column(type="string")
24+
*/
25+
private $image;
26+
27+
// other properties, getters, setters, ...
28+
}
29+
```
30+
31+
2. In order to use the new client instead of `League\Bundle\OAuth2ServerBundle\Model\Client`, edit the configuration like the following:
32+
33+
```yaml
34+
league_oauth2_server:
35+
client:
36+
classname: App\Entity\Client
37+
```

src/Command/CreateClientCommand.php

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace League\Bundle\OAuth2ServerBundle\Command;
66

77
use League\Bundle\OAuth2ServerBundle\Manager\ClientManagerInterface;
8-
use League\Bundle\OAuth2ServerBundle\Model\Client;
8+
use League\Bundle\OAuth2ServerBundle\Model\AbstractClient;
99
use League\Bundle\OAuth2ServerBundle\Model\Grant;
1010
use League\Bundle\OAuth2ServerBundle\Model\RedirectUri;
1111
use League\Bundle\OAuth2ServerBundle\Model\Scope;
@@ -25,11 +25,17 @@ final class CreateClientCommand extends Command
2525
*/
2626
private $clientManager;
2727

28-
public function __construct(ClientManagerInterface $clientManager)
28+
/**
29+
* @var string
30+
*/
31+
private $clientFqcn;
32+
33+
public function __construct(ClientManagerInterface $clientManager, string $clientFqcn)
2934
{
3035
parent::__construct();
3136

3237
$this->clientManager = $clientManager;
38+
$this->clientFqcn = $clientFqcn;
3339
}
3440

3541
protected function configure(): void
@@ -57,6 +63,18 @@ protected function configure(): void
5763
'Sets allowed scope for client. Use this option multiple times to set multiple scopes.',
5864
[]
5965
)
66+
->addOption(
67+
'public',
68+
null,
69+
InputOption::VALUE_NONE,
70+
'Create a public client.'
71+
)
72+
->addOption(
73+
'allow-plain-text-pkce',
74+
null,
75+
InputOption::VALUE_NONE,
76+
'Create a client who is allowed to use plain challenge method for PKCE.'
77+
)
6078
->addArgument(
6179
'name',
6280
InputArgument::REQUIRED,
@@ -72,18 +90,6 @@ protected function configure(): void
7290
InputArgument::OPTIONAL,
7391
'The client secret'
7492
)
75-
->addOption(
76-
'public',
77-
null,
78-
InputOption::VALUE_NONE,
79-
'Create a public client.'
80-
)
81-
->addOption(
82-
'allow-plain-text-pkce',
83-
null,
84-
InputOption::VALUE_NONE,
85-
'Create a client who is allowed to use plain challenge method for PKCE.'
86-
)
8793
;
8894
}
8995

@@ -100,7 +106,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
100106
}
101107

102108
$this->clientManager->save($client);
103-
$io->success('New oAuth2 client created successfully.');
109+
$io->success('New OAuth2 client created successfully.');
104110

105111
$headers = ['Identifier', 'Secret'];
106112
$rows = [
@@ -111,9 +117,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int
111117
return 0;
112118
}
113119

114-
private function buildClientFromInput(InputInterface $input): Client
120+
private function buildClientFromInput(InputInterface $input): AbstractClient
115121
{
116122
$name = $input->getArgument('name');
123+
117124
/** @var string $identifier */
118125
$identifier = $input->getArgument('identifier') ?? hash('md5', random_bytes(16));
119126

@@ -126,7 +133,8 @@ private function buildClientFromInput(InputInterface $input): Client
126133
/** @var string $secret */
127134
$secret = $isPublic ? null : $input->getArgument('secret') ?? hash('sha512', random_bytes(32));
128135

129-
$client = new Client($name, $identifier, $secret);
136+
/** @var AbstractClient $client */
137+
$client = new $this->clientFqcn($name, $identifier, $secret);
130138
$client->setActive(true);
131139
$client->setAllowPlainTextPkce($input->getOption('allow-plain-text-pkce'));
132140

src/Command/DeleteClientCommand.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public function __construct(ClientManagerInterface $clientManager)
3030
protected function configure(): void
3131
{
3232
$this
33-
->setDescription('Deletes an oAuth2 client')
33+
->setDescription('Deletes an OAuth2 client')
3434
->addArgument(
3535
'identifier',
3636
InputArgument::REQUIRED,
@@ -42,15 +42,15 @@ protected function configure(): void
4242
protected function execute(InputInterface $input, OutputInterface $output): int
4343
{
4444
$io = new SymfonyStyle($input, $output);
45-
$identifier = $input->getArgument('identifier');
46-
$client = $this->clientManager->find($identifier);
47-
if (null === $client) {
48-
$io->error(sprintf('oAuth2 client identified as "%s" does not exist', $identifier));
45+
46+
if (null === $client = $this->clientManager->find($input->getArgument('identifier'))) {
47+
$io->error(sprintf('OAuth2 client identified as "%s" does not exist.', $input->getArgument('identifier')));
4948

5049
return 1;
5150
}
51+
5252
$this->clientManager->remove($client);
53-
$io->success('Given oAuth2 client deleted successfully.');
53+
$io->success('OAuth2 client deleted successfully.');
5454

5555
return 0;
5656
}

src/Command/ListClientsCommand.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use League\Bundle\OAuth2ServerBundle\Manager\ClientFilter;
88
use League\Bundle\OAuth2ServerBundle\Manager\ClientManagerInterface;
9-
use League\Bundle\OAuth2ServerBundle\Model\Client;
9+
use League\Bundle\OAuth2ServerBundle\Model\AbstractClient;
1010
use League\Bundle\OAuth2ServerBundle\Model\Grant;
1111
use League\Bundle\OAuth2ServerBundle\Model\RedirectUri;
1212
use League\Bundle\OAuth2ServerBundle\Model\Scope;
@@ -18,7 +18,7 @@
1818

1919
final class ListClientsCommand extends Command
2020
{
21-
private const ALLOWED_COLUMNS = ['identifier', 'secret', 'scope', 'redirect uri', 'grant type'];
21+
private const ALLOWED_COLUMNS = ['name', 'identifier', 'secret', 'scope', 'redirect uri', 'grant type'];
2222

2323
protected static $defaultName = 'league:oauth2-server:list-clients';
2424

@@ -112,8 +112,9 @@ private function drawTable(InputInterface $input, OutputInterface $output, array
112112

113113
private function getRows(array $clients, array $columns): array
114114
{
115-
return array_map(static function (Client $client) use ($columns): array {
115+
return array_map(static function (AbstractClient $client) use ($columns): array {
116116
$values = [
117+
'name' => $client->getName(),
117118
'identifier' => $client->getIdentifier(),
118119
'secret' => $client->getSecret(),
119120
'scope' => implode(', ', $client->getScopes()),

src/Command/UpdateClientCommand.php

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace League\Bundle\OAuth2ServerBundle\Command;
66

77
use League\Bundle\OAuth2ServerBundle\Manager\ClientManagerInterface;
8-
use League\Bundle\OAuth2ServerBundle\Model\Client;
8+
use League\Bundle\OAuth2ServerBundle\Model\AbstractClient;
99
use League\Bundle\OAuth2ServerBundle\Model\Grant;
1010
use League\Bundle\OAuth2ServerBundle\Model\RedirectUri;
1111
use League\Bundle\OAuth2ServerBundle\Model\Scope;
@@ -35,7 +35,7 @@ public function __construct(ClientManagerInterface $clientManager)
3535
protected function configure(): void
3636
{
3737
$this
38-
->setDescription('Updates an oAuth2 client')
38+
->setDescription('Updates an OAuth2 client')
3939
->addOption(
4040
'redirect-uri',
4141
null,
@@ -57,6 +57,13 @@ protected function configure(): void
5757
'Sets allowed scope for client. Use this option multiple times to set multiple scopes.',
5858
[]
5959
)
60+
->addOption(
61+
'name',
62+
null,
63+
InputOption::VALUE_REQUIRED,
64+
'Sets name for client.',
65+
[]
66+
)
6067
->addOption(
6168
'deactivated',
6269
null,
@@ -76,19 +83,20 @@ protected function execute(InputInterface $input, OutputInterface $output): int
7683
$io = new SymfonyStyle($input, $output);
7784

7885
if (null === $client = $this->clientManager->find($input->getArgument('identifier'))) {
79-
$io->error(sprintf('oAuth2 client identified as "%s"', $input->getArgument('identifier')));
86+
$io->error(sprintf('OAuth2 client identified as "%s" does not exist.', $input->getArgument('identifier')));
8087

8188
return 1;
8289
}
8390

8491
$client = $this->updateClientFromInput($client, $input);
8592
$this->clientManager->save($client);
86-
$io->success('Given oAuth2 client updated successfully.');
93+
94+
$io->success('OAuth2 client updated successfully.');
8795

8896
return 0;
8997
}
9098

91-
private function updateClientFromInput(Client $client, InputInterface $input): Client
99+
private function updateClientFromInput(AbstractClient $client, InputInterface $input): AbstractClient
92100
{
93101
$client->setActive(!$input->getOption('deactivated'));
94102

@@ -99,7 +107,7 @@ private function updateClientFromInput(Client $client, InputInterface $input): C
99107
/** @var list<string> $scopeStrings */
100108
$scopeStrings = $input->getOption('scope');
101109

102-
return $client
110+
$client
103111
->setRedirectUris(...array_map(static function (string $redirectUri): RedirectUri {
104112
return new RedirectUri($redirectUri);
105113
}, $redirectUriStrings))
@@ -110,5 +118,11 @@ private function updateClientFromInput(Client $client, InputInterface $input): C
110118
return new Scope($scope);
111119
}, $scopeStrings))
112120
;
121+
122+
if ($name = $input->getOption('name')) {
123+
$client->setName($name);
124+
}
125+
126+
return $client;
113127
}
114128
}

src/Controller/AuthorizationController.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
use League\Bundle\OAuth2ServerBundle\Event\AuthorizationRequestResolveEvent;
99
use League\Bundle\OAuth2ServerBundle\Event\AuthorizationRequestResolveEventFactory;
1010
use League\Bundle\OAuth2ServerBundle\Manager\ClientManagerInterface;
11-
use League\Bundle\OAuth2ServerBundle\Model\Client;
11+
use League\Bundle\OAuth2ServerBundle\Model\AbstractClient;
1212
use League\Bundle\OAuth2ServerBundle\OAuth2Events;
1313
use League\OAuth2\Server\AuthorizationServer;
1414
use League\OAuth2\Server\Exception\OAuthServerException;
@@ -90,7 +90,7 @@ public function indexAction(Request $request): Response
9090
$authRequest = $this->server->validateAuthorizationRequest($serverRequest);
9191

9292
if ('plain' === $authRequest->getCodeChallengeMethod()) {
93-
/** @var Client $client */
93+
/** @var AbstractClient $client */
9494
$client = $this->clientManager->find($authRequest->getClient()->getIdentifier());
9595
if (!$client->isPlainTextPkceAllowed()) {
9696
throw OAuthServerException::invalidRequest('code_challenge_method', 'Plain code challenge method is not allowed for this client');
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace League\Bundle\OAuth2ServerBundle\DependencyInjection\CompilerPass;
6+
7+
use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass;
8+
use League\Bundle\OAuth2ServerBundle\Persistence\Mapping\Driver;
9+
use Symfony\Component\DependencyInjection\Reference;
10+
11+
/**
12+
* @author Mathias Arlaud <[email protected]>
13+
*/
14+
class RegisterDoctrineOrmMappingPass extends DoctrineOrmMappingsPass
15+
{
16+
public function __construct()
17+
{
18+
parent::__construct(
19+
new Reference(Driver::class),
20+
['League\Bundle\OAuth2ServerBundle\Model'],
21+
['league.oauth2_server.persistence.doctrine.manager'],
22+
'league.oauth2_server.persistence.doctrine.enabled',
23+
['LeagueOAuth2ServerBundle' => 'League\Bundle\OAuth2ServerBundle\Model']
24+
);
25+
}
26+
}

0 commit comments

Comments
 (0)