Skip to content

Commit

Permalink
Merge branch 'master' into master-fix-some-bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
hafezdivandari authored Oct 14, 2024
2 parents e382fa4 + 1dc1ee8 commit 1dcdd52
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 60 deletions.
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased]
- Fixed bug on setting interval visibility of device authorization grant (PR #1410)

## [9.0.1] - released 2024-10-14
### Fixed
- Auto-generated event emitter is now persisted. Previously, a new emitter was generated every time (PR #1428)
- Fixed bug where you could not omit a redirect uri even if one had not been specified during the auth request (PR #1428)
- Fixed bug on setting interval visibility of device authorization grant (PR #1410)
- Fixed bug where "state" parameter wasn't present on `invalid_scope` error response and wasn't on fragment part of `access_denied` redirect URI on Implicit grant (PR #1298)
- Fixed bug where disabling refresh token revocation via `revokeRefreshTokens(false)` unintentionally disables issuing new refresh token (PR #1449)

## [9.0.0] - released 2024-05-13
### Added
Expand Down Expand Up @@ -652,7 +656,8 @@ Version 5 is a complete code rewrite.

- First major release

[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/9.0.0...HEAD
[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/9.0.1...HEAD
[9.0.1]: https://github.com/thephpleague/oauth2-server/compare/9.0.0...9.0.1
[9.0.0]: https://github.com/thephpleague/oauth2-server/compare/9.0.0-RC1...9.0.0
[9.0.0-RC1]: https://github.com/thephpleague/oauth2-server/compare/8.5.4...9.0.0-RC1
[8.5.4]: https://github.com/thephpleague/oauth2-server/compare/8.5.3...8.5.4
Expand Down
11 changes: 11 additions & 0 deletions src/Grant/AbstractAuthorizeGrant.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

namespace League\OAuth2\Server\Grant;

use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
use League\OAuth2\Server\RequestTypes\AuthorizationRequestInterface;

Expand All @@ -35,4 +36,14 @@ protected function createAuthorizationRequest(): AuthorizationRequestInterface
{
return new AuthorizationRequest();
}

/**
* Get the client redirect URI.
*/
protected function getClientRedirectUri(ClientEntityInterface $client): string
{
return is_array($client->getRedirectUri())
? $client->getRedirectUri()[0]
: $client->getRedirectUri();
}
}
23 changes: 6 additions & 17 deletions src/Grant/AuthCodeGrant.php
Original file line number Diff line number Diff line change
Expand Up @@ -280,17 +280,16 @@ public function validateAuthorizationRequest(ServerRequestInterface $request): A
throw OAuthServerException::invalidClient($request);
}

$defaultClientRedirectUri = is_array($client->getRedirectUri())
? $client->getRedirectUri()[0]
: $client->getRedirectUri();
$stateParameter = $this->getQueryStringParameter('state', $request);

$scopes = $this->validateScopes(
$this->getQueryStringParameter('scope', $request, $this->defaultScope),
$redirectUri ?? $defaultClientRedirectUri
$this->makeRedirectUri(
$redirectUri ?? $this->getClientRedirectUri($client),
$stateParameter !== null ? ['state' => $stateParameter] : []
)
);

$stateParameter = $this->getQueryStringParameter('state', $request);

$authorizationRequest = $this->createAuthorizationRequest();
$authorizationRequest->setGrantTypeId($this->getIdentifier());
$authorizationRequest->setClient($client);
Expand Down Expand Up @@ -354,7 +353,7 @@ public function completeAuthorizationRequest(AuthorizationRequestInterface $auth
}

$finalRedirectUri = $authorizationRequest->getRedirectUri()
?? $this->getClientRedirectUri($authorizationRequest);
?? $this->getClientRedirectUri($authorizationRequest->getClient());

// The user approved the client, redirect them back with an auth code
if ($authorizationRequest->isAuthorizationApproved() === true) {
Expand Down Expand Up @@ -408,14 +407,4 @@ public function completeAuthorizationRequest(AuthorizationRequestInterface $auth
)
);
}

/**
* Get the client redirect URI if not set in the request.
*/
private function getClientRedirectUri(AuthorizationRequestInterface $authorizationRequest): string
{
return is_array($authorizationRequest->getClient()->getRedirectUri())
? $authorizationRequest->getClient()->getRedirectUri()[0]
: $authorizationRequest->getClient()->getRedirectUri();
}
}
28 changes: 13 additions & 15 deletions src/Grant/ImplicitGrant.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,24 +111,24 @@ public function validateAuthorizationRequest(ServerRequestInterface $request): A
if ($redirectUri !== null) {
$this->validateRedirectUri($redirectUri, $client, $request);
} elseif (
is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1
|| $client->getRedirectUri() === ''
$client->getRedirectUri() === '' ||
(is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1)
) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient($request);
} else {
$redirectUri = is_array($client->getRedirectUri())
? $client->getRedirectUri()[0]
: $client->getRedirectUri();
}

$stateParameter = $this->getQueryStringParameter('state', $request);

$scopes = $this->validateScopes(
$this->getQueryStringParameter('scope', $request, $this->defaultScope),
$redirectUri
$this->makeRedirectUri(
$redirectUri ?? $this->getClientRedirectUri($client),
$stateParameter !== null ? ['state' => $stateParameter] : [],
$this->queryDelimiter
)
);

$stateParameter = $this->getQueryStringParameter('state', $request);

$authorizationRequest = $this->createAuthorizationRequest();
$authorizationRequest->setGrantTypeId($this->getIdentifier());
$authorizationRequest->setClient($client);
Expand All @@ -152,11 +152,8 @@ public function completeAuthorizationRequest(AuthorizationRequestInterface $auth
throw new LogicException('An instance of UserEntityInterface should be set on the AuthorizationRequest');
}

$clientRegisteredRedirectUri = is_array($authorizationRequest->getClient()->getRedirectUri())
? $authorizationRequest->getClient()->getRedirectUri()[0]
: $authorizationRequest->getClient()->getRedirectUri();

$finalRedirectUri = $authorizationRequest->getRedirectUri() ?? $clientRegisteredRedirectUri;
$finalRedirectUri = $authorizationRequest->getRedirectUri()
?? $this->getClientRedirectUri($authorizationRequest->getClient());

// The user approved the client, redirect them back with an access token
if ($authorizationRequest->isAuthorizationApproved() === true) {
Expand Down Expand Up @@ -199,7 +196,8 @@ public function completeAuthorizationRequest(AuthorizationRequestInterface $auth
$finalRedirectUri,
[
'state' => $authorizationRequest->getState(),
]
],
$this->queryDelimiter
)
);
}
Expand Down
10 changes: 4 additions & 6 deletions src/Grant/RefreshTokenGrant.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,11 @@ public function respondToAccessTokenRequest(
$responseType->setAccessToken($accessToken);

// Issue and persist new refresh token if given
if ($this->revokeRefreshTokens) {
$refreshToken = $this->issueRefreshToken($accessToken);
$refreshToken = $this->issueRefreshToken($accessToken);

if ($refreshToken !== null) {
$this->getEmitter()->emit(new RequestRefreshTokenEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request, $refreshToken));
$responseType->setRefreshToken($refreshToken);
}
if ($refreshToken !== null) {
$this->getEmitter()->emit(new RequestRefreshTokenEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request, $refreshToken));
$responseType->setRefreshToken($refreshToken);
}

return $responseType;
Expand Down
57 changes: 54 additions & 3 deletions tests/Grant/AuthCodeGrantTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,49 @@ public function testValidateAuthorizationRequestInvalidCodeChallengeMethod(): vo
$grant->validateAuthorizationRequest($request);
}

