diff --git a/.travis.yml b/.travis.yml index 13962424..1f085674 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,12 +7,9 @@ cache: - vendor php: - - 5.5.9 - - 5.5 - 5.6 - 7.0 - 7.1 - - hhvm install: - travis_retry composer install --no-interaction --prefer-source diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ae31e9d..958a941e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## 6.0.2 (released 2017-08-03) + +* An invalid refresh token that can't be decrypted now returns a HTTP 401 error instead of HTTP 400 (Issue #759) +* Removed chmod from CryptKey and add toggle to disable checking (Issue #776) +* Fixes invalid code challenge method payload key name (Issue #777) + +## 6.0.1 (released 2017-07-19) + +To address feedback from the security release the following change has been made: + +* If an RSA key cannot be chmod'ed to 600 then it will now throw a E_USER_NOTICE instead of an exception. + +## 6.0.0 (released 2017-07-01) + +* Breaking change: The `AuthorizationServer` constructor now expects an encryption key string instead of a public key +* Remove support for HHVM +* Remove support for PHP 5.5 + +## 5.1.4 (released 2017-07-01) + +* Fixed multiple security vulnerabilities as a result of a security audit paid for by the [Mozilla Secure Open Source Fund](https://wiki.mozilla.org/MOSS/Secure_Open_Source). All users of this library are encouraged to update as soon as possible to this version or version 6.0 or greater. + * It is recommended on each `AuthorizationServer` instance you set the `setEncryptionKey()`. This will result in stronger encryption being used. If this method is not set messages will be sent to the defined error handling routines (using `error_log`). Please see the examples and documentation for examples. +* TravisCI now tests PHP 7.1 (Issue #671) +* Fix middleware example fatal error (Issue #682) +* Fix typo in the first README sentence (Issue #690) +* Corrected DateInterval from 1 min to 1 month (Issue #709) + ## 5.1.3 (released 2016-10-12) * Fixed WWW-Authenticate header (Issue #669) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0391ae5e..af439e0a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,7 @@ Thanks for contributing to this project. -**Please submit your pull request against the `develop` branch only.** +**Please submit your pull request against the `master` branch only.** Please ensure that you run `phpunit` from the project root after you've made any changes. diff --git a/README.md b/README.md index de5f3471..93e3341e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # PHP OAuth 2.0 Server +### :warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning: +### Security Notice + +### Please upgrade to version `>=5.1.4` (backwards compatible) or `6.x` (one tiny breaking change) to fix some potential security vulnerabilities - [visit this page for more information](https://oauth2.thephpleague.com/v5-security-improvements/) +### :warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning: + [![Latest Version](http://img.shields.io/packagist/v/league/oauth2-server.svg?style=flat-square)](https://github.com/thephpleague/oauth2-server/releases) [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) [![Build Status](https://img.shields.io/travis/thephpleague/oauth2-server/master.svg?style=flat-square)](https://travis-ci.org/thephpleague/oauth2-server) @@ -7,7 +13,7 @@ [![Quality Score](https://img.shields.io/scrutinizer/g/thephpleague/oauth2-server.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/oauth2-server) [![Total Downloads](https://img.shields.io/packagist/dt/league/oauth2-server.svg?style=flat-square)](https://packagist.org/packages/league/oauth2-server) -`league/oauth2-server` is a a standards compliant implementation of an [OAuth 2.0](https://tools.ietf.org/html/rfc6749) authorization server written in PHP which makes working with OAuth 2.0 trivial. You can easily configure an OAuth 2.0 server to protect your API with access tokens, or allow clients to request new access tokens and refresh them. +`league/oauth2-server` is a standards compliant implementation of an [OAuth 2.0](https://tools.ietf.org/html/rfc6749) authorization server written in PHP which makes working with OAuth 2.0 trivial. You can easily configure an OAuth 2.0 server to protect your API with access tokens, or allow clients to request new access tokens and refresh them. It supports out of the box the following grants: @@ -30,10 +36,9 @@ This library was created by Alex Bilbie. Find him on Twitter at [@alexbilbie](ht The following versions of PHP are supported: -* PHP 5.5 (>=5.5.9) * PHP 5.6 * PHP 7.0 -* HHVM +* PHP 7.1 The `openssl` extension is also required. @@ -56,6 +61,10 @@ Bugs and feature request are tracked on [GitHub](https://github.com/thephpleague If you have any questions about OAuth _please_ open a ticket here; please **don't** email the address below. + + Sponsor + + ## Commercial Support If you would like help implementing this library into your existing platform, or would be interested in OAuth advice or training for you and your team please get in touch with [Glynde Labs](https://glyndelabs.com). @@ -70,8 +79,13 @@ This package is released under the MIT License. See the bundled [LICENSE](https: ## Credits -This code is principally developed and maintained by [Alex Bilbie](https://twitter.com/alexbilbie). +This code is principally developed and maintained by [Andy Millington](https://twitter.com/Sephster), [Brian +Retterer](https://twitter.com/bretterer), and [Simon Hamp](https://twitter.com/simonhamp). -Special thanks to [all of these awesome contributors](https://github.com/thephpleague/oauth2-server/contributors) +Between 2012 and 2017 this library was developed and maintained by [Alex Bilbie](https://alexbilbie.com/). + +Special thanks to [all of these awesome contributors](https://github.com/thephpleague/oauth2-server/contributors). + +Additional thanks go to the [Mozilla Secure Open Source Fund](https://wiki.mozilla.org/MOSS/Secure_Open_Source) for funding a security audit of this library. The initial code was developed as part of the [Linkey](http://linkey.blogs.lincoln.ac.uk) project which was funded by [JISC](http://jisc.ac.uk) under the Access and Identity Management programme. diff --git a/composer.json b/composer.json index 64df3ccb..d6740aa4 100644 --- a/composer.json +++ b/composer.json @@ -4,17 +4,17 @@ "homepage": "https://oauth2.thephpleague.com/", "license": "MIT", "require": { - "php": ">=5.5.9", + "php": ">=5.6.0", "ext-openssl": "*", "league/event": "^2.1", "lcobucci/jwt": "^3.1", - "paragonie/random_compat": "^1.1 || ^2.0", - "psr/http-message": "^1.0" + "paragonie/random_compat": "^2.0", + "psr/http-message": "^1.0", + "defuse/php-encryption": "^2.1" }, "require-dev": { "phpunit/phpunit": "^4.8 || ^5.0", - "zendframework/zend-diactoros": "^1.0", - "indigophp/hash-compat": "^1.1" + "zendframework/zend-diactoros": "^1.0" }, "repositories": [ { @@ -59,13 +59,5 @@ "psr-4": { "LeagueTests\\": "tests/" } - }, - "extra": { - "branch-alias": { - "dev-V5-WIP": "5.0-dev" - } - }, - "suggest": { - "indigophp/hash-compat": "Polyfill for hash_equals function for PHP 5.5" } } diff --git a/examples/composer.json b/examples/composer.json index 59d6d557..ec7387cf 100644 --- a/examples/composer.json +++ b/examples/composer.json @@ -5,8 +5,9 @@ "require-dev": { "league/event": "^2.1", "lcobucci/jwt": "^3.1", - "paragonie/random_compat": "^1.1", + "paragonie/random_compat": "^2.0", "psr/http-message": "^1.0", + "defuse/php-encryption": "^2.1", "zendframework/zend-diactoros": "^1.0" }, "autoload": { diff --git a/examples/composer.lock b/examples/composer.lock index 9c6c83cb..7210f31e 100644 --- a/examples/composer.lock +++ b/examples/composer.lock @@ -4,23 +4,25 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "48bcb7a3514d7c7f271c554ba1440124", - "content-hash": "e41be75973527cb9d63f27ad14ac8624", + "content-hash": "9813ed7c3b6dcf107f44df9392935b8f", "packages": [ { "name": "container-interop/container-interop", - "version": "1.1.0", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/container-interop/container-interop.git", - "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e" + "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/container-interop/container-interop/zipball/fc08354828f8fd3245f77a66b9e23a6bca48297e", - "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e", + "url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8", + "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8", "shasum": "" }, + "require": { + "psr/container": "^1.0" + }, "type": "library", "autoload": { "psr-4": { @@ -32,7 +34,8 @@ "MIT" ], "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", - "time": "2014-12-30 15:22:37" + "homepage": "https://github.com/container-interop/container-interop", + "time": "2017-02-14T19:40:03+00:00" }, { "name": "nikic/fast-route", @@ -75,7 +78,7 @@ "router", "routing" ], - "time": "2015-06-18 19:15:47" + "time": "2015-06-18T19:15:47+00:00" }, { "name": "pimple/pimple", @@ -121,20 +124,69 @@ "container", "dependency injection" ], - "time": "2015-09-11 15:10:35" + "time": "2015-09-11T15:10:35+00:00" }, { - "name": "psr/http-message", - "version": "1.0", + "name": "psr/container", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298" + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/85d63699f0dbedb190bbd4b0d2b9dc707ea4c298", - "reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "time": "2017-02-14T16:28:37+00:00" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", "shasum": "" }, "require": { @@ -162,6 +214,7 @@ } ], "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", "keywords": [ "http", "http-message", @@ -170,7 +223,7 @@ "request", "response" ], - "time": "2015-05-04 20:22:00" + "time": "2016-08-06T14:39:51+00:00" }, { "name": "slim/slim", @@ -236,22 +289,85 @@ "micro", "router" ], - "time": "2015-12-07 14:11:09" + "time": "2015-12-07T14:11:09+00:00" } ], "packages-dev": [ { - "name": "lcobucci/jwt", - "version": "3.1.1", + "name": "defuse/php-encryption", + "version": "v2.1.0", "source": { "type": "git", - "url": "https://github.com/lcobucci/jwt.git", - "reference": "afea8e682e911a21574fd8519321b32522fa25b5" + "url": "https://github.com/defuse/php-encryption.git", + "reference": "5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/jwt/zipball/afea8e682e911a21574fd8519321b32522fa25b5", - "reference": "afea8e682e911a21574fd8519321b32522fa25b5", + "url": "https://api.github.com/repos/defuse/php-encryption/zipball/5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689", + "reference": "5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "paragonie/random_compat": "~2.0", + "php": ">=5.4.0" + }, + "require-dev": { + "nikic/php-parser": "^2.0|^3.0", + "phpunit/phpunit": "^4|^5" + }, + "bin": [ + "bin/generate-defuse-key" + ], + "type": "library", + "autoload": { + "psr-4": { + "Defuse\\Crypto\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Hornby", + "email": "taylor@defuse.ca", + "homepage": "https://defuse.ca/" + }, + { + "name": "Scott Arciszewski", + "email": "info@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "Secure PHP Encryption Library", + "keywords": [ + "aes", + "authenticated encryption", + "cipher", + "crypto", + "cryptography", + "encrypt", + "encryption", + "openssl", + "security", + "symmetric key cryptography" + ], + "time": "2017-05-18T21:28:48+00:00" + }, + { + "name": "lcobucci/jwt", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/jwt.git", + "reference": "ddce703826f9c5229781933b1a39069e38e6a0f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/ddce703826f9c5229781933b1a39069e38e6a0f3", + "reference": "ddce703826f9c5229781933b1a39069e38e6a0f3", "shasum": "" }, "require": { @@ -259,7 +375,7 @@ "php": ">=5.5" }, "require-dev": { - "mdanter/ecc": "~0.3", + "mdanter/ecc": "~0.3.1", "mikey179/vfsstream": "~1.5", "phpmd/phpmd": "~2.2", "phpunit/php-invoker": "~1.1", @@ -296,7 +412,7 @@ "JWS", "jwt" ], - "time": "2016-03-24 22:46:13" + "time": "2016-10-31T20:09:32+00:00" }, { "name": "league/event", @@ -346,20 +462,20 @@ "event", "listener" ], - "time": "2015-05-21 12:24:47" + "time": "2015-05-21T12:24:47+00:00" }, { "name": "paragonie/random_compat", - "version": "v1.4.1", + "version": "v2.0.10", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "c7e26a21ba357863de030f0b9e701c7d04593774" + "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/c7e26a21ba357863de030f0b9e701c7d04593774", - "reference": "c7e26a21ba357863de030f0b9e701c7d04593774", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/634bae8e911eefa89c1abfbf1b66da679ac8f54d", + "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d", "shasum": "" }, "require": { @@ -394,7 +510,7 @@ "pseudorandom", "random" ], - "time": "2016-03-18 20:34:03" + "time": "2017-03-13T16:27:32+00:00" } ], "aliases": [], diff --git a/examples/public/auth_code.php b/examples/public/auth_code.php index e014f55a..3c4ca68d 100644 --- a/examples/public/auth_code.php +++ b/examples/public/auth_code.php @@ -36,7 +36,6 @@ $app = new App([ $refreshTokenRepository = new RefreshTokenRepository(); $privateKeyPath = 'file://' . __DIR__ . '/../private.key'; - $publicKeyPath = 'file://' . __DIR__ . '/../public.key'; // Setup the authorization server $server = new AuthorizationServer( @@ -44,7 +43,7 @@ $app = new App([ $accessTokenRepository, $scopeRepository, $privateKeyPath, - $publicKeyPath + 'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen' ); // Enable the authentication code grant on the server with a token TTL of 1 hour diff --git a/examples/public/client_credentials.php b/examples/public/client_credentials.php index c982f275..433fbdce 100644 --- a/examples/public/client_credentials.php +++ b/examples/public/client_credentials.php @@ -32,7 +32,6 @@ $app = new App([ // Path to public and private keys $privateKey = 'file://' . __DIR__ . '/../private.key'; //$privateKey = new CryptKey('file://path/to/private.key', 'passphrase'); // if private key has a pass phrase - $publicKey = 'file://' . __DIR__ . '/../public.key'; // Setup the authorization server $server = new AuthorizationServer( @@ -40,7 +39,7 @@ $app = new App([ $accessTokenRepository, $scopeRepository, $privateKey, - $publicKey + 'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen' ); // Enable the client credentials grant on the server diff --git a/examples/public/implicit.php b/examples/public/implicit.php index 2a82097f..73de09ec 100644 --- a/examples/public/implicit.php +++ b/examples/public/implicit.php @@ -32,7 +32,6 @@ $app = new App([ $accessTokenRepository = new AccessTokenRepository(); $privateKeyPath = 'file://' . __DIR__ . '/../private.key'; - $publicKeyPath = 'file://' . __DIR__ . '/../public.key'; // Setup the authorization server $server = new AuthorizationServer( @@ -40,8 +39,9 @@ $app = new App([ $accessTokenRepository, $scopeRepository, $privateKeyPath, - $publicKeyPath + 'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen' ); + $server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'); // Enable the implicit grant on the server with a token TTL of 1 hour $server->enableGrantType(new ImplicitGrant(new \DateInterval('PT1H'))); diff --git a/examples/public/middleware_use.php b/examples/public/middleware_use.php index 2dfe1834..121b9155 100644 --- a/examples/public/middleware_use.php +++ b/examples/public/middleware_use.php @@ -12,6 +12,7 @@ use League\OAuth2\Server\Grant\AuthCodeGrant; use League\OAuth2\Server\Grant\RefreshTokenGrant; use League\OAuth2\Server\Middleware\AuthorizationServerMiddleware; use League\OAuth2\Server\Middleware\ResourceServerMiddleware; +use League\OAuth2\Server\ResourceServer; use OAuth2ServerExamples\Repositories\AccessTokenRepository; use OAuth2ServerExamples\Repositories\AuthCodeRepository; use OAuth2ServerExamples\Repositories\ClientRepository; @@ -37,7 +38,6 @@ $app = new App([ $refreshTokenRepository = new RefreshTokenRepository(); $privateKeyPath = 'file://' . __DIR__ . '/../private.key'; - $publicKeyPath = 'file://' . __DIR__ . '/../public.key'; // Setup the authorization server $server = new AuthorizationServer( @@ -45,7 +45,7 @@ $app = new App([ $accessTokenRepository, $scopeRepository, $privateKeyPath, - $publicKeyPath + 'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen' ); // Enable the authentication code grant on the server with a token TTL of 1 hour @@ -61,7 +61,17 @@ $app = new App([ // Enable the refresh token grant on the server with a token TTL of 1 month $server->enableGrantType( new RefreshTokenGrant($refreshTokenRepository), - new \DateInterval('PT1M') + new \DateInterval('P1M') + ); + + return $server; + }, + ResourceServer::class => function () { + $publicKeyPath = 'file://' . __DIR__ . '/../public.key'; + + $server = new ResourceServer( + new AccessTokenRepository(), + $publicKeyPath ); return $server; @@ -94,6 +104,6 @@ $app->group('/api', function () { return $response->withBody($body); }); -})->add(new ResourceServerMiddleware($app->getContainer()->get(AuthorizationServer::class))); +})->add(new ResourceServerMiddleware($app->getContainer()->get(ResourceServer::class))); $app->run(); diff --git a/examples/public/password.php b/examples/public/password.php index 02a85a56..6857e988 100644 --- a/examples/public/password.php +++ b/examples/public/password.php @@ -24,7 +24,7 @@ $app = new App([ new AccessTokenRepository(), // instance of AccessTokenRepositoryInterface new ScopeRepository(), // instance of ScopeRepositoryInterface 'file://' . __DIR__ . '/../private.key', // path to private key - 'file://' . __DIR__ . '/../public.key' // path to public key + 'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen' // encryption key ); $grant = new PasswordGrant( diff --git a/examples/public/refresh_token.php b/examples/public/refresh_token.php index 0649c117..39be0826 100644 --- a/examples/public/refresh_token.php +++ b/examples/public/refresh_token.php @@ -17,7 +17,6 @@ use OAuth2ServerExamples\Repositories\ScopeRepository; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Slim\App; -use Zend\Diactoros\Stream; include __DIR__ . '/../vendor/autoload.php'; @@ -33,7 +32,6 @@ $app = new App([ $refreshTokenRepository = new RefreshTokenRepository(); $privateKeyPath = 'file://' . __DIR__ . '/../private.key'; - $publicKeyPath = 'file://' . __DIR__ . '/../public.key'; // Setup the authorization server $server = new AuthorizationServer( @@ -41,7 +39,7 @@ $app = new App([ $accessTokenRepository, $scopeRepository, $privateKeyPath, - $publicKeyPath + 'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen' ); // Enable the refresh token grant on the server @@ -66,10 +64,9 @@ $app->post('/access_token', function (ServerRequestInterface $request, ResponseI } catch (OAuthServerException $exception) { return $exception->generateHttpResponse($response); } catch (\Exception $exception) { - $body = new Stream('php://temp', 'r+'); - $body->write($exception->getMessage()); + $response->getBody()->write($exception->getMessage()); - return $response->withStatus(500)->withBody($body); + return $response->withStatus(500); } }); diff --git a/src/AuthorizationServer.php b/src/AuthorizationServer.php index 0517124a..35e745d3 100644 --- a/src/AuthorizationServer.php +++ b/src/AuthorizationServer.php @@ -66,6 +66,11 @@ class AuthorizationServer implements EmitterAwareInterface */ private $scopeRepository; + /** + * @var string + */ + private $encryptionKey; + /** * New server instance. * @@ -73,7 +78,7 @@ class AuthorizationServer implements EmitterAwareInterface * @param AccessTokenRepositoryInterface $accessTokenRepository * @param ScopeRepositoryInterface $scopeRepository * @param CryptKey|string $privateKey - * @param CryptKey|string $publicKey + * @param string $encryptionKey * @param null|ResponseTypeInterface $responseType */ public function __construct( @@ -81,7 +86,7 @@ class AuthorizationServer implements EmitterAwareInterface AccessTokenRepositoryInterface $accessTokenRepository, ScopeRepositoryInterface $scopeRepository, $privateKey, - $publicKey, + $encryptionKey, ResponseTypeInterface $responseType = null ) { $this->clientRepository = $clientRepository; @@ -93,11 +98,7 @@ class AuthorizationServer implements EmitterAwareInterface } $this->privateKey = $privateKey; - if ($publicKey instanceof CryptKey === false) { - $publicKey = new CryptKey($publicKey); - } - $this->publicKey = $publicKey; - + $this->encryptionKey = $encryptionKey; $this->responseType = $responseType; } @@ -117,8 +118,8 @@ class AuthorizationServer implements EmitterAwareInterface $grantType->setClientRepository($this->clientRepository); $grantType->setScopeRepository($this->scopeRepository); $grantType->setPrivateKey($this->privateKey); - $grantType->setPublicKey($this->publicKey); $grantType->setEmitter($this->getEmitter()); + $grantType->setEncryptionKey($this->encryptionKey); $this->enabledGrantTypes[$grantType->getIdentifier()] = $grantType; $this->grantTypeAccessTokenTTL[$grantType->getIdentifier()] = $accessTokenTTL; @@ -200,6 +201,7 @@ class AuthorizationServer implements EmitterAwareInterface } $this->responseType->setPrivateKey($this->privateKey); + $this->responseType->setEncryptionKey($this->encryptionKey); return $this->responseType; } diff --git a/src/AuthorizationValidators/BearerTokenValidator.php b/src/AuthorizationValidators/BearerTokenValidator.php index 2bab4cb5..6f299ce4 100644 --- a/src/AuthorizationValidators/BearerTokenValidator.php +++ b/src/AuthorizationValidators/BearerTokenValidator.php @@ -12,6 +12,7 @@ namespace League\OAuth2\Server\AuthorizationValidators; use Lcobucci\JWT\Parser; use Lcobucci\JWT\Signer\Rsa\Sha256; use Lcobucci\JWT\ValidationData; +use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\CryptTrait; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; @@ -26,6 +27,11 @@ class BearerTokenValidator implements AuthorizationValidatorInterface */ private $accessTokenRepository; + /** + * @var \League\OAuth2\Server\CryptKey + */ + protected $publicKey; + /** * @param AccessTokenRepositoryInterface $accessTokenRepository */ @@ -34,6 +40,16 @@ class BearerTokenValidator implements AuthorizationValidatorInterface $this->accessTokenRepository = $accessTokenRepository; } + /** + * Set the public key + * + * @param \League\OAuth2\Server\CryptKey $key + */ + public function setPublicKey(CryptKey $key) + { + $this->publicKey = $key; + } + /** * {@inheritdoc} */ diff --git a/src/CryptKey.php b/src/CryptKey.php index aedeafb0..2ede9e33 100644 --- a/src/CryptKey.php +++ b/src/CryptKey.php @@ -29,8 +29,9 @@ class CryptKey /** * @param string $keyPath * @param null|string $passPhrase + * @param bool $keyPermissionsCheck */ - public function __construct($keyPath, $passPhrase = null) + public function __construct($keyPath, $passPhrase = null, $keyPermissionsCheck = true) { if (preg_match(self::RSA_KEY_PATTERN, $keyPath)) { $keyPath = $this->saveKeyToFile($keyPath); @@ -44,6 +45,18 @@ class CryptKey throw new \LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath)); } + if ($keyPermissionsCheck === true) { + // Verify the permissions of the key + $keyPathPerms = decoct(fileperms($keyPath) & 0777); + if (in_array($keyPathPerms, ['600', '660'], true) === false) { + trigger_error(sprintf( + 'Key file "%s" permissions are not correct, should be 600 or 660 instead of %s', + $keyPath, + $keyPathPerms + ), E_USER_NOTICE); + } + } + $this->keyPath = $keyPath; $this->passPhrase = $passPhrase; } @@ -57,15 +70,26 @@ class CryptKey */ private function saveKeyToFile($key) { - $keyPath = sys_get_temp_dir() . '/' . sha1($key) . '.key'; + $tmpDir = sys_get_temp_dir(); + $keyPath = $tmpDir . '/' . sha1($key) . '.key'; if (!file_exists($keyPath) && !touch($keyPath)) { // @codeCoverageIgnoreStart - throw new \RuntimeException('"%s" key file could not be created', $keyPath); + throw new \RuntimeException(sprintf('"%s" key file could not be created', $keyPath)); // @codeCoverageIgnoreEnd } - file_put_contents($keyPath, $key); + if (file_put_contents($keyPath, $key) === false) { + // @codeCoverageIgnoreStart + throw new \RuntimeException(sprintf('Unable to write key file to temporary directory "%s"', $tmpDir)); + // @codeCoverageIgnoreEnd + } + + if (chmod($keyPath, 0600) === false) { + // @codeCoverageIgnoreStart + throw new \RuntimeException(sprintf('The key file "%s" file mode could not be changed with chmod to 600', $keyPath)); + // @codeCoverageIgnoreEnd + } return 'file://' . $keyPath; } diff --git a/src/CryptTrait.php b/src/CryptTrait.php index d417e115..125a757e 100644 --- a/src/CryptTrait.php +++ b/src/CryptTrait.php @@ -11,37 +11,14 @@ namespace League\OAuth2\Server; +use Defuse\Crypto\Crypto; + trait CryptTrait { /** - * @var CryptKey + * @var string */ - protected $privateKey; - - /** - * @var CryptKey - */ - protected $publicKey; - - /** - * Set path to private key. - * - * @param CryptKey $privateKey - */ - public function setPrivateKey(CryptKey $privateKey) - { - $this->privateKey = $privateKey; - } - - /** - * Set path to public key. - * - * @param CryptKey $publicKey - */ - public function setPublicKey(CryptKey $publicKey) - { - $this->publicKey = $publicKey; - } + protected $encryptionKey; /** * Encrypt data with a private key. @@ -54,30 +31,11 @@ trait CryptTrait */ protected function encrypt($unencryptedData) { - $privateKey = openssl_pkey_get_private($this->privateKey->getKeyPath(), $this->privateKey->getPassPhrase()); - $privateKeyDetails = @openssl_pkey_get_details($privateKey); - if ($privateKeyDetails === null) { - throw new \LogicException( - sprintf('Could not get details of private key: %s', $this->privateKey->getKeyPath()) - ); + try { + return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey); + } catch (\Exception $e) { + throw new \LogicException($e->getMessage()); } - - $chunkSize = ceil($privateKeyDetails['bits'] / 8) - 11; - $output = ''; - - while ($unencryptedData) { - $chunk = substr($unencryptedData, 0, $chunkSize); - $unencryptedData = substr($unencryptedData, $chunkSize); - if (openssl_private_encrypt($chunk, $encrypted, $privateKey) === false) { - // @codeCoverageIgnoreStart - throw new \LogicException('Failed to encrypt data'); - // @codeCoverageIgnoreEnd - } - $output .= $encrypted; - } - openssl_pkey_free($privateKey); - - return base64_encode($output); } /** @@ -91,31 +49,20 @@ trait CryptTrait */ protected function decrypt($encryptedData) { - $publicKey = openssl_pkey_get_public($this->publicKey->getKeyPath()); - $publicKeyDetails = @openssl_pkey_get_details($publicKey); - if ($publicKeyDetails === null) { - throw new \LogicException( - sprintf('Could not get details of public key: %s', $this->publicKey->getKeyPath()) - ); + try { + return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey); + } catch (\Exception $e) { + throw new \LogicException($e->getMessage()); } + } - $chunkSize = ceil($publicKeyDetails['bits'] / 8); - $output = ''; - - $encryptedData = base64_decode($encryptedData); - - while ($encryptedData) { - $chunk = substr($encryptedData, 0, $chunkSize); - $encryptedData = substr($encryptedData, $chunkSize); - if (openssl_public_decrypt($chunk, $decrypted, $publicKey/*, OPENSSL_PKCS1_OAEP_PADDING*/) === false) { - // @codeCoverageIgnoreStart - throw new \LogicException('Failed to decrypt data'); - // @codeCoverageIgnoreEnd - } - $output .= $decrypted; - } - openssl_pkey_free($publicKey); - - return $output; + /** + * Set the encryption key + * + * @param string $key + */ + public function setEncryptionKey($key = null) + { + $this->encryptionKey = $key; } } diff --git a/src/Exception/OAuthServerException.php b/src/Exception/OAuthServerException.php index 6ffa0fb1..45e03c07 100644 --- a/src/Exception/OAuthServerException.php +++ b/src/Exception/OAuthServerException.php @@ -105,7 +105,10 @@ class OAuthServerException extends \Exception public static function invalidScope($scope, $redirectUri = null) { $errorMessage = 'The requested scope is invalid, unknown, or malformed'; - $hint = sprintf('Check the `%s` scope', $scope); + $hint = sprintf( + 'Check the `%s` scope', + htmlspecialchars($scope, ENT_QUOTES, 'UTF-8', false) + ); return new static($errorMessage, 5, 'invalid_scope', 400, $hint, $redirectUri); } @@ -149,7 +152,7 @@ class OAuthServerException extends \Exception */ public static function invalidRefreshToken($hint = null) { - return new static('The refresh token is invalid.', 8, 'invalid_request', 400, $hint); + return new static('The refresh token is invalid.', 8, 'invalid_request', 401, $hint); } /** diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index d916e3f1..3ac98cf4 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -11,6 +11,7 @@ namespace League\OAuth2\Server\Grant; use League\Event\EmitterAwareTrait; +use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\CryptTrait; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\AuthCodeEntityInterface; @@ -75,6 +76,11 @@ abstract class AbstractGrant implements GrantTypeInterface */ protected $refreshTokenTTL; + /** + * @var \League\OAuth2\Server\CryptKey + */ + protected $privateKey; + /** * @param ClientRepositoryInterface $clientRepository */ @@ -131,6 +137,16 @@ abstract class AbstractGrant implements GrantTypeInterface $this->refreshTokenTTL = $refreshTokenTTL; } + /** + * Set the private key + * + * @param \League\OAuth2\Server\CryptKey $key + */ + public function setPrivateKey(CryptKey $key) + { + $this->privateKey = $key; + } + /** * Validate the client. * diff --git a/src/Grant/AuthCodeGrant.php b/src/Grant/AuthCodeGrant.php index df89400e..a138366f 100644 --- a/src/Grant/AuthCodeGrant.php +++ b/src/Grant/AuthCodeGrant.php @@ -264,6 +264,13 @@ class AuthCodeGrant extends AbstractAuthorizeGrant throw OAuthServerException::invalidRequest('code_challenge'); } + if (preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeChallenge) !== 1) { + throw OAuthServerException::invalidRequest( + 'code_challenge', + 'The code_challenge must be between 43 and 128 characters' + ); + } + $codeChallengeMethod = $this->getQueryStringParameter('code_challenge_method', $request, 'plain'); if (in_array($codeChallengeMethod, ['plain', 'S256']) === false) { throw OAuthServerException::invalidRequest( @@ -304,6 +311,17 @@ class AuthCodeGrant extends AbstractAuthorizeGrant $authorizationRequest->getScopes() ); + $payload = [ + 'client_id' => $authCode->getClient()->getIdentifier(), + 'redirect_uri' => $authCode->getRedirectUri(), + 'auth_code_id' => $authCode->getIdentifier(), + 'scopes' => $authCode->getScopes(), + 'user_id' => $authCode->getUserIdentifier(), + 'expire_time' => (new \DateTime())->add($this->authCodeTTL)->format('U'), + 'code_challenge' => $authorizationRequest->getCodeChallenge(), + 'code_challenge_method' => $authorizationRequest->getCodeChallengeMethod(), + ]; + $response = new RedirectResponse(); $response->setRedirectUri( $this->makeRedirectUri( @@ -311,16 +329,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant [ 'code' => $this->encrypt( json_encode( - [ - 'client_id' => $authCode->getClient()->getIdentifier(), - 'redirect_uri' => $authCode->getRedirectUri(), - 'auth_code_id' => $authCode->getIdentifier(), - 'scopes' => $authCode->getScopes(), - 'user_id' => $authCode->getUserIdentifier(), - 'expire_time' => (new \DateTime())->add($this->authCodeTTL)->format('U'), - 'code_challenge' => $authorizationRequest->getCodeChallenge(), - 'code_challenge_method ' => $authorizationRequest->getCodeChallengeMethod(), - ] + $payload ) ), 'state' => $authorizationRequest->getState(), diff --git a/src/Grant/GrantTypeInterface.php b/src/Grant/GrantTypeInterface.php index c01a571d..7aa98242 100644 --- a/src/Grant/GrantTypeInterface.php +++ b/src/Grant/GrantTypeInterface.php @@ -127,9 +127,9 @@ interface GrantTypeInterface extends EmitterAwareInterface public function setPrivateKey(CryptKey $privateKey); /** - * Set the path to the public key. + * Set the encryption key * - * @param CryptKey $publicKey + * @param string|null $key */ - public function setPublicKey(CryptKey $publicKey); + public function setEncryptionKey($key = null); } diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index 62a48147..4a16314f 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -27,11 +27,18 @@ class ImplicitGrant extends AbstractAuthorizeGrant private $accessTokenTTL; /** - * @param \DateInterval $accessTokenTTL + * @var string */ - public function __construct(\DateInterval $accessTokenTTL) + private $queryDelimiter; + + /** + * @param \DateInterval $accessTokenTTL + * @param string $queryDelimiter + */ + public function __construct(\DateInterval $accessTokenTTL, $queryDelimiter = '#') { $this->accessTokenTTL = $accessTokenTTL; + $this->queryDelimiter = $queryDelimiter; } /** @@ -95,7 +102,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant public function canRespondToAuthorizationRequest(ServerRequestInterface $request) { return ( - array_key_exists('response_type', $request->getQueryParams()) + isset($request->getQueryParams()['response_type']) && $request->getQueryParams()['response_type'] === 'token' && isset($request->getQueryParams()['client_id']) ); @@ -200,11 +207,11 @@ class ImplicitGrant extends AbstractAuthorizeGrant $finalRedirectUri, [ 'access_token' => (string) $accessToken->convertToJWT($this->privateKey), - 'token_type' => 'bearer', + 'token_type' => 'Bearer', 'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - (new \DateTime())->getTimestamp(), 'state' => $authorizationRequest->getState(), ], - '#' + $this->queryDelimiter ) ); diff --git a/src/Grant/RefreshTokenGrant.php b/src/Grant/RefreshTokenGrant.php index 17448a92..53dfdf7d 100644 --- a/src/Grant/RefreshTokenGrant.php +++ b/src/Grant/RefreshTokenGrant.php @@ -102,7 +102,7 @@ class RefreshTokenGrant extends AbstractGrant // Validate refresh token try { $refreshToken = $this->decrypt($encryptedRefreshToken); - } catch (\LogicException $e) { + } catch (\Exception $e) { throw OAuthServerException::invalidRefreshToken('Cannot decrypt the refresh token'); } diff --git a/src/Repositories/AccessTokenRepositoryInterface.php b/src/Repositories/AccessTokenRepositoryInterface.php index 04e98bc6..72ddf1f4 100644 --- a/src/Repositories/AccessTokenRepositoryInterface.php +++ b/src/Repositories/AccessTokenRepositoryInterface.php @@ -12,6 +12,7 @@ namespace League\OAuth2\Server\Repositories; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Entities\ScopeEntityInterface; +use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException; /** * Access token interface. @@ -33,6 +34,8 @@ interface AccessTokenRepositoryInterface extends RepositoryInterface * Persists a new access token to permanent storage. * * @param AccessTokenEntityInterface $accessTokenEntity + * + * @throws UniqueTokenIdentifierConstraintViolationException */ public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity); diff --git a/src/Repositories/AuthCodeRepositoryInterface.php b/src/Repositories/AuthCodeRepositoryInterface.php index 4d23439a..2dc285b8 100644 --- a/src/Repositories/AuthCodeRepositoryInterface.php +++ b/src/Repositories/AuthCodeRepositoryInterface.php @@ -10,6 +10,7 @@ namespace League\OAuth2\Server\Repositories; use League\OAuth2\Server\Entities\AuthCodeEntityInterface; +use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException; /** * Auth code storage interface. @@ -27,6 +28,8 @@ interface AuthCodeRepositoryInterface extends RepositoryInterface * Persists a new auth code to permanent storage. * * @param AuthCodeEntityInterface $authCodeEntity + * + * @throws UniqueTokenIdentifierConstraintViolationException */ public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity); diff --git a/src/Repositories/RefreshTokenRepositoryInterface.php b/src/Repositories/RefreshTokenRepositoryInterface.php index d7e686cc..0c0697bf 100644 --- a/src/Repositories/RefreshTokenRepositoryInterface.php +++ b/src/Repositories/RefreshTokenRepositoryInterface.php @@ -10,6 +10,7 @@ namespace League\OAuth2\Server\Repositories; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; +use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException; /** * Refresh token interface. @@ -27,6 +28,8 @@ interface RefreshTokenRepositoryInterface extends RepositoryInterface * Create a new refresh token_name. * * @param RefreshTokenEntityInterface $refreshTokenEntity + * + * @throws UniqueTokenIdentifierConstraintViolationException */ public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity); diff --git a/src/ResponseTypes/AbstractResponseType.php b/src/ResponseTypes/AbstractResponseType.php index 6e164392..d013bab0 100644 --- a/src/ResponseTypes/AbstractResponseType.php +++ b/src/ResponseTypes/AbstractResponseType.php @@ -11,6 +11,7 @@ namespace League\OAuth2\Server\ResponseTypes; +use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\CryptTrait; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; @@ -29,6 +30,11 @@ abstract class AbstractResponseType implements ResponseTypeInterface */ protected $refreshToken; + /** + * @var CryptKey + */ + protected $privateKey; + /** * {@inheritdoc} */ @@ -44,4 +50,14 @@ abstract class AbstractResponseType implements ResponseTypeInterface { $this->refreshToken = $refreshToken; } + + /** + * Set the private key + * + * @param \League\OAuth2\Server\CryptKey $key + */ + public function setPrivateKey(CryptKey $key) + { + $this->privateKey = $key; + } } diff --git a/src/ResponseTypes/ResponseTypeInterface.php b/src/ResponseTypes/ResponseTypeInterface.php index 9f358a53..8ac20b8c 100644 --- a/src/ResponseTypes/ResponseTypeInterface.php +++ b/src/ResponseTypes/ResponseTypeInterface.php @@ -33,4 +33,11 @@ interface ResponseTypeInterface * @return ResponseInterface */ public function generateHttpResponse(ResponseInterface $response); + + /** + * Set the encryption key + * + * @param string|null $key + */ + public function setEncryptionKey($key = null); } diff --git a/tests/AuthorizationServerTest.php b/tests/AuthorizationServerTest.php index 909da159..4571c5e1 100644 --- a/tests/AuthorizationServerTest.php +++ b/tests/AuthorizationServerTest.php @@ -3,7 +3,6 @@ namespace LeagueTests; use League\OAuth2\Server\AuthorizationServer; -use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Grant\AuthCodeGrant; use League\OAuth2\Server\Grant\ClientCredentialsGrant; @@ -26,6 +25,13 @@ use Zend\Diactoros\ServerRequestFactory; class AuthorizationServerTest extends \PHPUnit_Framework_TestCase { + public function setUp() + { + // Make sure the keys have the correct permissions. + chmod(__DIR__ . '/Stubs/private.key', 0600); + chmod(__DIR__ . '/Stubs/public.key', 0600); + } + public function testRespondToRequestInvalidGrantType() { $server = new AuthorizationServer( @@ -33,7 +39,7 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(), $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(), 'file://' . __DIR__ . '/Stubs/private.key', - 'file://' . __DIR__ . '/Stubs/public.key', + base64_encode(random_bytes(36)), new StubResponseType() ); @@ -63,7 +69,7 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase $accessTokenRepositoryMock, $scopeRepositoryMock, 'file://' . __DIR__ . '/Stubs/private.key', - 'file://' . __DIR__ . '/Stubs/public.key', + base64_encode(random_bytes(36)), new StubResponseType() ); @@ -116,9 +122,6 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase new \DateInterval('PT10M') ); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/Stubs/public.key')); - $server->enableGrantType($grant); $authRequest = new AuthorizationRequest(); diff --git a/tests/CryptTraitTest.php b/tests/CryptTraitTest.php index d0ada49e..3d60ec9d 100644 --- a/tests/CryptTraitTest.php +++ b/tests/CryptTraitTest.php @@ -2,13 +2,12 @@ namespace LeagueTests\Utils; -use League\OAuth2\Server\CryptKey; use LeagueTests\Stubs\CryptTraitStub; class CryptTraitTest extends \PHPUnit_Framework_TestCase { /** - * CryptTrait stub + * @var \LeagueTests\Stubs\CryptTraitStub */ protected $cryptStub; @@ -26,30 +25,4 @@ class CryptTraitTest extends \PHPUnit_Framework_TestCase $this->assertNotEquals($payload, $encrypted); $this->assertEquals($payload, $plainText); } - - /** - * @expectedException \LogicException - */ - public function testBadPrivateKey() - { - $this->cryptStub->setPrivateKey(new CryptKey(__DIR__ . '/Stubs/public.key')); - $this->cryptStub->doEncrypt(''); - } - - /** - * @expectedException \LogicException - */ - public function testBadPublicKey() - { - $this->cryptStub->setPublicKey(new CryptKey(__DIR__ . '/Stubs/private.key')); - $this->cryptStub->doDecrypt(''); - } - - /** - * @expectedException \LogicException - */ - public function testNonExistentKey() - { - new CryptKey('foo/bar'); - } } diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index 3ef3f133..4cd490e3 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -3,7 +3,6 @@ namespace LeagueTests\Grant; use League\Event\Emitter; -use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\AuthCodeEntityInterface; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; @@ -27,8 +26,6 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase { /** @var AbstractGrant $grantMock */ $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); - $grantMock->setPrivateKey(new CryptKey(__DIR__ . '/../Stubs/private.key')); - $grantMock->setPublicKey(new CryptKey(__DIR__ . '/../Stubs/public.key')); $grantMock->setEmitter(new Emitter()); } diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 498fdb4e..d63aec29 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -2,7 +2,6 @@ namespace LeagueTests\Grant; -use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; use League\OAuth2\Server\Exception\OAuthServerException; @@ -164,13 +163,124 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase 'response_type' => 'code', 'client_id' => 'foo', 'redirect_uri' => 'http://foo/bar', - 'code_challenge' => 'FOOBAR', + 'code_challenge' => str_repeat('A', 43), ] ); $this->assertTrue($grant->validateAuthorizationRequest($request) instanceof AuthorizationRequest); } + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + */ + public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooShort() + { + $client = new ClientEntity(); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $grant = new AuthCodeGrant( + $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), + $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), + new \DateInterval('PT10M') + ); + $grant->enableCodeExchangeProof(); + $grant->setClientRepository($clientRepositoryMock); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + [], + [], + [ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code_challenge' => str_repeat('A', 42), + ] + ); + + $grant->validateAuthorizationRequest($request); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + */ + public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooLong() + { + $client = new ClientEntity(); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $grant = new AuthCodeGrant( + $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), + $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), + new \DateInterval('PT10M') + ); + $grant->enableCodeExchangeProof(); + $grant->setClientRepository($clientRepositoryMock); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + [], + [], + [ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code_challenge' => str_repeat('A', 129), + ] + ); + + $grant->validateAuthorizationRequest($request); + } + + /** + * @expectedException \League\OAuth2\Server\Exception\OAuthServerException + */ + public function testValidateAuthorizationRequestCodeChallengeInvalidCharacters() + { + $client = new ClientEntity(); + $client->setRedirectUri('http://foo/bar'); + $clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock(); + $clientRepositoryMock->method('getClientEntity')->willReturn($client); + + $grant = new AuthCodeGrant( + $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(), + $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), + new \DateInterval('PT10M') + ); + $grant->enableCodeExchangeProof(); + $grant->setClientRepository($clientRepositoryMock); + + $request = new ServerRequest( + [], + [], + null, + null, + 'php://input', + [], + [], + [ + 'response_type' => 'code', + 'client_id' => 'foo', + 'redirect_uri' => 'http://foo/bar', + 'code_challenge' => str_repeat('A', 42) . '!', + ] + ); + + $grant->validateAuthorizationRequest($request); + } + /** * @expectedException \League\OAuth2\Server\Exception\OAuthServerException * @expectedExceptionCode 3 @@ -399,9 +509,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); - - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse); } @@ -426,9 +534,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); - - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->completeAuthorizationRequest($authRequest); } @@ -463,8 +569,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setScopeRepository($scopeRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -532,8 +637,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setScopeRepository($scopeRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -604,8 +708,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setScopeRepository($scopeRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -662,7 +765,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase new \DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -709,7 +812,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase new \DateInterval('PT10M') ); $grant->setClientRepository($clientRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -762,8 +865,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -808,8 +910,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -872,8 +973,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -933,8 +1033,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -994,8 +1093,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -1053,8 +1151,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -1126,8 +1223,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -1199,8 +1295,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -1259,9 +1354,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); - - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse); } @@ -1287,9 +1380,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(), new \DateInterval('PT10M') ); - - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse); } @@ -1316,9 +1407,6 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase new \DateInterval('PT10M') ); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse); } @@ -1353,8 +1441,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setScopeRepository($scopeRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -1425,8 +1512,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setScopeRepository($scopeRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], @@ -1497,8 +1583,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase $grant->setScopeRepository($scopeRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); - $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $request = new ServerRequest( [], diff --git a/tests/Grant/ImplicitGrantTest.php b/tests/Grant/ImplicitGrantTest.php index 0fc06370..3bfe4b84 100644 --- a/tests/Grant/ImplicitGrantTest.php +++ b/tests/Grant/ImplicitGrantTest.php @@ -283,7 +283,6 @@ class ImplicitGrantTest extends \PHPUnit_Framework_TestCase $grant = new ImplicitGrant(new \DateInterval('PT10M')); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse); @@ -307,7 +306,6 @@ class ImplicitGrantTest extends \PHPUnit_Framework_TestCase $grant = new ImplicitGrant(new \DateInterval('PT10M')); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->completeAuthorizationRequest($authRequest); @@ -329,7 +327,6 @@ class ImplicitGrantTest extends \PHPUnit_Framework_TestCase $grant = new ImplicitGrant(new \DateInterval('PT10M')); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse); @@ -354,7 +351,6 @@ class ImplicitGrantTest extends \PHPUnit_Framework_TestCase $grant = new ImplicitGrant(new \DateInterval('PT10M')); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->completeAuthorizationRequest($authRequest); @@ -379,7 +375,6 @@ class ImplicitGrantTest extends \PHPUnit_Framework_TestCase $grant = new ImplicitGrant(new \DateInterval('PT10M')); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->completeAuthorizationRequest($authRequest); diff --git a/tests/Grant/RefreshTokenGrantTest.php b/tests/Grant/RefreshTokenGrantTest.php index 90a63276..47d7ad17 100644 --- a/tests/Grant/RefreshTokenGrantTest.php +++ b/tests/Grant/RefreshTokenGrantTest.php @@ -21,7 +21,7 @@ use Zend\Diactoros\ServerRequest; class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase { /** - * CryptTrait stub + * @var CryptTraitStub */ protected $cryptStub; @@ -65,7 +65,7 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase $grant->setClientRepository($clientRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $oldRefreshToken = $this->cryptStub->doEncrypt( @@ -121,7 +121,7 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $oldRefreshToken = $this->cryptStub->doEncrypt( @@ -180,7 +180,7 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $oldRefreshToken = $this->cryptStub->doEncrypt( @@ -227,7 +227,7 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $serverRequest = new ServerRequest(); @@ -259,7 +259,7 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $oldRefreshToken = 'foobar'; @@ -291,14 +291,13 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf(); - $refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(); $refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf(); $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $oldRefreshToken = $this->cryptStub->doEncrypt( @@ -344,7 +343,7 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $oldRefreshToken = $this->cryptStub->doEncrypt( @@ -391,7 +390,7 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase $grant = new RefreshTokenGrant($refreshTokenRepositoryMock); $grant->setClientRepository($clientRepositoryMock); $grant->setAccessTokenRepository($accessTokenRepositoryMock); - $grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $grant->setEncryptionKey($this->cryptStub->getKey()); $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $oldRefreshToken = $this->cryptStub->doEncrypt( diff --git a/tests/Middleware/AuthorizationServerMiddlewareTest.php b/tests/Middleware/AuthorizationServerMiddlewareTest.php index affc2a3b..74dffbf7 100644 --- a/tests/Middleware/AuthorizationServerMiddlewareTest.php +++ b/tests/Middleware/AuthorizationServerMiddlewareTest.php @@ -33,7 +33,7 @@ class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase $accessRepositoryMock, $scopeRepositoryMock, 'file://' . __DIR__ . '/../Stubs/private.key', - 'file://' . __DIR__ . '/../Stubs/public.key', + base64_encode(random_bytes(36)), new StubResponseType() ); @@ -66,7 +66,7 @@ class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(), $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(), 'file://' . __DIR__ . '/../Stubs/private.key', - 'file://' . __DIR__ . '/../Stubs/public.key', + base64_encode(random_bytes(36)), new StubResponseType() ); @@ -97,7 +97,8 @@ class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase $response = $exception->generateHttpResponse(new Response()); $this->assertEquals(302, $response->getStatusCode()); - $this->assertEquals('http://foo/bar?error=invalid_scope&message=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed&hint=Check+the+%60test%60+scope', $response->getHeader('location')[0]); + $this->assertEquals('http://foo/bar?error=invalid_scope&message=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed&hint=Check+the+%60test%60+scope', + $response->getHeader('location')[0]); } public function testOAuthErrorResponseRedirectUriFragment() @@ -106,6 +107,7 @@ class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase $response = $exception->generateHttpResponse(new Response(), true); $this->assertEquals(302, $response->getStatusCode()); - $this->assertEquals('http://foo/bar#error=invalid_scope&message=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed&hint=Check+the+%60test%60+scope', $response->getHeader('location')[0]); + $this->assertEquals('http://foo/bar#error=invalid_scope&message=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed&hint=Check+the+%60test%60+scope', + $response->getHeader('location')[0]); } } diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index ca660ab7..7f710d92 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -23,7 +23,7 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase $responseType = new BearerTokenResponse($accessTokenRepositoryMock); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -67,7 +67,7 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase $responseType = new BearerTokenResponseWithParams($accessTokenRepositoryMock); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -115,7 +115,7 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase $responseType = new BearerTokenResponse($accessTokenRepositoryMock); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -141,7 +141,6 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase $accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(false); $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); - $authorizationValidator->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $request = new ServerRequest(); @@ -162,7 +161,7 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase $responseType = new BearerTokenResponse($accessTokenRepositoryMock); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -185,7 +184,6 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase $json = json_decode((string) $response->getBody()); $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); - $authorizationValidator->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $request = new ServerRequest(); @@ -205,7 +203,7 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase { $responseType = new BearerTokenResponse(); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $client = new ClientEntity(); $client->setIdentifier('clientName'); @@ -231,7 +229,6 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase $accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(true); $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); - $authorizationValidator->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $request = new ServerRequest(); @@ -253,12 +250,11 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase $responseType = new BearerTokenResponse($accessTokenRepositoryMock); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); - $authorizationValidator->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $request = new ServerRequest(); @@ -280,12 +276,11 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase $responseType = new BearerTokenResponse($accessTokenRepositoryMock); $responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); + $responseType->setEncryptionKey(base64_encode(random_bytes(36))); $accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(); $authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock); - $authorizationValidator->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key')); $request = new ServerRequest(); diff --git a/tests/Stubs/CryptTraitStub.php b/tests/Stubs/CryptTraitStub.php index 2414c199..3fe02199 100644 --- a/tests/Stubs/CryptTraitStub.php +++ b/tests/Stubs/CryptTraitStub.php @@ -2,7 +2,6 @@ namespace LeagueTests\Stubs; -use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\CryptTrait; class CryptTraitStub @@ -11,8 +10,12 @@ class CryptTraitStub public function __construct() { - $this->setPrivateKey(new CryptKey('file://' . __DIR__ . '/private.key')); - $this->setPublicKey(new CryptKey('file://' . __DIR__ . '/public.key')); + $this->setEncryptionKey(base64_encode(random_bytes(36))); + } + + public function getKey() + { + return $this->encryptionKey; } public function doEncrypt($unencryptedData)