Skip to content

Commit

Permalink
Add support for the "implicit" grant type
Browse files Browse the repository at this point in the history
  • Loading branch information
ajgarlag committed Jun 4, 2019
1 parent 82c6995 commit 2c75180
Show file tree
Hide file tree
Showing 7 changed files with 225 additions and 1 deletion.
4 changes: 4 additions & 0 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ private function createAuthorizationServerNode(): NodeDefinition
->info('Whether to enable the authorization code grant')
->defaultTrue()
->end()
->booleanNode('enable_implicit_grant')
->info('Whether to enable the implicit grant')
->defaultTrue()
->end()
->end()
;

Expand Down
12 changes: 12 additions & 0 deletions DependencyInjection/TrikoderOAuth2Extension.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,13 @@ private function configureAuthorizationServer(ContainerBuilder $container, array
]);
}

if ($config['enable_implicit_grant']) {
$authorizationServer->addMethodCall('enableGrantType', [
new Reference('league.oauth2.server.grant.implicit_grant'),
new Definition(DateInterval::class, [$config['access_token_ttl']]),
]);
}

$this->configureGrants($container, $config);
}

Expand All @@ -197,6 +204,11 @@ private function configureGrants(ContainerBuilder $container, array $config): vo
new Definition(DateInterval::class, [$config['refresh_token_ttl']]),
])
;

$container
->getDefinition('league.oauth2.server.grant.implicit_grant')
->replaceArgument('$accessTokenTTL', new Definition(DateInterval::class, [$config['access_token_ttl']]))
;
}

private function configurePersistence(LoaderInterface $loader, ContainerBuilder $container, array $config)
Expand Down
2 changes: 1 addition & 1 deletion OAuth2Grants.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ final class OAuth2Grants

public static function has(string $grant): bool
{
// TODO: Add support for "implicit" grant type.
return \in_array($grant, [
self::CLIENT_CREDENTIALS,
self::PASSWORD,
self::REFRESH_TOKEN,
self::AUTHORIZATION_CODE,
self::IMPLICIT,
]);
}
}
3 changes: 3 additions & 0 deletions Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@
<argument type="service" id="trikoder.oauth2.league.repository.refresh_token_repository" />
<argument key="$authCodeTTL" />
</service>
<service id="league.oauth2.server.grant.implicit_grant" class="League\OAuth2\Server\Grant\ImplicitGrant" >
<argument key="$accessTokenTTL" />
</service>

<!-- Authorization controller -->
<service id="trikoder.oauth2.controller.authorization_controller" class="Trikoder\Bundle\OAuth2Bundle\Controller\AuthorizationController">
Expand Down
38 changes: 38 additions & 0 deletions Tests/Acceptance/AuthorizationEndpointTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,44 @@ public function testSuccessfulCodeRequest()
$this->assertEquals('foobar', $query['state']);
}

public function testSuccessfulTokenRequest()
{
$this->client
->getContainer()
->get('event_dispatcher')
->addListener(OAuth2Events::AUTHORIZATION_REQUEST_RESOLVE, function (AuthorizationRequestResolveEvent $event) {
$event->resolveAuthorization(AuthorizationRequestResolveEvent::AUTHORIZATION_APPROVED);
});

timecop_freeze(new DateTime());

$this->client->request(
'GET',
'/authorize',
[
'client_id' => FixtureFactory::FIXTURE_CLIENT_FIRST,
'response_type' => 'token',
'state' => 'foobar',
]
);

timecop_return();

$response = $this->client->getResponse();

$this->assertSame(302, $response->getStatusCode());
$redirectUri = $response->headers->get('Location');

$this->assertStringStartsWith(FixtureFactory::FIXTURE_CLIENT_FIRST_REDIRECT_URI, $redirectUri);
$fragment = [];
parse_str(parse_url($redirectUri, PHP_URL_FRAGMENT), $fragment);
$this->assertArrayHasKey('access_token', $fragment);
$this->assertArrayHasKey('token_type', $fragment);
$this->assertArrayHasKey('expires_in', $fragment);
$this->assertArrayHasKey('state', $fragment);
$this->assertEquals('foobar', $fragment['state']);
}

public function testCodeRequestRedirectToResolutionUri()
{
$this->client
Expand Down
2 changes: 2 additions & 0 deletions Tests/Integration/AbstractIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\AuthCodeGrant;
use League\OAuth2\Server\Grant\ClientCredentialsGrant;
use League\OAuth2\Server\Grant\ImplicitGrant;
use League\OAuth2\Server\Grant\PasswordGrant;
use League\OAuth2\Server\Grant\RefreshTokenGrant;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
Expand Down Expand Up @@ -270,6 +271,7 @@ private function createAuthorizationServer(
$authorizationServer->enableGrantType(new RefreshTokenGrant($refreshTokenRepository));
$authorizationServer->enableGrantType(new PasswordGrant($userRepository, $refreshTokenRepository));
$authorizationServer->enableGrantType(new AuthCodeGrant($authCodeRepository, $refreshTokenRepository, new DateInterval('PT10M')));
$authorizationServer->enableGrantType(new ImplicitGrant(new DateInterval('PT10M')));

return $authorizationServer;
}
Expand Down
165 changes: 165 additions & 0 deletions Tests/Integration/AuthorizationServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -724,4 +724,169 @@ public function testFailedAuthorizationWithInvalidRedirectUri(): void
$this->assertSame('invalid_client', $response['error']);
$this->assertSame('Client authentication failed', $response['message']);
}

