Compare commits

...

8 Commits

Author SHA1 Message Date
ErickSkrauch
677c10a61d Add tests 2019-08-23 00:15:10 +03:00
ErickSkrauch
3684a76ade Add getter for the OAuthServerException::redirectUri param and make it public 2019-08-23 00:00:15 +03:00
Andrew Millington
0b0b43d433 Merge pull request #1035 from matt-allan/prevent-public-client-confidential-grant
Prevent public clients from using the client_credentials grant type
2019-07-25 19:20:11 +01:00
Andrew Millington
cd8742f630 Reword changelog 2019-07-25 19:14:08 +01:00
Andrew Millington
2097edd6eb Update changelog 2019-07-25 19:12:33 +01:00
Andrew Millington
705120c974 Add blank space to keep formatting consistent 2019-07-25 19:09:47 +01:00
Andrew Millington
8a78e00a2e Add blank line above throw 2019-07-25 19:04:44 +01:00
Matt Allan
3413c20590 Prevent public clients from using the client_credentials grant type
See https://tools.ietf.org/html/rfc6749#section-4.4.2
2019-07-22 18:21:29 -04:00
7 changed files with 66 additions and 11 deletions

View File

@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
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
- Clients are now explicitly prevented from using the Client Credentials grant unless they are confidential to conform
with the OAuth2 spec (PR #1035)
## [8.0.0] - released 2019-07-13
### Added

View File

@@ -294,14 +294,9 @@ class OAuthServerException extends Exception
$payload = $this->getPayload();
if ($this->redirectUri !== null) {
if ($useFragment === true) {
$this->redirectUri .= (strstr($this->redirectUri, '#') === false) ? '#' : '&';
} else {
$this->redirectUri .= (strstr($this->redirectUri, '?') === false) ? '?' : '&';
}
return $response->withStatus(302)->withHeader('Location', $this->redirectUri . http_build_query($payload));
$redirectUri = $this->getRedirectUri($useFragment);
if ($redirectUri !== null) {
return $response->withStatus(302)->withHeader('Location', $redirectUri);
}
foreach ($headers as $header => $content) {
@@ -359,6 +354,31 @@ class OAuthServerException extends Exception
return $this->redirectUri !== null;
}
/**
* Returns the redirectUri with all necessary args.
*
* Null will be returned if the exception doesn't contain the redirectUri.
*
* @param bool $useFragment True if errors should be in the URI fragment instead of query string
*
* @return string|null
*/
public function getRedirectUri(bool $useFragment = false): ?string
{
if ($this->redirectUri === null) {
return null;
}
$redirectUri = $this->redirectUri;
if ($useFragment) {
$redirectUri .= strpos($this->redirectUri, '#') === false ? '#' : '&';
} else {
$redirectUri .= strpos($this->redirectUri, '?') === false ? '?' : '&';
}
return $redirectUri . http_build_query($this->getPayload());
}
/**
* Returns the HTTP status code to send when the exceptions is output.
*

View File

@@ -12,6 +12,7 @@
namespace League\OAuth2\Server\Grant;
use DateInterval;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\RequestEvent;
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
use Psr\Http\Message\ServerRequestInterface;
@@ -29,8 +30,19 @@ class ClientCredentialsGrant extends AbstractGrant
ResponseTypeInterface $responseType,
DateInterval $accessTokenTTL
) {
list($clientId) = $this->getClientCredentials($request);
$client = $this->getClientEntityOrFail($clientId, $request);
if (!$client->isConfidential()) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient($request);
}
// Validate request
$client = $this->validateClient($request);
$this->validateClient($request);
$scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope));
// Finalize the requested scopes

View File

@@ -62,8 +62,11 @@ class AuthorizationServerTest extends TestCase
public function testRespondToRequest()
{
$client = new ClientEntity();
$client->setConfidential();
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepository->method('getClientEntity')->willReturn(new ClientEntity());
$clientRepository->method('getClientEntity')->willReturn($client);
$scope = new ScopeEntity();
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();

View File

@@ -71,6 +71,14 @@ class OAuthServerExceptionTest extends TestCase
$exceptionWithRedirect = OAuthServerException::accessDenied('some hint', 'https://example.com/error');
$this->assertTrue($exceptionWithRedirect->hasRedirect());
$this->assertSame(
'https://example.com/error?error=access_denied&error_description=The+resource+owner+or+authorization+server+denied+the+request.&hint=some+hint&message=The+resource+owner+or+authorization+server+denied+the+request.',
$exceptionWithRedirect->getRedirectUri()
);
$this->assertSame(
'https://example.com/error#error=access_denied&error_description=The+resource+owner+or+authorization+server+denied+the+request.&hint=some+hint&message=The+resource+owner+or+authorization+server+denied+the+request.',
$exceptionWithRedirect->getRedirectUri(true)
);
}
public function testDoesNotHaveRedirect()
@@ -78,6 +86,7 @@ class OAuthServerExceptionTest extends TestCase
$exceptionWithoutRedirect = OAuthServerException::accessDenied('Some hint');
$this->assertFalse($exceptionWithoutRedirect->hasRedirect());
$this->assertNull($exceptionWithoutRedirect->getRedirectUri());
}
public function testHasPrevious()

View File

@@ -29,6 +29,8 @@ class ClientCredentialsGrantTest extends TestCase
public function testRespondToRequest()
{
$client = new ClientEntity();
$client->setConfidential();
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);

View File

@@ -24,8 +24,11 @@ class AuthorizationServerMiddlewareTest extends TestCase
public function testValidResponse()
{
$client = new ClientEntity();
$client->setConfidential();
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepository->method('getClientEntity')->willReturn(new ClientEntity());
$clientRepository->method('getClientEntity')->willReturn($client);
$scopeEntity = new ScopeEntity;
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();