mirror of
https://github.com/elyby/oauth2-server.git
synced 2024-12-22 13:09:44 +05:30
commit
382b6f5fbf
@ -41,7 +41,6 @@ enabled:
|
|||||||
- print_to_echo
|
- print_to_echo
|
||||||
- short_array_syntax
|
- short_array_syntax
|
||||||
- short_scalar_cast
|
- short_scalar_cast
|
||||||
- simplified_null_return
|
|
||||||
- single_quote
|
- single_quote
|
||||||
- spaces_cast
|
- spaces_cast
|
||||||
- standardize_not_equal
|
- standardize_not_equal
|
||||||
|
@ -5,6 +5,8 @@ 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).
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
### Changed
|
||||||
|
- RefreshTokenRepository can now return null, allowing refresh tokens to be optional. (PR #649)
|
||||||
|
|
||||||
## [7.3.3] - released 2019-03-29
|
## [7.3.3] - released 2019-03-29
|
||||||
### Added
|
### Added
|
||||||
|
@ -472,16 +472,21 @@ abstract class AbstractGrant implements GrantTypeInterface
|
|||||||
* @throws OAuthServerException
|
* @throws OAuthServerException
|
||||||
* @throws UniqueTokenIdentifierConstraintViolationException
|
* @throws UniqueTokenIdentifierConstraintViolationException
|
||||||
*
|
*
|
||||||
* @return RefreshTokenEntityInterface
|
* @return RefreshTokenEntityInterface|null
|
||||||
*/
|
*/
|
||||||
protected function issueRefreshToken(AccessTokenEntityInterface $accessToken)
|
protected function issueRefreshToken(AccessTokenEntityInterface $accessToken)
|
||||||
{
|
{
|
||||||
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
|
|
||||||
|
|
||||||
$refreshToken = $this->refreshTokenRepository->getNewRefreshToken();
|
$refreshToken = $this->refreshTokenRepository->getNewRefreshToken();
|
||||||
|
|
||||||
|
if ($refreshToken === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
$refreshToken->setExpiryDateTime((new DateTime())->add($this->refreshTokenTTL));
|
$refreshToken->setExpiryDateTime((new DateTime())->add($this->refreshTokenTTL));
|
||||||
$refreshToken->setAccessToken($accessToken);
|
$refreshToken->setAccessToken($accessToken);
|
||||||
|
|
||||||
|
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
|
||||||
|
|
||||||
while ($maxGenerationAttempts-- > 0) {
|
while ($maxGenerationAttempts-- > 0) {
|
||||||
$refreshToken->setIdentifier($this->generateUniqueIdentifier());
|
$refreshToken->setIdentifier($this->generateUniqueIdentifier());
|
||||||
try {
|
try {
|
||||||
|
@ -145,17 +145,18 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issue and persist access + refresh tokens
|
// Issue and persist new access token
|
||||||
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $authCodePayload->user_id, $scopes);
|
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $authCodePayload->user_id, $scopes);
|
||||||
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
|
||||||
|
$responseType->setAccessToken($accessToken);
|
||||||
|
|
||||||
|
// Issue and persist new refresh token if given
|
||||||
$refreshToken = $this->issueRefreshToken($accessToken);
|
$refreshToken = $this->issueRefreshToken($accessToken);
|
||||||
|
|
||||||
// Send events to emitter
|
if ($refreshToken !== null) {
|
||||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
|
||||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
|
$responseType->setRefreshToken($refreshToken);
|
||||||
|
}
|
||||||
// Inject tokens into response type
|
|
||||||
$responseType->setAccessToken($accessToken);
|
|
||||||
$responseType->setRefreshToken($refreshToken);
|
|
||||||
|
|
||||||
// Revoke used auth code
|
// Revoke used auth code
|
||||||
$this->authCodeRepository->revokeAuthCode($authCodePayload->auth_code_id);
|
$this->authCodeRepository->revokeAuthCode($authCodePayload->auth_code_id);
|
||||||
|
@ -56,17 +56,18 @@ class PasswordGrant extends AbstractGrant
|
|||||||
// Finalize the requested scopes
|
// Finalize the requested scopes
|
||||||
$finalizedScopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $user->getIdentifier());
|
$finalizedScopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $user->getIdentifier());
|
||||||
|
|
||||||
// Issue and persist new tokens
|
// Issue and persist new access token
|
||||||
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $finalizedScopes);
|
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $finalizedScopes);
|
||||||
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
|
||||||
|
$responseType->setAccessToken($accessToken);
|
||||||
|
|
||||||
|
// Issue and persist new refresh token if given
|
||||||
$refreshToken = $this->issueRefreshToken($accessToken);
|
$refreshToken = $this->issueRefreshToken($accessToken);
|
||||||
|
|
||||||
// Send events to emitter
|
if ($refreshToken !== null) {
|
||||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
|
||||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
|
$responseType->setRefreshToken($refreshToken);
|
||||||
|
}
|
||||||
// Inject tokens into response
|
|
||||||
$responseType->setAccessToken($accessToken);
|
|
||||||
$responseType->setRefreshToken($refreshToken);
|
|
||||||
|
|
||||||
return $responseType;
|
return $responseType;
|
||||||
}
|
}
|
||||||
|
@ -63,17 +63,18 @@ class RefreshTokenGrant extends AbstractGrant
|
|||||||
$this->accessTokenRepository->revokeAccessToken($oldRefreshToken['access_token_id']);
|
$this->accessTokenRepository->revokeAccessToken($oldRefreshToken['access_token_id']);
|
||||||
$this->refreshTokenRepository->revokeRefreshToken($oldRefreshToken['refresh_token_id']);
|
$this->refreshTokenRepository->revokeRefreshToken($oldRefreshToken['refresh_token_id']);
|
||||||
|
|
||||||
// Issue and persist new tokens
|
// Issue and persist new access token
|
||||||
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $oldRefreshToken['user_id'], $scopes);
|
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $oldRefreshToken['user_id'], $scopes);
|
||||||
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
|
||||||
|
$responseType->setAccessToken($accessToken);
|
||||||
|
|
||||||
|
// Issue and persist new refresh token if given
|
||||||
$refreshToken = $this->issueRefreshToken($accessToken);
|
$refreshToken = $this->issueRefreshToken($accessToken);
|
||||||
|
|
||||||
// Send events to emitter
|
if ($refreshToken !== null) {
|
||||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
|
||||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
|
$responseType->setRefreshToken($refreshToken);
|
||||||
|
}
|
||||||
// Inject tokens into response
|
|
||||||
$responseType->setAccessToken($accessToken);
|
|
||||||
$responseType->setRefreshToken($refreshToken);
|
|
||||||
|
|
||||||
return $responseType;
|
return $responseType;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ interface RefreshTokenRepositoryInterface extends RepositoryInterface
|
|||||||
/**
|
/**
|
||||||
* Creates a new refresh token
|
* Creates a new refresh token
|
||||||
*
|
*
|
||||||
* @return RefreshTokenEntityInterface
|
* @return RefreshTokenEntityInterface|null
|
||||||
*/
|
*/
|
||||||
public function getNewRefreshToken();
|
public function getNewRefreshToken();
|
||||||
|
|
||||||
|
@ -346,6 +346,27 @@ class AbstractGrantTest extends TestCase
|
|||||||
$this->assertEquals($accessToken, $refreshToken->getAccessToken());
|
$this->assertEquals($accessToken, $refreshToken->getAccessToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testIssueNullRefreshToken()
|
||||||
|
{
|
||||||
|
$refreshTokenRepoMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||||
|
$refreshTokenRepoMock
|
||||||
|
->expects($this->once())
|
||||||
|
->method('getNewRefreshToken')
|
||||||
|
->willReturn(null);
|
||||||
|
|
||||||
|
/** @var AbstractGrant $grantMock */
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
$grantMock->setRefreshTokenTTL(new \DateInterval('PT1M'));
|
||||||
|
$grantMock->setRefreshTokenRepository($refreshTokenRepoMock);
|
||||||
|
|
||||||
|
$abstractGrantReflection = new \ReflectionClass($grantMock);
|
||||||
|
$issueRefreshTokenMethod = $abstractGrantReflection->getMethod('issueRefreshToken');
|
||||||
|
$issueRefreshTokenMethod->setAccessible(true);
|
||||||
|
|
||||||
|
$accessToken = new AccessTokenEntity();
|
||||||
|
$this->assertNull($issueRefreshTokenMethod->invoke($grantMock, $accessToken));
|
||||||
|
}
|
||||||
|
|
||||||
public function testIssueAccessToken()
|
public function testIssueAccessToken()
|
||||||
{
|
{
|
||||||
$accessTokenRepoMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
$accessTokenRepoMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
@ -644,6 +644,73 @@ class AuthCodeGrantTest extends TestCase
|
|||||||
$this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken());
|
$this->assertInstanceOf(RefreshTokenEntityInterface::class, $response->getRefreshToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testRespondToAccessTokenRequestNullRefreshToken()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setIdentifier('foo');
|
||||||
|
$client->setRedirectUri('http://foo/bar');
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||||
|
$scopeEntity = new ScopeEntity();
|
||||||
|
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
|
||||||
|
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||||
|
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
|
||||||
|
|
||||||
|
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||||
|
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
|
||||||
|
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(null);
|
||||||
|
|
||||||
|
$grant = new AuthCodeGrant(
|
||||||
|
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
|
||||||
|
$refreshTokenRepositoryMock,
|
||||||
|
new \DateInterval('PT10M')
|
||||||
|
);
|
||||||
|
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
$grant->setScopeRepository($scopeRepositoryMock);
|
||||||
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
$grant->setEncryptionKey($this->cryptStub->getKey());
|
||||||
|
|
||||||
|
$request = new ServerRequest(
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
null,
|
||||||
|
'POST',
|
||||||
|
'php://input',
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[
|
||||||
|
'grant_type' => 'authorization_code',
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'redirect_uri' => 'http://foo/bar',
|
||||||
|
'code' => $this->cryptStub->doEncrypt(
|
||||||
|
json_encode(
|
||||||
|
[
|
||||||
|
'auth_code_id' => uniqid(),
|
||||||
|
'expire_time' => time() + 3600,
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'user_id' => 123,
|
||||||
|
'scopes' => ['foo'],
|
||||||
|
'redirect_uri' => 'http://foo/bar',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
/** @var StubResponseType $response */
|
||||||
|
$response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M'));
|
||||||
|
|
||||||
|
$this->assertInstanceOf(AccessTokenEntityInterface::class, $response->getAccessToken());
|
||||||
|
$this->assertNull($response->getRefreshToken());
|
||||||
|
}
|
||||||
|
|
||||||
public function testRespondToAccessTokenRequestCodeChallengePlain()
|
public function testRespondToAccessTokenRequestCodeChallengePlain()
|
||||||
{
|
{
|
||||||
$client = new ClientEntity();
|
$client = new ClientEntity();
|
||||||
|
@ -78,6 +78,51 @@ class PasswordGrantTest extends TestCase
|
|||||||
$this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken());
|
$this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testRespondToRequestNullRefreshToken()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||||
|
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
|
||||||
|
|
||||||
|
$userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock();
|
||||||
|
$userEntity = new UserEntity();
|
||||||
|
$userRepositoryMock->method('getUserEntityByUserCredentials')->willReturn($userEntity);
|
||||||
|
|
||||||
|
$scope = new ScopeEntity();
|
||||||
|
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||||
|
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope);
|
||||||
|
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
||||||
|
|
||||||
|
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||||
|
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(null);
|
||||||
|
|
||||||
|
$grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock);
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
$grant->setScopeRepository($scopeRepositoryMock);
|
||||||
|
$grant->setDefaultScope(self::DEFAULT_SCOPE);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'client_secret' => 'bar',
|
||||||
|
'username' => 'foo',
|
||||||
|
'password' => 'bar',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$responseType = new StubResponseType();
|
||||||
|
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
|
||||||
|
|
||||||
|
$this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken());
|
||||||
|
$this->assertNull($responseType->getRefreshToken());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
*/
|
*/
|
||||||
|
@ -94,6 +94,63 @@ class RefreshTokenGrantTest extends TestCase
|
|||||||
$this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken());
|
$this->assertInstanceOf(RefreshTokenEntityInterface::class, $responseType->getRefreshToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testRespondToRequestNullRefreshToken()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setIdentifier('foo');
|
||||||
|
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$scopeEntity = new ScopeEntity();
|
||||||
|
$scopeEntity->setIdentifier('foo');
|
||||||
|
|
||||||
|
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||||
|
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||||
|
$accessTokenRepositoryMock->expects($this->once())->method('persistNewAccessToken')->willReturnSelf();
|
||||||
|
|
||||||
|
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||||
|
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(null);
|
||||||
|
$refreshTokenRepositoryMock->expects($this->never())->method('persistNewRefreshToken');
|
||||||
|
|
||||||
|
$grant = new RefreshTokenGrant($refreshTokenRepositoryMock);
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
$grant->setScopeRepository($scopeRepositoryMock);
|
||||||
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
$grant->setEncryptionKey($this->cryptStub->getKey());
|
||||||
|
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
|
|
||||||
|
$oldRefreshToken = $this->cryptStub->doEncrypt(
|
||||||
|
json_encode(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'refresh_token_id' => 'zyxwvu',
|
||||||
|
'access_token_id' => 'abcdef',
|
||||||
|
'scopes' => ['foo'],
|
||||||
|
'user_id' => 123,
|
||||||
|
'expire_time' => time() + 3600,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody([
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'client_secret' => 'bar',
|
||||||
|
'refresh_token' => $oldRefreshToken,
|
||||||
|
'scopes' => ['foo'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$responseType = new StubResponseType();
|
||||||
|
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
|
||||||
|
|
||||||
|
$this->assertInstanceOf(AccessTokenEntityInterface::class, $responseType->getAccessToken());
|
||||||
|
$this->assertNull($responseType->getRefreshToken());
|
||||||
|
}
|
||||||
|
|
||||||
public function testRespondToReducedScopes()
|
public function testRespondToReducedScopes()
|
||||||
{
|
{
|
||||||
$client = new ClientEntity();
|
$client = new ClientEntity();
|
||||||
|
Loading…
Reference in New Issue
Block a user