public function testSuccessfulImplicitRequest(): void
{
$request = $this->createAuthorizeRequest('foo:secret', [
'response_type' => 'token',
'client_id' => 'foo',
]);

$response = $this->handleAuthorizationRequest($request);
$this->assertSame(302, $response->getStatusCode());
$responseData = [];
parse_str(parse_url($response->getHeaderLine('Location'), PHP_URL_FRAGMENT), $responseData);
$accessToken = $this->getAccessToken($responseData['access_token']);

// Response assertions.
$this->assertSame('Bearer', $responseData['token_type']);
$this->assertEquals(600, $responseData['expires_in']);
$this->assertInstanceOf(AccessToken::class, $accessToken);
$this->assertSame('foo', $accessToken->getClient()->getIdentifier());
}

public function testSuccessfulImplicitRequestWithState(): void
{
$request = $this->createAuthorizeRequest(null, [
'response_type' => 'token',
'client_id' => 'foo',
'state' => 'quzbaz',
]);

$response = $this->handleAuthorizationRequest($request);
$this->assertSame(302, $response->getStatusCode());
$responseData = [];
parse_str(parse_url($response->getHeaderLine('Location'), PHP_URL_FRAGMENT), $responseData);
$accessToken = $this->getAccessToken($responseData['access_token']);

// Response assertions.
$this->assertSame('Bearer', $responseData['token_type']);
$this->assertEquals(600, $responseData['expires_in']);
$this->assertInstanceOf(AccessToken::class, $accessToken);
$this->assertSame('foo', $accessToken->getClient()->getIdentifier());
$this->assertSame('quzbaz', $responseData['state']);
}

public function testSuccessfulImplicitRequestRedirectUri(): void
{
$request = $this->createAuthorizeRequest(null, [
'response_type' => 'token',
'client_id' => 'foo',
'redirect_uri' => 'https://example.org/oauth2/redirect-uri',
]);

$response = $this->handleAuthorizationRequest($request);
$this->assertSame(302, $response->getStatusCode());
$responseData = [];
parse_str(parse_url($response->getHeaderLine('Location'), PHP_URL_FRAGMENT), $responseData);
$accessToken = $this->getAccessToken($responseData['access_token']);

// Response assertions.
$this->assertSame('Bearer', $responseData['token_type']);
$this->assertEquals(600, $responseData['expires_in']);
$this->assertInstanceOf(AccessToken::class, $accessToken);
$this->assertSame('foo', $accessToken->getClient()->getIdentifier());
}

public function testImplicitRequestWithInvalidScope(): void
{
$request = $this->createAuthorizeRequest(null, [
'response_type' => 'token',
'client_id' => 'foo',
'scope' => 'non_existing',
]);

$response = $this->handleAuthorizationRequest($request);
$this->assertSame(302, $response->getStatusCode());
$responseData = [];
parse_str(parse_url($response->getHeaderLine('Location'), PHP_URL_QUERY), $responseData);

// Response assertions.
$this->assertSame('invalid_scope', $responseData['error']);
$this->assertSame('The requested scope is invalid, unknown, or malformed', $responseData['message']);
$this->assertSame('Check the `non_existing` scope', $responseData['hint']);
}

public function testImplicitRequestWithInvalidRedirectUri(): void
{
$request = $this->createAuthorizeRequest(null, [
'response_type' => 'token',
'client_id' => 'foo',
'redirect_uri' => 'https://example.org/oauth2/other-uri',
]);

$response = $this->handleAuthorizationRequest($request);
$this->assertSame(401, $response->getStatusCode());
$responseData = json_decode((string) $response->getBody(), true);

// Response assertions.
$this->assertSame('invalid_client', $responseData['error']);
$this->assertSame('Client authentication failed', $responseData['message']);
}

public function testDeniedImplicitRequest(): void
{
$request = $this->createAuthorizeRequest(null, [
'response_type' => 'token',
'client_id' => 'foo',
]);

$response = $this->handleAuthorizationRequest($request, false);
$this->assertSame(302, $response->getStatusCode());
$responseData = [];
parse_str(parse_url($response->getHeaderLine('Location'), PHP_URL_QUERY), $responseData);

// Response assertions.
$this->assertSame('access_denied', $responseData['error']);
$this->assertSame('The resource owner or authorization server denied the request.', $responseData['message']);
$this->assertSame('The user denied the request', $responseData['hint']);
}

public function testImplicitRequestWithMissingClient(): void
{
$request = $this->createAuthorizeRequest(null, [
'response_type' => 'token',
'client_id' => 'yolo',
]);

$response = $this->handleAuthorizationRequest($request, false);
$this->assertSame(401, $response->getStatusCode());
$responseData = json_decode((string) $response->getBody(), true);

// Response assertions.
$this->assertSame('invalid_client', $responseData['error']);
$this->assertSame('Client authentication failed', $responseData['message']);
}

public function testImplicitRequestWithInactiveClient(): void
{
$request = $this->createAuthorizeRequest(null, [
'response_type' => 'token',
'client_id' => 'baz_inactive',
]);

$response = $this->handleAuthorizationRequest($request, false);
$this->assertSame(401, $response->getStatusCode());
$responseData = json_decode((string) $response->getBody(), true);

// Response assertions.
$this->assertSame('invalid_client', $responseData['error']);
$this->assertSame('Client authentication failed', $responseData['message']);
}

public function testImplicitRequestWithRestrictedGrantClient(): void
{
$request = $this->createAuthorizeRequest(null, [
'response_type' => 'token',
'client_id' => 'qux_restricted',
]);

$response = $this->handleAuthorizationRequest($request, false);
$this->assertSame(401, $response->getStatusCode());
$responseData = json_decode((string) $response->getBody(), true);

// Response assertions.
$this->assertSame('invalid_client', $responseData['error']);
$this->assertSame('Client authentication failed', $responseData['message']);
}
}

0 comments on commit 2c75180

Please sign in to comment.