Skip to content

Commit 904d57f

Browse files
authored
Merge pull request #2 from chalasr/readd-deprecated-grants
Adding back deprecated password & implicit grants
2 parents a343efe + 2169523 commit 904d57f

File tree

13 files changed

+604
-2
lines changed

13 files changed

+604
-2
lines changed

src/DependencyInjection/Configuration.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ private function createAuthorizationServerNode(): NodeDefinition
8787
->info('Whether to enable the client credentials grant')
8888
->defaultTrue()
8989
->end()
90+
->booleanNode('enable_password_grant')
91+
->info('Whether to enable the password grant')
92+
->defaultTrue()
93+
->end()
9094
->booleanNode('enable_refresh_token_grant')
9195
->info('Whether to enable the refresh token grant')
9296
->defaultTrue()
@@ -99,6 +103,10 @@ private function createAuthorizationServerNode(): NodeDefinition
99103
->info('Whether to require code challenge for public clients for the auth code grant')
100104
->defaultTrue()
101105
->end()
106+
->booleanNode('enable_implicit_grant')
107+
->info('Whether to enable the implicit grant')
108+
->defaultTrue()
109+
->end()
102110
->end()
103111
;
104112

src/DependencyInjection/LeagueOAuth2ServerExtension.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
use League\OAuth2\Server\CryptKey;
2626
use League\OAuth2\Server\Grant\AuthCodeGrant;
2727
use League\OAuth2\Server\Grant\ClientCredentialsGrant;
28+
use League\OAuth2\Server\Grant\ImplicitGrant;
29+
use League\OAuth2\Server\Grant\PasswordGrant;
2830
use League\OAuth2\Server\Grant\RefreshTokenGrant;
2931
use League\OAuth2\Server\ResourceServer;
3032
use LogicException;
@@ -155,6 +157,13 @@ private function configureAuthorizationServer(ContainerBuilder $container, array
155157
]);
156158
}
157159

160+
if ($config['enable_password_grant']) {
161+
$authorizationServer->addMethodCall('enableGrantType', [
162+
new Reference(PasswordGrant::class),
163+
new Definition(DateInterval::class, [$config['access_token_ttl']]),
164+
]);
165+
}
166+
158167
if ($config['enable_refresh_token_grant']) {
159168
$authorizationServer->addMethodCall('enableGrantType', [
160169
new Reference(RefreshTokenGrant::class),
@@ -169,11 +178,25 @@ private function configureAuthorizationServer(ContainerBuilder $container, array
169178
]);
170179
}
171180

181+
if ($config['enable_implicit_grant']) {
182+
$authorizationServer->addMethodCall('enableGrantType', [
183+
new Reference(ImplicitGrant::class),
184+
new Definition(DateInterval::class, [$config['access_token_ttl']]),
185+
]);
186+
}
187+
172188
$this->configureGrants($container, $config);
173189
}
174190