public function testValidateAuthorizationRequestInvalidScopes(): void
{
$client = new ClientEntity();
$client->setRedirectUri(self::REDIRECT_URI);
$client->setConfidential();

$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);

$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn(null);

$grant = new AuthCodeGrant(
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new DateInterval('PT10M')
);

$grant->setClientRepository($clientRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);
$grant->setDefaultScope(self::DEFAULT_SCOPE);

$request = (new ServerRequest())->withQueryParams([
'response_type' => 'code',
'client_id' => 'foo',
'redirect_uri' => self::REDIRECT_URI,
'scope' => 'foo',
'state' => 'foo',
]);

try {
$grant->validateAuthorizationRequest($request);
} catch (OAuthServerException $e) {
self::assertSame(5, $e->getCode());
self::assertSame('invalid_scope', $e->getErrorType());
self::assertSame('https://foo/bar?state=foo', $e->getRedirectUri());

return;
}

self::fail('The expected exception was not thrown');
}

public function testCompleteAuthorizationRequest(): void
{
$client = new ClientEntity();
Expand Down Expand Up @@ -529,6 +572,7 @@ public function testCompleteAuthorizationRequestDenied(): void
$authRequest->setClient($client);
$authRequest->setGrantTypeId('authorization_code');
$authRequest->setUser(new UserEntity());
$authRequest->setState('foo');

$authCodeRepository = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock();
$authCodeRepository->method('getNewAuthCode')->willReturn(new AuthCodeEntity());
Expand All @@ -540,10 +584,17 @@ public function testCompleteAuthorizationRequestDenied(): void
);
$grant->setEncryptionKey($this->cryptStub->getKey());

$this->expectException(OAuthServerException::class);
$this->expectExceptionCode(9);
try {
$grant->completeAuthorizationRequest($authRequest);
} catch (OAuthServerException $e) {
self::assertSame(9, $e->getCode());
self::assertSame('access_denied', $e->getErrorType());
self::assertSame('http://foo/bar?state=foo', $e->getRedirectUri());

return;
}

