Skip to content

Commit 7826675

Browse files
authored
Merge branch 'master' into rfc8252-compliance
2 parents 58bd2ba + b1ca467 commit 7826675

17 files changed

+286
-33
lines changed

.github/dependabot.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: composer
4+
directory: "/"
5+
schedule:
6+
interval: daily
7+
time: "11:00"
8+
open-pull-requests-limit: 10
9+
ignore:
10+
- dependency-name: league/event
11+
versions:
12+
- 3.0.0

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/vendor
22
/composer.lock
33
phpunit.xml
4+
.phpunit.result.cache
45
.idea
56
/examples/vendor
67
examples/public.key

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
66

77
## [Unreleased]
88
### Added
9-
The server will now validate redirect uris according to rfc8252 (PR #1203)
9+
- The server will now validate redirect uris according to rfc8252 (PR #1203)
10+
- Events emitted now include the refresh token and access token payloads (PR #1211)
1011

1112
### Fixed
1213
- 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)
14+
- Added type validation for redirect uri, client ID, client secret, scopes, auth code, state, username, and password inputs (PR #1210)
1315

1416
## [8.2.4] - released 2020-12-10
1517
### Fixed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"php": "^7.2 || ^8.0",
88
"ext-openssl": "*",
99
"league/event": "^2.2",
10-
"lcobucci/jwt": "^3.4 || ^4.0",
10+
"lcobucci/jwt": "^3.4 || ~4.0.0",
1111
"psr/http-message": "^1.0.1",
1212
"defuse/php-encryption": "^2.2.1",
1313
"ext-json": "*"

src/AuthorizationValidators/BearerTokenValidator.php

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,9 @@
1212
use DateTimeZone;
1313
use Lcobucci\Clock\SystemClock;
1414
use Lcobucci\JWT\Configuration;
15-
use Lcobucci\JWT\Encoding\CannotDecodeContent;
1615
use Lcobucci\JWT\Signer\Key\InMemory;
1716
use Lcobucci\JWT\Signer\Key\LocalFileReference;
1817
use Lcobucci\JWT\Signer\Rsa\Sha256;
19-
use Lcobucci\JWT\Token\InvalidTokenStructure;
20-
use Lcobucci\JWT\Token\UnsupportedHeaderFound;
2118
use Lcobucci\JWT\Validation\Constraint\SignedWith;
2219
use Lcobucci\JWT\Validation\Constraint\ValidAt;
2320
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
@@ -95,18 +92,18 @@ public function validateAuthorization(ServerRequestInterface $request)
9592
$jwt = \trim((string) \preg_replace('/^(?:\s+)?Bearer\s/', '', $header[0]));
9693

9794
try {
98-
// Attempt to parse and validate the JWT
95+
// Attempt to parse the JWT
9996
$token = $this->jwtConfiguration->parser()->parse($jwt);
97+
} catch (\Lcobucci\JWT\Exception $exception) {
98+
throw OAuthServerException::accessDenied($exception->getMessage(), null, $exception);
99+
}
100100

101+
try {
102+
// Attempt to validate the JWT
101103
$constraints = $this->jwtConfiguration->validationConstraints();
102-
103-
try {
104-
$this->jwtConfiguration->validator()->assert($token, ...$constraints);
105-
} catch (RequiredConstraintsViolated $exception) {
106-
throw OAuthServerException::accessDenied('Access token could not be verified');
107-
}
108-
} catch (CannotDecodeContent | InvalidTokenStructure | UnsupportedHeaderFound $exception) {
109-
throw OAuthServerException::accessDenied($exception->getMessage(), null, $exception);
104+
$this->jwtConfiguration->validator()->assert($token, ...$constraints);
105+
} catch (RequiredConstraintsViolated $exception) {
106+
throw OAuthServerException::accessDenied('Access token could not be verified');
110107
}
111108

112109
$claims = $token->claims();

src/Grant/AbstractGrant.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,10 @@ protected function validateClient(ServerRequestInterface $request)
192192
$redirectUri = $this->getRequestParameter('redirect_uri', $request, null);
193193

194194
if ($redirectUri !== null) {
195+
if (!\is_string($redirectUri)) {
196+
throw OAuthServerException::invalidRequest('redirect_uri');
197+
}
198+
195199
$this->validateRedirectUri($redirectUri, $client, $request);
196200
}
197201

@@ -239,12 +243,16 @@ protected function getClientCredentials(ServerRequestInterface $request)
239243

240244
$clientId = $this->getRequestParameter('client_id', $request, $basicAuthUser);
241245

242-
if (\is_null($clientId)) {
246+
if (!\is_string($clientId)) {
243247
throw OAuthServerException::invalidRequest('client_id');
244248
}
245249

246250
$clientSecret = $this->getRequestParameter('client_secret', $request, $basicAuthPassword);
247251

252+
if ($clientSecret !== null && !\is_string($clientSecret)) {
253+
throw OAuthServerException::invalidRequest('client_secret');
254+
}
255+
248256
return [$clientId, $clientSecret];
249257
}
250258

@@ -282,10 +290,16 @@ protected function validateRedirectUri(
282290
*/
283291
public function validateScopes($scopes, $redirectUri = null)
284292
{
285-
if (!\is_array($scopes)) {
293+
if ($scopes === null) {
294+
$scopes = [];
295+
} elseif (\is_string($scopes)) {
286296
$scopes = $this->convertScopesQueryStringToArray($scopes);
287297
}
288298

299+
if (!\is_array($scopes)) {
300+
throw OAuthServerException::invalidRequest('scope');
301+
}
302+
289303
$validScopes = [];
290304

291305
foreach ($scopes as $scopeItem) {
@@ -308,7 +322,7 @@ public function validateScopes($scopes, $redirectUri = null)
308322
*
309323
* @return array
310324
*/
311-
private function convertScopesQueryStringToArray($scopes)
325+
private function convertScopesQueryStringToArray(string $scopes)
312326
{
313327
return \array_filter(\explode(self::SCOPE_DELIMITER_STRING, \trim($scopes)), function ($scope) {
314328
return !empty($scope);

src/Grant/AuthCodeGrant.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
use League\OAuth2\Server\Exception\OAuthServerException;
2121
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
2222
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
23+
use League\OAuth2\Server\RequestAccessTokenEvent;
2324
use League\OAuth2\Server\RequestEvent;
25+
use League\OAuth2\Server\RequestRefreshTokenEvent;
2426
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
2527
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
2628
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
@@ -106,7 +108,7 @@ public function respondToAccessTokenRequest(
106108

107109
$encryptedAuthCode = $this->getRequestParameter('code', $request, null);
108110

109-
if ($encryptedAuthCode === null) {
111+
if (!\is_string($encryptedAuthCode)) {
110112
throw OAuthServerException::invalidRequest('code');
111113
}
112114

@@ -162,14 +164,14 @@ public function respondToAccessTokenRequest(
162164

163165
// Issue and persist new access token
164166
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $authCodePayload->user_id, $scopes);
165-
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
167+
$this->getEmitter()->emit(new RequestAccessTokenEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request, $accessToken));
166168
$responseType->setAccessToken($accessToken);
167169

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

171173
if ($refreshToken !== null) {
172-
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
174+
$this->getEmitter()->emit(new RequestRefreshTokenEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request, $refreshToken));
173175
$responseType->setRefreshToken($refreshToken);
174176
}
175177

@@ -260,6 +262,10 @@ public function validateAuthorizationRequest(ServerRequestInterface $request)
260262
$redirectUri = $this->getQueryStringParameter('redirect_uri', $request);
261263

262264
if ($redirectUri !== null) {
265+
if (!\is_string($redirectUri)) {
266+
throw OAuthServerException::invalidRequest('redirect_uri');
267+
}
268+
263269
$this->validateRedirectUri($redirectUri, $client, $request);
264270
} elseif (empty($client->getRedirectUri()) ||
265271
(\is_array($client->getRedirectUri()) && \count($client->getRedirectUri()) !== 1)) {

src/Grant/ClientCredentialsGrant.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use DateInterval;
1515
use League\OAuth2\Server\Exception\OAuthServerException;
16+
use League\OAuth2\Server\RequestAccessTokenEvent;
1617
use League\OAuth2\Server\RequestEvent;
1718
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
1819
use Psr\Http\Message\ServerRequestInterface;
@@ -52,7 +53,7 @@ public function respondToAccessTokenRequest(
5253
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, null, $finalizedScopes);
5354

5455
// Send event to emitter
55-
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
56+
$this->getEmitter()->emit(new RequestAccessTokenEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request, $accessToken));
5657

5758
// Inject access token into response type
5859
$responseType->setAccessToken($accessToken);

src/Grant/ImplicitGrant.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public function validateAuthorizationRequest(ServerRequestInterface $request)
120120
$this->getServerParameter('PHP_AUTH_USER', $request)
121121
);
122122

123-
if (\is_null($clientId)) {
123+
if (!\is_string($clientId)) {
124124
throw OAuthServerException::invalidRequest('client_id');
125125
}
126126

@@ -129,6 +129,10 @@ public function validateAuthorizationRequest(ServerRequestInterface $request)
129129
$redirectUri = $this->getQueryStringParameter('redirect_uri', $request);
130130

131131
if ($redirectUri !== null) {
132+
if (!\is_string($redirectUri)) {
133+
throw OAuthServerException::invalidRequest('redirect_uri');
134+
}
135+
132136
$this->validateRedirectUri($redirectUri, $client, $request);
133137
} elseif (\is_array($client->getRedirectUri()) && \count($client->getRedirectUri()) !== 1
134138
|| empty($client->getRedirectUri())) {
@@ -147,6 +151,10 @@ public function validateAuthorizationRequest(ServerRequestInterface $request)
147151

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

154+
if ($stateParameter !== null && !\is_string($stateParameter)) {
155+
throw OAuthServerException::invalidRequest('state');
156+
}
157+
150158
$authorizationRequest = new AuthorizationRequest();
151159
$authorizationRequest->setGrantTypeId($this->getIdentifier());
152160
$authorizationRequest->setClient($client);

src/Grant/PasswordGrant.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
use League\OAuth2\Server\Exception\OAuthServerException;
1818
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
1919
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
20+
use League\OAuth2\Server\RequestAccessTokenEvent;
2021
use League\OAuth2\Server\RequestEvent;
22+
use League\OAuth2\Server\RequestRefreshTokenEvent;
2123
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
2224
use Psr\Http\Message\ServerRequestInterface;
2325

@@ -58,14 +60,14 @@ public function respondToAccessTokenRequest(
5860

5961
// Issue and persist new access token
6062
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $finalizedScopes);
61-
$this->getEmitter()->emit(new RequestEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request));
63+
$this->getEmitter()->emit(new RequestAccessTokenEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request, $accessToken));
6264
$responseType->setAccessToken($accessToken);
6365

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

6769
if ($refreshToken !== null) {
68-
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request));
70+
$this->getEmitter()->emit(new RequestRefreshTokenEvent(RequestEvent::REFRESH_TOKEN_ISSUED, $request, $refreshToken));
6971
$responseType->setRefreshToken($refreshToken);
7072
}
7173

@@ -84,13 +86,13 @@ protected function validateUser(ServerRequestInterface $request, ClientEntityInt
8486
{
8587
$username = $this->getRequestParameter('username', $request);
8688

87-
if (\is_null($username)) {
89+
if (!\is_string($username)) {
8890
throw OAuthServerException::invalidRequest('username');
8991
}
9092

9193
$password = $this->getRequestParameter('password', $request);
9294

93-
if (\is_null($password)) {
95+
if (!\is_string($password)) {
9496
throw OAuthServerException::invalidRequest('password');
9597
}
9698

0 commit comments

Comments
 (0)