Add optional code challenge check for public clients

This commit is contained in:
Andrew Millington 2018-08-12 20:06:34 +01:00
parent 5ad00b0e33
commit 972808561d
No known key found for this signature in database
GPG Key ID: 077754CA23023F4F
4 changed files with 142 additions and 30 deletions

120
examples/composer.lock generated
View File

@ -1,10 +1,10 @@
{ {
"_readme": [ "_readme": [
"This file locks the dependencies of your project to a known state", "This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "9813ed7c3b6dcf107f44df9392935b8f", "content-hash": "6701e0eaa09f74e1ebb19c3d61f39068",
"packages": [ "packages": [
{ {
"name": "container-interop/container-interop", "name": "container-interop/container-interop",
@ -82,25 +82,29 @@
}, },
{ {
"name": "pimple/pimple", "name": "pimple/pimple",
"version": "v3.0.2", "version": "v3.2.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/silexphp/Pimple.git", "url": "https://github.com/silexphp/Pimple.git",
"reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a" "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/silexphp/Pimple/zipball/a30f7d6e57565a2e1a316e1baf2a483f788b258a", "url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32",
"reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a", "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.0" "php": ">=5.3.0",
"psr/container": "^1.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^3.2"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "3.0.x-dev" "dev-master": "3.2.x-dev"
} }
}, },
"autoload": { "autoload": {
@ -124,7 +128,7 @@
"container", "container",
"dependency injection" "dependency injection"
], ],
"time": "2015-09-11T15:10:35+00:00" "time": "2018-01-21T07:42:36+00:00"
}, },
{ {
"name": "psr/container", "name": "psr/container",
@ -295,25 +299,25 @@
"packages-dev": [ "packages-dev": [
{ {
"name": "defuse/php-encryption", "name": "defuse/php-encryption",
"version": "v2.1.0", "version": "v2.2.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/defuse/php-encryption.git", "url": "https://github.com/defuse/php-encryption.git",
"reference": "5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689" "reference": "0f407c43b953d571421e0020ba92082ed5fb7620"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/defuse/php-encryption/zipball/5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689", "url": "https://api.github.com/repos/defuse/php-encryption/zipball/0f407c43b953d571421e0020ba92082ed5fb7620",
"reference": "5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689", "reference": "0f407c43b953d571421e0020ba92082ed5fb7620",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-openssl": "*", "ext-openssl": "*",
"paragonie/random_compat": "~2.0", "paragonie/random_compat": ">= 2",
"php": ">=5.4.0" "php": ">=5.4.0"
}, },
"require-dev": { "require-dev": {
"nikic/php-parser": "^2.0|^3.0", "nikic/php-parser": "^2.0|^3.0|^4.0",
"phpunit/phpunit": "^4|^5" "phpunit/phpunit": "^4|^5"
}, },
"bin": [ "bin": [
@ -354,20 +358,20 @@
"security", "security",
"symmetric key cryptography" "symmetric key cryptography"
], ],
"time": "2017-05-18T21:28:48+00:00" "time": "2018-07-24T23:27:56+00:00"
}, },
{ {
"name": "lcobucci/jwt", "name": "lcobucci/jwt",
"version": "3.2.1", "version": "3.2.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/lcobucci/jwt.git", "url": "https://github.com/lcobucci/jwt.git",
"reference": "ddce703826f9c5229781933b1a39069e38e6a0f3" "reference": "c9704b751315d21735dc98d78d4f37bd73596da7"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/ddce703826f9c5229781933b1a39069e38e6a0f3", "url": "https://api.github.com/repos/lcobucci/jwt/zipball/c9704b751315d21735dc98d78d4f37bd73596da7",
"reference": "ddce703826f9c5229781933b1a39069e38e6a0f3", "reference": "c9704b751315d21735dc98d78d4f37bd73596da7",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -412,7 +416,7 @@
"JWS", "JWS",
"jwt" "jwt"
], ],
"time": "2016-10-31T20:09:32+00:00" "time": "2018-08-03T11:23:50+00:00"
}, },
{ {
"name": "league/event", "name": "league/event",
@ -466,16 +470,16 @@
}, },
{ {
"name": "paragonie/random_compat", "name": "paragonie/random_compat",
"version": "v2.0.10", "version": "v2.0.17",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/paragonie/random_compat.git", "url": "https://github.com/paragonie/random_compat.git",
"reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d" "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/634bae8e911eefa89c1abfbf1b66da679ac8f54d", "url": "https://api.github.com/repos/paragonie/random_compat/zipball/29af24f25bab834fcbb38ad2a69fa93b867e070d",
"reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d", "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -507,10 +511,74 @@
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
"keywords": [ "keywords": [
"csprng", "csprng",
"polyfill",
"pseudorandom", "pseudorandom",
"random" "random"
], ],
"time": "2017-03-13T16:27:32+00:00" "time": "2018-07-04T16:31:37+00:00"
},
{
"name": "zendframework/zend-diactoros",
"version": "1.8.4",
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-diactoros.git",
"reference": "736ffa7c2bfa4a60e8a10acb316fa2ac456c5fba"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/736ffa7c2bfa4a60e8a10acb316fa2ac456c5fba",
"reference": "736ffa7c2bfa4a60e8a10acb316fa2ac456c5fba",
"shasum": ""
},
"require": {
"php": "^5.6 || ^7.0",
"psr/http-message": "^1.0"
},
"provide": {
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"ext-dom": "*",
"ext-libxml": "*",
"phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7",
"zendframework/zend-coding-standard": "~1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.8.x-dev",
"dev-develop": "1.9.x-dev",
"dev-release-2.0": "2.0.x-dev"
}
},
"autoload": {
"files": [
"src/functions/create_uploaded_file.php",
"src/functions/marshal_headers_from_sapi.php",
"src/functions/marshal_method_from_sapi.php",
"src/functions/marshal_protocol_version_from_sapi.php",
"src/functions/marshal_uri_from_sapi.php",
"src/functions/normalize_server.php",
"src/functions/normalize_uploaded_files.php",
"src/functions/parse_cookie_header.php"
],
"psr-4": {
"Zend\\Diactoros\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-2-Clause"
],
"description": "PSR HTTP Message implementations",
"homepage": "https://github.com/zendframework/zend-diactoros",
"keywords": [
"http",
"psr",
"psr-7"
],
"time": "2018-08-01T13:47:49+00:00"
} }
], ],
"aliases": [], "aliases": [],