175191
private function configureGrants(ContainerBuilder $container, array $config): void
176192
{
193+
$container
194+
->getDefinition(PasswordGrant::class)
195+
->addMethodCall('setRefreshTokenTTL', [
196+
new Definition(DateInterval::class, [$config['refresh_token_ttl']]),
197+
])
198+
;
199+
177200
$container
178201
->getDefinition(RefreshTokenGrant::class)
179202
->addMethodCall('setRefreshTokenTTL', [
@@ -191,6 +214,11 @@ private function configureGrants(ContainerBuilder $container, array $config): vo
191214
if (false === $config['require_code_challenge_for_public_clients']) {
192215
$authCodeGrantDefinition->addMethodCall('disableRequireCodeChallengeForPublicClients');
193216
}
217+
218+
$container
219+
->getDefinition(ImplicitGrant::class)
220+
->replaceArgument('$accessTokenTTL', new Definition(DateInterval::class, [$config['access_token_ttl']]))
221+
;
194222
}
195223

196224
/**

src/Event/UserResolveEvent.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace League\Bundle\OAuth2ServerBundle\Event;
6+
7+
use League\Bundle\OAuth2ServerBundle\Model\Client;
8+
use League\Bundle\OAuth2ServerBundle\Model\Grant;
9+
use Symfony\Component\Security\Core\User\UserInterface;
10+
use Symfony\Contracts\EventDispatcher\Event;
11+
12+
final class UserResolveEvent extends Event
13+
{
14+
/**
15+
* @var string
16+
*/
17+
private $username;
18+
19+
/**
20+
* @var string
21+
*/
22+
private $password;
23+
24+
/**
25+
* @var Grant
26+
*/
27+
private $grant;
28+
29+
/**
30+
* @var Client
31+
*/
32+
private $client;
33+
34+
/**
35+
* @var UserInterface|null
36+
*/
37+
private $user;
38+
39+
public function __construct(string $username, string $password, Grant $grant, Client $client)
40+
{
41+
$this->username = $username;
42+
$this->password = $password;
43+
$this->grant = $grant;
44+
$this->client = $client;
45+
}
46+
47+
public function getUsername(): string
48+
{
49+
return $this->username;
50+
}
51+
52+
public function getPassword(): string
53+
{
54+
return $this->password;
55+
}
56+
57+
public function getGrant(): Grant
58+
{
59+
return $this->grant;
60+
}
61+
62+
public function getClient(): Client
63+
{
64+
return $this->client;
65+
}
66+
67+
public function getUser(): ?UserInterface
68+
{
69+
return $this->user;
70+
}
71+
72+
public function setUser(?UserInterface $user): self
73+
{
74+
$this->user = $user;
75+
76+
return $this;
77+
}
78+
}

src/League/Repository/ScopeRepository.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use League\Bundle\OAuth2ServerBundle\Model\Client as ClientModel;
1212
use League\Bundle\OAuth2ServerBundle\Model\Grant as GrantModel;
1313
use League\Bundle\OAuth2ServerBundle\Model\Scope as ScopeModel;
14+
use League\Bundle\OAuth2ServerBundle\OAuth2Events;
1415
use League\OAuth2\Server\Entities\ClientEntityInterface;
1516
use League\OAuth2\Server\Exception\OAuthServerException;
1617
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
@@ -78,7 +79,13 @@ public function finalizeScopes(
7879
$scopes = $this->setupScopes($client, $this->scopeConverter->toDomainArray($scopes));
7980

8081
$event = $this->eventDispatcher->dispatch(
81-
new ScopeResolveEvent($scopes, new GrantModel($grantType), $client, $userIdentifier)
82+
new ScopeResolveEvent(
83+
$scopes,
84+
new GrantModel($grantType),
85+
$client,
86+
$userIdentifier
87+
),
88+
OAuth2Events::SCOPE_RESOLVE
8289
);
8390

8491
return $this->scopeConverter->toLeagueArray($event->getScopes());
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace League\Bundle\OAuth2ServerBundle\League\Repository;
6+
7+
use League\Bundle\OAuth2ServerBundle\Converter\UserConverterInterface;
8+
use League\Bundle\OAuth2ServerBundle\Event\UserResolveEvent;
9+
use League\Bundle\OAuth2ServerBundle\Manager\ClientManagerInterface;
10+
use League\Bundle\OAuth2ServerBundle\Model\Grant as GrantModel;
11+
use League\Bundle\OAuth2ServerBundle\OAuth2Events;
12+
use League\OAuth2\Server\Entities\ClientEntityInterface;
13+
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
14+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
15+
16+
final class UserRepository implements UserRepositoryInterface
17+
{
18+
/**
19+
* @var ClientManagerInterface
20+
*/
21+
private $clientManager;
22+
23+
/**
24+
* @var EventDispatcherInterface
25+
*/
26+
private $eventDispatcher;
27+
28+
/**
29+
* @var UserConverterInterface
30+
*/
31+
private $userConverter;
32+
33+
public function __construct(
34+
ClientManagerInterface $clientManager,
35+
EventDispatcherInterface $eventDispatcher,
36+
UserConverterInterface $userConverter
37+
) {
38+
$this->clientManager = $clientManager;
39+
$this->eventDispatcher = $eventDispatcher;
40+
$this->userConverter = $userConverter;
41+
}
42+
43+
/**
44+
* {@inheritdoc}
45+
*/
46+
public function getUserEntityByUserCredentials(
47+
$username,
48+
$password,
49+
$grantType,
50+
ClientEntityInterface $clientEntity
51+
) {
52+
$client = $this->clientManager->find($clientEntity->getIdentifier());
53+
54+
$event = $this->eventDispatcher->dispatch(
55+
new UserResolveEvent(
56+
$username,
57+
$password,
58+
new GrantModel($grantType),
59+
$client
60+
),
61+
OAuth2Events::USER_RESOLVE
62+
);
63+
64+
$user = $event->getUser();
65+
66+
if (null === $user) {
67+
return null;
68+
}
69+
70+
return $this->userConverter->toLeague($user);
71+
}
72+
}

src/OAuth2Events.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66

77
final class OAuth2Events
88
{
9+
/**
10+
* The USER_RESOLVE event occurs when the client requests a "password"
11+
* grant type from the authorization server.
12+
*
13+
* You should set a valid user here if applicable.
14+
*/
15+
public const USER_RESOLVE = 'league.oauth2-server.user_resolve';
16+
917
/**
1018
* The SCOPE_RESOLVE event occurs right before the user obtains their
1119
* valid access token.
@@ -16,7 +24,7 @@ final class OAuth2Events
1624

1725
/**
1826
* The AUTHORIZATION_REQUEST_RESOLVE event occurs right before the system
19-
* completes the authorization request.
27+
* complete the authorization request.
2028
*
2129
* You could approve or deny the authorization request, or set the uri where
2230
* must be redirected to resolve the authorization request.

src/OAuth2Grants.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@ final class OAuth2Grants
2121
public const CLIENT_CREDENTIALS = 'client_credentials';
2222

2323
/**
24+
* @see https://tools.ietf.org/html/rfc6749#section-1.3.2
25+
*
26+
* @var string
27+
*/
28+
public const IMPLICIT = 'implicit';
29+
30+
/**
31+
* @see https://tools.ietf.org/html/rfc6749#section-1.3.3
32+
*
33+
* @var string
34+
*/
35+
public const PASSWORD = 'password';
36+
2437
/**
2538
* @see https://tools.ietf.org/html/rfc6749#section-6
2639
*
@@ -32,8 +45,10 @@ public static function has(string $grant): bool
3245
{
3346
return \in_array($grant, [
3447
self::CLIENT_CREDENTIALS,
48+
self::PASSWORD,
3549
self::REFRESH_TOKEN,
3650
self::AUTHORIZATION_CODE,
51+
self::IMPLICIT,
3752
]);
3853
}
3954
}

src/Resources/config/services.xml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@
3131
</service>
3232
<service id="League\OAuth2\Server\Repositories\ScopeRepositoryInterface" alias="League\Bundle\OAuth2ServerBundle\League\Repository\ScopeRepository" />
3333

34+
<service id="League\Bundle\OAuth2ServerBundle\League\Repository\UserRepository">
35+
<argument type="service" id="League\Bundle\OAuth2ServerBundle\Manager\ClientManagerInterface" />
36+
<argument type="service" id="Symfony\Component\EventDispatcher\EventDispatcherInterface" />
37+
<argument type="service" id="League\Bundle\OAuth2ServerBundle\Converter\UserConverter" />
38+
</service>
39+
<service id="League\OAuth2\Server\Repositories\UserRepositoryInterface" alias="League\Bundle\OAuth2ServerBundle\League\Repository\UserRepository" />
40+
3441
<service id="League\Bundle\OAuth2ServerBundle\League\Repository\AuthCodeRepository">
3542
<argument type="service" id="League\Bundle\OAuth2ServerBundle\Manager\AuthorizationCodeManagerInterface" />
3643
<argument type="service" id="League\Bundle\OAuth2ServerBundle\Manager\ClientManagerInterface" />
@@ -80,6 +87,12 @@
8087
<service id="League\OAuth2\Server\Grant\ClientCredentialsGrant" />
8188
<service id="league.oauth2.server.grant.client_credentials_grant" alias="League\OAuth2\Server\Grant\ClientCredentialsGrant" />
8289

90+
<service id="League\OAuth2\Server\Grant\PasswordGrant">
91+
<argument type="service" id="League\OAuth2\Server\Repositories\UserRepositoryInterface" />
92+
<argument type="service" id="League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface" />
93+
</service>
94+
<service id="league.oauth2.server.grant.password_grant" alias="League\OAuth2\Server\Grant\PasswordGrant" />
95+
8396
<service id="League\OAuth2\Server\Grant\RefreshTokenGrant">
8497
<argument type="service" id="League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface" />
8598
</service>
@@ -92,6 +105,11 @@
92105
</service>
93106
<service id="league.oauth2.server.grant.auth_code_grant" alias="League\OAuth2\Server\Grant\AuthCodeGrant" />
94107

108+
<service id="League\OAuth2\Server\Grant\ImplicitGrant">
109+
<argument key="$accessTokenTTL" />
110+
</service>
111+
<service id="league.oauth2.server.grant.implicit_grant" alias="League\OAuth2\Server\Grant\ImplicitGrant" />
112+
95113
<!-- Authorization controller -->
96114
<service id="League\Bundle\OAuth2ServerBundle\Controller\AuthorizationController">
97115
<argument type="service" id="League\OAuth2\Server\AuthorizationServer" />

tests/Acceptance/AuthorizationEndpointTest.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,46 @@ public function testAuthCodeRequestWithClientWhoIsAllowedToMakeARequestWithPlain
294294
$this->assertSame(FixtureFactory::FIXTURE_PUBLIC_CLIENT_ALLOWED_TO_USE_PLAIN_CHALLENGE_METHOD, $authCode->getClient()->getIdentifier());
295295
}
296296

297+
public function testSuccessfulTokenRequest(): void
298+
{
299+
$this->client
300+
->getContainer()
301+
->get('event_dispatcher')
302+
->addListener(OAuth2Events::AUTHORIZATION_REQUEST_RESOLVE, static function (AuthorizationRequestResolveEvent $event): void {
303+
$event->resolveAuthorization(AuthorizationRequestResolveEvent::AUTHORIZATION_APPROVED);
304+
});
305+
306+
timecop_freeze(new DateTimeImmutable());
307+
308+
try {
309+
$this->client->request(
310+
'GET',
311+
'/authorize',
312+
[
313+
'client_id' => FixtureFactory::FIXTURE_CLIENT_FIRST,
314+
'response_type' => 'token',
315+
'state' => 'foobar',
316+
]
317+
);
318+
} finally {
319+
timecop_return();
320+
}
321+
322+
$response = $this->client->getResponse();
323+
324+
$this->assertSame(302, $response->getStatusCode());
325+
$redirectUri = $response->headers->get('Location');
326+
327+
$this->assertStringStartsWith(FixtureFactory::FIXTURE_CLIENT_FIRST_REDIRECT_URI, $redirectUri);
328+
$fragment = [];
329+
parse_str(parse_url($redirectUri, PHP_URL_FRAGMENT), $fragment);
330+
$this->assertArrayHasKey('access_token', $fragment);
331+
$this->assertArrayHasKey('token_type', $fragment);
332+
$this->assertArrayHasKey('expires_in', $fragment);
333+
$this->assertArrayHasKey('state', $fragment);
334+
$this->assertEquals('foobar', $fragment['state']);
335+
}
336+
297337
public function testCodeRequestRedirectToResolutionUri(): void
298338
{
299339
$this->client

0 commit comments

Comments
 (0)