fixed comments, names and changelog
MODIFY
CHANGELOG.md
CHANGELOG.md
@@ -5,6 +5,9 @@ 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
+The server will now validate redirect uris according to rfc8252 (PR #1203)
+
### Fixed
- The server will now only recognise and handle an authorization header if the value of the header is non-empty. This is to circumvent issues where some common frameworks set this header even if no value is present (PR #1170)

MODIFY
src/Grant/AbstractGrant.php
src/Grant/AbstractGrant.php
@@ -24,7 +24,7 @@ use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
-use League\OAuth2\Server\RedirectUriValidators\Rfc8252RedirectUriValidator;
+use League\OAuth2\Server\RedirectUriValidators\RedirectUriValidator;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
@@ -263,8 +263,9 @@ abstract class AbstractGrant implements GrantTypeInterface
ClientEntityInterface $client,
ServerRequestInterface $request
) {
- $validator = new Rfc8252RedirectUriValidator($client);
+ $validator = new RedirectUriValidator($client);
if (!$validator->validateRedirectUri($redirectUri)) {
+ $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient($request);
}
}

ADD
src/RedirectUriValidators/RedirectUriValidator.php
+<?php
+/**
+ * @author Sebastiano Degan <sebdeg87@gmail.com>
+ * @copyright Copyright (c) Sebastiano Degan
+ * @license http://mit-license.org/
+ *
+ * @link https://github.com/thephpleague/oauth2-server
+ */
+
+namespace League\OAuth2\Server\RedirectUriValidators;
+
+use League\OAuth2\Server\Entities\ClientEntityInterface;
+
+class RedirectUriValidator implements RedirectUriValidatorInterface
+{
+ /**
+ * @var ClientEntityInterface
+ */
+ private $client;
+
+ /**
+ * New validator instance for the given client
+ *
+ * @param ClientEntityInterface $client
+ */
+ public function __construct(ClientEntityInterface $client)
+ {
+ $this->client = $client;
+ }
+
+ /**
+ * Validates the redirect uri.
+ *
+ * @param string $redirectUri
+ *
+ * @return bool Return true if valid, false otherwise
+ */
+ public function validateRedirectUri($redirectUri)
+ {
+ $parsedUrl = $this->parseUrl($redirectUri);
+ if ($this->isLoopbackAddress($parsedUrl)) {
+ return $this->allowDifferentPort($parsedUrl);
+ } else {
+ return $this->matchExactUri($redirectUri);
+ }
+ }
+
+ /**
+ * Determine if the given url is a loopback url.
+ *
+ * @param array $parsedUri As returned by parseUrl
+ *
+ * @return bool
+ */
+ private function isLoopbackAddress(array $parsedUri)
+ {
+ return $parsedUri['scheme'] === 'http'
+ && (\in_array($parsedUri['host'], ['127.0.0.1', '[::1]'], true));
+ }
+
+ /**
+ * Find an exact match among client uris
+ *
+ * @param string $redirectUri
+ *
+ * @return bool Return true if an exact match is found, false otherwise
+ */
+ private function matchExactUri($redirectUri)
+ {
+ return \in_array($redirectUri, $this->getClientRedirectUris(), true);
+ }
+
+ /**
+ * Find a match among client uris, allowing for different port numbers
+ *
+ * @param array $parsedUrl As returned by parseUrl
+ *
+ * @return bool Return true if a match is found, false otherwise
+ */
+ private function allowDifferentPort(array $parsedUrl)
+ {
+ foreach ($this->getClientRedirectUris() as $clientRedirectUri) {
+ if ($parsedUrl == $this->parseUrl($clientRedirectUri)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Parse an url like \parse_url, excluding the port
+ *
+ * @return array
+ */
+ private function parseUrl(string $url)
+ {
+ $parsedUrl = \parse_url($url);
+ unset($parsedUrl['port']);
+
+ return $parsedUrl;
+ }
+
+ /**
+ * Retrieve allowed client redirect uris
+ *
+ * @return array
+ */
+ private function getClientRedirectUris()
+ {
+ $clientRedirectUri = $this->client->getRedirectUri();
+ if (\is_string($clientRedirectUri)) {
+ return [$clientRedirectUri];
+ } elseif (\is_array($clientRedirectUri)) {
+ return $clientRedirectUri;
+ } else {
+ return [];
+ }
+ }
+}

ADD
src/RedirectUriValidators/RedirectUriValidatorInterface.php
+<?php
+/**
+ * @author Sebastiano Degan <sebdeg87@gmail.com>
+ * @copyright Copyright (c) Sebastiano Degan
+ * @license http://mit-license.org/
+ *
+ * @link https://github.com/thephpleague/oauth2-server
+ */
+
+namespace League\OAuth2\Server\RedirectUriValidators;
+
+interface RedirectUriValidatorInterface
+{
+ /**
+ * Validates the redirect uri.
+ *
+ * @param string $redirectUri
+ *
+ * @return bool Return true if valid, false otherwise
+ */
+ public function validateRedirectUri($redirectUri);
+}

DELETE
src/RedirectUriValidators/Rfc8252RedirectUriValidator.php
-<?php
-
-
-namespace League\OAuth2\Server\RedirectUriValidators;
-
-use League\OAuth2\Server\Entities\ClientEntityInterface;
-
-class Rfc8252RedirectUriValidator
-{
- private $client;
-
- public function __construct(ClientEntityInterface $client)
- {
- $this->client = $client;
- }
-
- public function validateRedirectUri(string $redirectUri)
- {
- $parsedUrl = $this->parseUrl($redirectUri);
- if ($this->isLoopbackAddress($parsedUrl)) {
- return $this->allowDifferentPort($parsedUrl);
- } else {
- return $this->matchFullUri($redirectUri);
- }
- }
-
- private function isLoopbackAddress(array $parsedUri)
- {
- return $parsedUri['scheme'] === 'http'
- && (\in_array($parsedUri['host'], ['127.0.0.1', '[::1]'], true));
- }
-
- private function matchFullUri(string $redirectUri)
- {
- return \in_array($redirectUri, $this->getClientRedirectUris(), true);
- }
-
- private function allowDifferentPort(array $parsedUrl)
- {
- foreach ($this->getClientRedirectUris() as $clientRedirectUri) {
- if ($parsedUrl == $this->parseUrl($clientRedirectUri)) {
- return true;
- }
- }
-
- return false;
- }
-
- private function parseUrl(string $url)
- {
- $parsedUrl = \parse_url($url);
- $parsedUrl['port'] = 80;
-
- return $parsedUrl;
- }
-
- private function getClientRedirectUris()
- {
- $clientRedirectUri = $this->client->getRedirectUri();
- if (\is_string($clientRedirectUri)) {
- return [$clientRedirectUri];
- } elseif (\is_array($clientRedirectUri)) {
- return $clientRedirectUri;
- } else {
- return [];
- }
- }
-}

RENAME
tests/RedirectUriValidators/Rfc8252RedirectUriValidatorTest.php
tests/RedirectUriValidators/RedirectUriValidatorTest.php
<?php
-
namespace LeagueTests\RedirectUriValidators;
-use League\OAuth2\Server\RedirectUriValidators\Rfc8252RedirectUriValidator;
+use League\OAuth2\Server\RedirectUriValidators\RedirectUriValidator;
use LeagueTests\Stubs\ClientEntity;
use PHPUnit\Framework\TestCase;
-class Rfc8252RedirectUriValidatorTest extends TestCase
+class RedirectUriValidatorTest extends TestCase
{
public function testInvalidNonLoopbackUri()
{
@@ -18,7 +17,7 @@ class Rfc8252RedirectUriValidatorTest extends TestCase
]);
$redirectUri = 'https://example.com/endpoint';
- $validator = new Rfc8252RedirectUriValidator($client);
+ $validator = new RedirectUriValidator($client);
$this->assertFalse(
$validator->validateRedirectUri($redirectUri),
@@ -35,7 +34,7 @@ class Rfc8252RedirectUriValidatorTest extends TestCase
]);
$redirectUri = 'https://example.com:8443/endpoint';
- $validator = new Rfc8252RedirectUriValidator($client);
+ $validator = new RedirectUriValidator($client);
$this->assertTrue(
$validator->validateRedirectUri($redirectUri),
@@ -50,7 +49,7 @@ class Rfc8252RedirectUriValidatorTest extends TestCase
$redirectUri = 'http://127.0.0.1:8443/different/endpoint';
- $validator = new Rfc8252RedirectUriValidator($client);
+ $validator = new RedirectUriValidator($client);
$this->assertFalse(
$validator->validateRedirectUri($redirectUri),
@@ -65,7 +64,7 @@ class Rfc8252RedirectUriValidatorTest extends TestCase
$redirectUri = 'http://127.0.0.1:8080/endpoint';
- $validator = new Rfc8252RedirectUriValidator($client);
+ $validator = new RedirectUriValidator($client);
$this->assertTrue(
$validator->validateRedirectUri($redirectUri),
@@ -80,7 +79,7 @@ class Rfc8252RedirectUriValidatorTest extends TestCase
$redirectUri = 'http://[::1]:8080/endpoint';
- $validator = new Rfc8252RedirectUriValidator($client);
+ $validator = new RedirectUriValidator($client);
$this->assertTrue(
$validator->validateRedirectUri($redirectUri),

fixed comments, names and changelog