View File

@ -33,4 +33,11 @@ interface ClientEntityInterface
* @return string|string[] * @return string|string[]
*/ */
public function getRedirectUri(); public function getRedirectUri();
/**
* Returns true if the client is confidential.
*
* @return bool
*/
public function isConfidential();
} }

View File

@ -28,6 +28,11 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
*/ */
private $authCodeTTL; private $authCodeTTL;
/**
* @var bool
*/
private $requireCodeChallengeForPublicClients = true;
/** /**
* @param AuthCodeRepositoryInterface $authCodeRepository * @param AuthCodeRepositoryInterface $authCodeRepository
* @param RefreshTokenRepositoryInterface $refreshTokenRepository * @param RefreshTokenRepositoryInterface $refreshTokenRepository
@ -60,8 +65,29 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
ResponseTypeInterface $responseType, ResponseTypeInterface $responseType,
\DateInterval $accessTokenTTL \DateInterval $accessTokenTTL
) { ) {
// Validate request $clientId = $this->getRequestParameter('client_id', $request, null);
if ($clientId === null) {
throw OAuthServerException::invalidRequest('client_id');
}
if ($this->clientRepository->isClientConfidential($clientId)) {
$client = $this->validateClient($request); $client = $this->validateClient($request);
} else {
$client = $this->clientRepository->getClientEntity(
$clientId,
$this->getIdentifier(),
null,
false
);
}
// Validate request
// HERE I ONLY WANT TO VALIDATE IF THE CLIENT IS CONFIDENTIAL!
$encryptedAuthCode = $this->getRequestParameter('code', $request, null); $encryptedAuthCode = $this->getRequestParameter('code', $request, null);
if ($encryptedAuthCode === null) { if ($encryptedAuthCode === null) {
@ -86,6 +112,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
// The redirect URI is required in this request // The redirect URI is required in this request
$redirectUri = $this->getRequestParameter('redirect_uri', $request, null); $redirectUri = $this->getRequestParameter('redirect_uri', $request, null);
if (empty($authCodePayload->redirect_uri) === false && $redirectUri === null) { if (empty($authCodePayload->redirect_uri) === false && $redirectUri === null) {
throw OAuthServerException::invalidRequest('redirect_uri'); throw OAuthServerException::invalidRequest('redirect_uri');
} }
@ -119,7 +146,6 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
throw OAuthServerException::invalidRequest('code', 'Cannot decrypt the authorization code'); throw OAuthServerException::invalidRequest('code', 'Cannot decrypt the authorization code');
} }
// Validate code challenge // Validate code challenge
if (!empty($authCodePayload->code_challenge)) { if (!empty($authCodePayload->code_challenge)) {
$codeVerifier = $this->getRequestParameter('code_verifier', $request, null); $codeVerifier = $this->getRequestParameter('code_verifier', $request, null);
@ -164,6 +190,8 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
); );
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
} }
} else if ($this->$requireCodeChallengeForPublicClients && !$client->isConfidential()) {
throw OAuthServerException::invalidRequest('code_challenge', 'Code challenge must be provided for public clients');
} }
// Issue and persist access + refresh tokens // Issue and persist access + refresh tokens

View File

@ -28,4 +28,13 @@ interface ClientRepositoryInterface extends RepositoryInterface
* @return ClientEntityInterface * @return ClientEntityInterface
*/ */
public function getClientEntity($clientIdentifier, $grantType = null, $clientSecret = null, $mustValidateSecret = true); public function getClientEntity($clientIdentifier, $grantType = null, $clientSecret = null, $mustValidateSecret = true);
/**
* Check if a client is confidential.
*
* @param string $clientIdentifier The client's identifier
*
* @return bool
*/
public function isClientConfidential($clientIdentifier);
} }