$grant->completeAuthorizationRequest($authRequest);
self::fail('The expected exception was not thrown');
}

public function testRespondToAccessTokenRequest(): void
Expand Down
64 changes: 55 additions & 9 deletions tests/Grant/ImplicitGrantTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public function testValidateAuthorizationRequest(): void
$grant->setDefaultScope(self::DEFAULT_SCOPE);

$request = (new ServerRequest())->withQueryParams([
'response_type' => 'code',
'response_type' => 'token',
'client_id' => 'foo',
'redirect_uri' => self::REDIRECT_URI,
]);
Expand All @@ -120,7 +120,7 @@ public function testValidateAuthorizationRequestRedirectUriArray(): void
$grant->setDefaultScope(self::DEFAULT_SCOPE);

$request = (new ServerRequest())->withQueryParams([
'response_type' => 'code',
'response_type' => 'token',
'client_id' => 'foo',
'redirect_uri' => self::REDIRECT_URI,
]);
Expand All @@ -135,7 +135,7 @@ public function testValidateAuthorizationRequestMissingClientId(): void
$grant = new ImplicitGrant(new DateInterval('PT10M'));
$grant->setClientRepository($clientRepositoryMock);

$request = (new ServerRequest())->withQueryParams(['response_type' => 'code']);
$request = (new ServerRequest())->withQueryParams(['response_type' => 'token']);

$this->expectException(OAuthServerException::class);
$this->expectExceptionCode(3);
Expand All @@ -152,7 +152,7 @@ public function testValidateAuthorizationRequestInvalidClientId(): void
$grant->setClientRepository($clientRepositoryMock);

$request = (new ServerRequest())->withQueryParams([
'response_type' => 'code',
'response_type' => 'token',
'client_id' => 'foo',
]);

Expand All @@ -173,7 +173,7 @@ public function testValidateAuthorizationRequestBadRedirectUriString(): void
$grant->setClientRepository($clientRepositoryMock);

$request = (new ServerRequest())->withQueryParams([
'response_type' => 'code',
'response_type' => 'token',
'client_id' => 'foo',
'redirect_uri' => 'http://bar',
]);
Expand All @@ -195,7 +195,7 @@ public function testValidateAuthorizationRequestBadRedirectUriArray(): void
$grant->setClientRepository($clientRepositoryMock);

$request = (new ServerRequest())->withQueryParams([
'response_type' => 'code',
'response_type' => 'token',
'client_id' => 'foo',
'redirect_uri' => 'http://bar',
]);
Expand All @@ -206,6 +206,44 @@ public function testValidateAuthorizationRequestBadRedirectUriArray(): void
$grant->validateAuthorizationRequest($request);
}

public function testValidateAuthorizationRequestInvalidScopes(): void
{
$client = new ClientEntity();
$client->setRedirectUri(self::REDIRECT_URI);

$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);

$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn(null);

$grant = new ImplicitGrant(new DateInterval('PT10M'));

$grant->setClientRepository($clientRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);
$grant->setDefaultScope(self::DEFAULT_SCOPE);

$request = (new ServerRequest())->withQueryParams([
'response_type' => 'token',
'client_id' => 'foo',
'redirect_uri' => self::REDIRECT_URI,
'scope' => 'foo',
'state' => 'foo',
]);

try {
$grant->validateAuthorizationRequest($request);
} catch (OAuthServerException $e) {
self::assertSame(5, $e->getCode());
self::assertSame('invalid_scope', $e->getErrorType());
self::assertSame('https://foo/bar#state=foo', $e->getRedirectUri());

return;
}

self::fail('Did not throw expected exception');
}

public function testCompleteAuthorizationRequest(): void
{
$client = new ClientEntity();
Expand Down Expand Up @@ -248,6 +286,7 @@ public function testCompleteAuthorizationRequestDenied(): void
$authRequest->setClient($client);
$authRequest->setGrantTypeId('authorization_code');
$authRequest->setUser(new UserEntity());
$authRequest->setState('foo');

$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
Expand All @@ -261,10 +300,17 @@ public function testCompleteAuthorizationRequestDenied(): void
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);

$this->expectException(OAuthServerException::class);
$this->expectExceptionCode(9);
try {
$grant->completeAuthorizationRequest($authRequest);
} catch (OAuthServerException $e) {
self::assertSame(9, $e->getCode());
self::assertSame('access_denied', $e->getErrorType());
self::assertSame('https://foo/bar#state=foo', $e->getRedirectUri());

$grant->completeAuthorizationRequest($authRequest);
return;
}

self::fail('Did not throw expected exception');
}

public function testAccessTokenRepositoryUniqueConstraintCheck(): void
Expand Down
Loading

0 comments on commit 1dcdd52

Please sign in to comment.