diff --git a/.gitignore b/.gitignore index 49b4f12..e2de534 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ composer.lock .phplint-cache coverage.xml +*.cache diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a223541 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "php.version": "7.4" +} \ No newline at end of file diff --git a/README.md b/README.md index e4152cb..1e5fbf4 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ For example implementation see [Slim API Skeleton](https://github.com/tuupola/sl Install latest version using [composer](https://getcomposer.org/). ``` bash -$ composer require tuupola/slim-jwt-auth +composer require tuupola/slim-jwt-auth ``` If using Apache add the following to the `.htaccess` file. Otherwise PHP wont have access to `Authorization: Bearer` header. @@ -56,8 +56,8 @@ When a request is made, the middleware tries to validate and decode the token. I Validation errors are triggered when the token has been tampered with or the token has expired. For all possible validation errors, see [JWT library](https://github.com/firebase/php-jwt/blob/master/src/JWT.php#L60-L138) source. - ## Optional parameters + ### Path The optional `path` parameter allows you to specify the protected part of your website. It can be either a string or an array. You do not need to specify each URL. Instead think of `path` setting as a folder. In the example below everything starting with `/api` will be authenticated. If you do not define `path` all routes will be protected. @@ -195,7 +195,6 @@ $app->add(new Tuupola\Middleware\JwtAuthentication([ After function is called only when authentication succeeds and after the incoming middleware stack has been called. You can use this to alter the response before passing it next outgoing middleware in the stack. If it returns anything else than `Psr\Http\Message\ResponseInterface` the return value will be ignored. - ``` php $app = new Slim\App; @@ -257,7 +256,7 @@ RequestPathRule contains both a `path` parameter and a `ignore` parameter. Latte 99% of the cases you do not need to use the `rules` parameter. It is only provided for special cases when defaults do not suffice. -## Security +## Security in Tokens JSON Web Tokens are essentially passwords. You should treat them as such and you should always use HTTPS. If the middleware detects insecure usage over HTTP it will throw a `RuntimeException`. By default this rule is relaxed for requests to server running on `localhost`. To allow insecure usage you must enable it manually by setting `secure` to `false`. @@ -317,12 +316,12 @@ $app->delete("/item/{id}", function ($request, $response, $arguments) { You can run tests either manually or automatically on every code change. Automatic tests require [entr](http://entrproject.org/) to work. ``` bash -$ make test +make test ``` ``` bash -$ brew install entr -$ make watch +brew install entr +make watch ``` ## Contributing @@ -331,7 +330,7 @@ Please see [CONTRIBUTING](CONTRIBUTING.md) for details. ## Security -If you discover any security related issues, please email tuupola@appelsiini.net instead of using the issue tracker. +If you discover any security related issues, please email instead of using the issue tracker. ## License diff --git a/UPGRADING.md b/UPGRADING.md index 01adc49..ae3ec7f 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -1,20 +1,26 @@ # Updgrading from 2.x to 3.x ## New namespace + For most cases it is enough just to update the classname. Instead of using the old `Slim\Middleware` namespace: ```php -$app->add(new Slim\Middleware\JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub" -])); +$app->add(new Slim\Middleware\JwtAuthentication( + new JwtAuthOptions( + secret: "supersecretkeyyoushouldnotcommittogithub" + ) + ) +); ``` You should now use `Tuupola\Middleware` instead: ```php -$app->add(new Tuupola\Middleware\JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub" -])); +$app->add(new Tuupola\Middleware\JwtAuthentication( + new JwtAuthOptions( + secret: "supersecretkeyyoushouldnotcommittogithub" + ) +); ``` @@ -34,12 +40,15 @@ $app->add(new Tuupola\Middleware\JwtAuthentication([ You should now do the following instead. Note also that `$response` object is not bassed to `before` anymore. The `before` handler should return ``Psr\Http\Message\ServerRequestInterface`. Anything else will be ignored. ```php -$app->add(new Tuupola\Middleware\JwtAuthentication([ - "ignore" => ["/token"], - "before" => function ($request, $arguments) { +$options = new JwtAuthOptions( + secret: "supersecretkeyyoushouldnotcommittogithub", + ignore: ["/token"], + before: => function (ServerRequestInterface $request, array $arguments) { return $request->withHeader("Foo", "bar"); } -])); +); + +$app->add(new Tuupola\Middleware\JwtAuthentication($options)); ``` ## Changed error handler signature @@ -57,11 +66,13 @@ $app->add(new Tuupola\Middleware\JwtAuthentication([ You should now do the following instead. ```php -$app->add(new Tuupola\Middleware\JwtAuthentication([ - "error" => function ($response, $arguments) { +$options = new JwtAuthOptions( + error: function (ReponseInterface $response, array $arguments): ResponseInterface { return $response->witHeader("Foo", "bar"); } -])); +); + +$app->add(new Tuupola\Middleware\JwtAuthentication($options); ``` Note that `error` should now return an instance of `Psr\Http\Message\ResponseInterface`. Anything else will be ignored. @@ -90,3 +101,7 @@ $app->add(new Tuupola\Middleware\JwtAuthentication([ ## Decoded token is now an array The decoded token attached to the `$request` object is now an array instead of an object. This might require changes to token handling code. + +## Algorithm is a string now + +Prefer using strings instead of array of strings, for compartibility with firebase/php-jwt. diff --git a/composer.json b/composer.json index 490a448..a650140 100644 --- a/composer.json +++ b/composer.json @@ -20,11 +20,11 @@ } ], "require": { - "php": "^7.4|^8.0", - "psr/log": "^1.0|^2.0|^3.0", - "firebase/php-jwt": "^3.0|^4.0|^5.0", - "psr/http-message": "^1.0|^2.0", - "tuupola/http-factory": "^1.3", + "php": "^8.0", + "psr/log": "^3.0", + "firebase/php-jwt": "^6.0", + "psr/http-message": "^1.0", + "tuupola/http-factory": "^1.4", "tuupola/callable-handler": "^1.0", "psr/http-server-middleware": "^1.0" }, diff --git a/src/JwtAuthentication.php b/src/JwtAuthentication.php index 6a453bd..229f606 100644 --- a/src/JwtAuthentication.php +++ b/src/JwtAuthentication.php @@ -34,9 +34,8 @@ namespace Tuupola\Middleware; -use Closure; use DomainException; -use InvalidArgumentException; +use Firebase\JWT\Key; use Exception; use Firebase\JWT\JWT; use Psr\Http\Message\ServerRequestInterface; @@ -48,6 +47,7 @@ use RuntimeException; use SplStack; use Tuupola\Http\Factory\ResponseFactory; +use Tuupola\Middleware\JwtAuthentication\JwtAuthOptions; use Tuupola\Middleware\JwtAuthentication\RequestMethodRule; use Tuupola\Middleware\JwtAuthentication\RequestPathRule; use Tuupola\Middleware\JwtAuthentication\RuleInterface; @@ -58,9 +58,11 @@ final class JwtAuthentication implements MiddlewareInterface /** * PSR-3 compliant logger. - * @var LoggerInterface|null */ - private ?LoggerInterface $logger = null; + private ?LoggerInterface $logger; + + private string $message; + /** * The rules stack. @@ -68,76 +70,30 @@ final class JwtAuthentication implements MiddlewareInterface */ private SplStack $rules; - /** - * Stores all the options passed to the middleware. - * - * @var array{ - * secret?: string|array, - * secure: bool, - * relaxed: array, - * algorithm: array, - * header: string, - * regexp: string, - * cookie: string, - * attribute: string, - * path: array, - * ignore: array, - * before: null|callable, - * after: null|callable, - * error: null|callable, - * } - */ - private array $options = [ - "secure" => true, - "relaxed" => ["localhost", "127.0.0.1"], - "algorithm" => ["HS256", "HS512", "HS384"], - "header" => "Authorization", - "regexp" => "/Bearer\s+(.*)$/i", - "cookie" => "token", - "attribute" => "token", - "path" => ["/"], - "ignore" => [], - "before" => null, - "after" => null, - "error" => null - ]; + private JwtAuthOptions $options; - /** - * @param array{ - * secret?: string|array, - * secure?: bool, - * relaxed?: array, - * algorithm?: array, - * header?: string, - * regexp?: string, - * cookie?: string, - * attribute?: string, - * path?: array, - * ignore?: array, - * before?: null|callable, - * after?: null|callable, - * error?: null|callable, - * } $options - */ - public function __construct(array $options = []) + public function __construct(JwtAuthOptions $options, ?LoggerInterface $logger = null) { /* Setup stack for rules */ - $this->rules = new \SplStack; + $this->rules = new SplStack; - /* Store passed in options overwriting any defaults. */ - $this->hydrate($options); + $this->logger = $logger; + + $this->options = $options->bindToAuthentication($this); /* If nothing was passed in options add default rules. */ - /* This also means $options["rules"] overrides $options["path"] */ - /* and $options["ignore"] */ - if (!isset($options["rules"])) { + /* This also means $options->rules overrides $options->path */ + /* and $options->ignore */ + if (!count($options->rules)) { $this->rules->push(new RequestMethodRule([ "ignore" => ["OPTIONS"] ])); $this->rules->push(new RequestPathRule([ - "path" => $this->options["path"], - "ignore" => $this->options["ignore"] + "path" => $this->options->path, + "ignore" => $this->options->ignore ])); + } else { + $this->rules($options->rules); } } @@ -155,8 +111,8 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface } /* HTTP allowed only if secure is false or server is in relaxed array. */ - if ("https" !== $scheme && true === $this->options["secure"]) { - if (!in_array($host, $this->options["relaxed"])) { + if ("https" !== $scheme && $this->options->secure) { + if (!in_array($host, $this->options->relaxed)) { $message = sprintf( "Insecure use of middleware over %s denied by configuration.", strtoupper($scheme) @@ -171,9 +127,10 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface $decoded = $this->decodeToken($token); } catch (RuntimeException | DomainException $exception) { $response = (new ResponseFactory)->createResponse(401); + return $this->processError($response, [ "message" => $exception->getMessage(), - "uri" => (string)$request->getUri() + "uri" => (string) $request->getUri() ]); } @@ -183,29 +140,28 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface ]; /* Add decoded token to request as attribute when requested. */ - if ($this->options["attribute"]) { - $request = $request->withAttribute($this->options["attribute"], $decoded); + if ($this->options->attribute) { + $request = $request->withAttribute($this->options->attribute, $decoded); } /* Modify $request before calling next middleware. */ - if (is_callable($this->options["before"])) { - $beforeRequest = $this->options["before"]($request, $params); - if ($beforeRequest instanceof ServerRequestInterface) { - $request = $beforeRequest; - } + $beforeRequest = $this->options->onBeforeCallable($request, $params); + + if ($beforeRequest instanceof ServerRequestInterface) { + $request = $beforeRequest; } /* Everything ok, call next middleware. */ $response = $handler->handle($request); /* Modify $response before returning. */ - if (is_callable($this->options["after"])) { - $afterResponse = $this->options["after"]($response, $params); - if ($afterResponse instanceof ResponseInterface) { - return $afterResponse; - } + $afterResponse = $this->options->onAfterCallable($response, $params); + + if ($afterResponse) { + return $afterResponse; } + return $response; } @@ -219,11 +175,12 @@ public function withRules(array $rules): self $new = clone $this; /* Clear the stack */ unset($new->rules); - $new->rules = new \SplStack; + $new->rules = new SplStack; /* Add the rules */ foreach ($rules as $callable) { $new = $new->addRule($callable); } + return $new; } @@ -235,6 +192,7 @@ public function addRule(callable $callable): self $new = clone $this; $new->rules = clone $this->rules; $new->rules->push($callable); + return $new; } @@ -245,10 +203,11 @@ private function shouldAuthenticate(ServerRequestInterface $request): bool { /* If any of the rules in stack return false will not authenticate */ foreach ($this->rules as $callable) { - if (false === $callable($request)) { + if (!$callable($request)) { return false; } } + return true; } @@ -259,13 +218,7 @@ private function shouldAuthenticate(ServerRequestInterface $request): bool */ private function processError(ResponseInterface $response, array $arguments): ResponseInterface { - if (is_callable($this->options["error"])) { - $handlerResponse = $this->options["error"]($response, $arguments); - if ($handlerResponse instanceof ResponseInterface) { - return $handlerResponse; - } - } - return $response; + return $this->options->onError($response, $arguments) ?? $response; } /** @@ -274,10 +227,10 @@ private function processError(ResponseInterface $response, array $arguments): Re private function fetchToken(ServerRequestInterface $request): string { /* Check for token in header. */ - $header = $request->getHeaderLine($this->options["header"]); + $header = $request->getHeaderLine($this->options->header); - if (false === empty($header)) { - if (preg_match($this->options["regexp"], $header, $matches)) { + if (!empty($header)) { + if (preg_match($this->options->regexp, $header, $matches)) { $this->log(LogLevel::DEBUG, "Using token from request header"); return $matches[1]; } @@ -286,141 +239,58 @@ private function fetchToken(ServerRequestInterface $request): string /* Token not found in header try a cookie. */ $cookieParams = $request->getCookieParams(); - if (isset($cookieParams[$this->options["cookie"]])) { + if (isset($cookieParams[$this->options->cookie])) { $this->log(LogLevel::DEBUG, "Using token from cookie"); - if (preg_match($this->options["regexp"], $cookieParams[$this->options["cookie"]], $matches)) { + if (preg_match($this->options->regexp, $cookieParams[$this->options->cookie], $matches)) { return $matches[1]; } - return $cookieParams[$this->options["cookie"]]; - }; + return $cookieParams[$this->options->cookie]; + } /* If everything fails log and throw. */ $this->log(LogLevel::WARNING, "Token not found"); + throw new RuntimeException("Token not found."); } - /** - * Decode the token. - * - * @return mixed[] - */ private function decodeToken(string $token): array { + $keys = $this->createKeysFromAlgorithms(); + + if (count($keys) === 1) { + $keys = current($keys); + } + try { $decoded = JWT::decode( $token, - $this->options["secret"], - (array) $this->options["algorithm"] + $keys ); + return (array) $decoded; } catch (Exception $exception) { $this->log(LogLevel::WARNING, $exception->getMessage(), [$token]); + throw $exception; } } /** - * Hydrate options from given array. - * - * @param mixed[] $data + * @return array */ - private function hydrate(array $data = []): void + private function createKeysFromAlgorithms(): array { - foreach ($data as $key => $value) { - /* https://github.com/facebook/hhvm/issues/6368 */ - $key = str_replace(".", " ", $key); - $method = lcfirst(ucwords($key)); - $method = str_replace(" ", "", $method); - if (method_exists($this, $method)) { - /* Try to use setter */ - /** @phpstan-ignore-next-line */ - call_user_func([$this, $method], $value); - } else { - /* Or fallback to setting option directly */ - $this->options[$key] = $value; - } - } - } + $keyObjects = []; - /** - * Set path where middleware should bind to. - * - * @param string|string[] $path - */ - private function path($path): void - { - $this->options["path"] = (array) $path; - } + foreach ($this->options->algorithm as $kid => $algorithm) { + $keyId = is_numeric($kid) ? $algorithm : $kid; - /** - * Set path which middleware ignores. - * - * @param string|string[] $ignore - */ - private function ignore($ignore): void - { - $this->options["ignore"] = (array) $ignore; - } - - /** - * Set the cookie name where to search the token from. - */ - private function cookie(string $cookie): void - { - $this->options["cookie"] = $cookie; - } + $secret = $this->options->secret[$kid]; - /** - * Set the secure flag. - */ - private function secure(bool $secure): void - { - $this->options["secure"] = $secure; - } - - /** - * Set hosts where secure rule is relaxed. - * - * @param string[] $relaxed - */ - private function relaxed(array $relaxed): void - { - $this->options["relaxed"] = $relaxed; - } - - /** - * Set the secret key. - * - * @param string|string[] $secret - */ - private function secret($secret): void - { - if (false === is_array($secret) && false === is_string($secret) && ! $secret instanceof \ArrayAccess) { - throw new InvalidArgumentException( - 'Secret must be either a string or an array of "kid" => "secret" pairs' - ); + $keyObjects[$keyId] = new Key($secret, $algorithm); } - $this->options["secret"] = $secret; - } - /** - * Set the error handler. - */ - private function error(callable $error): void - { - if ($error instanceof Closure) { - $this->options["error"] = $error->bindTo($this); - } else { - $this->options["error"] = $error; - } - } - - /** - * Set the logger. - */ - private function logger(LoggerInterface $logger = null): void - { - $this->logger = $logger; + return $keyObjects; } /** @@ -435,65 +305,6 @@ private function log(string $level, string $message, array $context = []): void } } - /** - * Set the attribute name used to attach decoded token to request. - */ - private function attribute(string $attribute): void - { - $this->options["attribute"] = $attribute; - } - - /** - * Set the header where token is searched from. - */ - private function header(string $header): void - { - $this->options["header"] = $header; - } - - /** - * Set the regexp used to extract token from header or environment. - */ - private function regexp(string $regexp): void - { - $this->options["regexp"] = $regexp; - } - - /** - * Set the allowed algorithms - * - * @param string|string[] $algorithm - */ - private function algorithm($algorithm): void - { - $this->options["algorithm"] = (array) $algorithm; - } - - /** - * Set the before handler. - */ - - private function before(callable $before): void - { - if ($before instanceof Closure) { - $this->options["before"] = $before->bindTo($this); - } else { - $this->options["before"] = $before; - } - } - - /** - * Set the after handler. - */ - private function after(callable $after): void - { - if ($after instanceof Closure) { - $this->options["after"] = $after->bindTo($this); - } else { - $this->options["after"] = $after; - } - } - /** * Set the rules. * @param RuleInterface[] $rules diff --git a/src/JwtAuthentication/JwtAuthOptions.php b/src/JwtAuthentication/JwtAuthOptions.php new file mode 100644 index 0000000..ce9f7c3 --- /dev/null +++ b/src/JwtAuthentication/JwtAuthOptions.php @@ -0,0 +1,219 @@ + */ + public array $relaxed; + + public string $header; + public string $regexp; + public string $cookie; + public string $attribute; + /** @var array */ + public array $path; + + /** @var RuleInterface[] $rules */ + public array $rules; + + /** @var array */ + public array $ignore; + public $before; + public $after; + public $error; + + private JwtAuthentication $jwtAuthentication; + + public function __construct( + /** @var string|array|ArrayAccessImpl */ + $secret, + /** @var string|array|ArrayAccessImpl */ + $algorithm = "HS256", + bool $secure = true, + array $relaxed = ["localhost", "127.0.0.1"], + string $header = "Authorization", + string $regexp = "/Bearer\s+(.*)$/i", + string $cookie = "token", + string $attribute = "token", + array $path = ["/"], + array $ignore = [], + array $rules = [], + ?callable $before = null, + ?callable $after = null, + ?callable $error = null + ) { + $this->secret = $this->checkSecret($secret); + $this->algorithm = $this->applyAlgorithm($this->secret, $algorithm); + $this->secure = $secure; + $this->relaxed = $relaxed; + $this->header = $header; + $this->regexp = $regexp; + $this->cookie = $cookie; + $this->attribute = $attribute; + $this->path = $path; + $this->rules = $rules; + $this->ignore = $ignore; + $this->before = $before; + $this->after = $after; + $this->error = $error; + } + + public static function fromArray(array $data): self + { + $values = [ + "secret" => "", + "algorithm" => "HS256", + "secure" => true, + "relaxed" => ["localhost", "127.0.0.1"], + "header" => "Authorization", + "regexp" => "/Bearer\s+(.*)$/i", + "cookie" => "token", + "attribute" => "token", + "path" => ["/"], + "ignore" => [], + "rules" => [], + "before" => null, + "after" => null, + "error" => null + ]; + $inArray = []; + + foreach ($values as $key => $value) { + $inArray[$key] = $data[$key] ?? $value; + } + + return new self(...$inArray); + } + + public function bindToAuthentication(JwtAuthentication $target): self + { + $this->jwtAuthentication = $target; + + $this->error = $this->bindClosure($this->error, $target); + $this->before = $this->bindClosure($this->before, $target); + $this->after = $this->bindClosure($this->after, $target); + + return $this; + } + + /** + * Set the error handler. + */ + public function onError(ResponseInterface $response, array $arguments): ?ResponseInterface + { + $func = $this->error; + + return is_null($func) ? null : $func($response, $arguments); + } + + /** + * Set the before handler. + */ + public function onBeforeCallable(ServerRequestInterface $request, array $params): ?ServerRequestInterface + { + $func = $this->before; + + return is_null($func) ? null : $func($request, $params); + } + /** + * Set the after handler. + */ + public function onAfterCallable(ResponseInterface $response, array $params): ?ResponseInterface + { + $func = $this->after; + + return is_null($func) ? null : $func($response, $params); + } + + private function checkSecret($secret): array + { + if (!(is_array($secret) || is_string($secret) || $secret instanceof \ArrayAccess)) { + throw new InvalidArgumentException( + 'Secret must be either a string or an array of "kid" => "secret" pairs' + ); + } + + return (array) $secret; + } + + private function applyAlgorithm(array $secret, $algorithm) + { + if (is_string($algorithm)) { + $secretIndex = array_keys($secret); + + return array_fill_keys($secretIndex, $algorithm); + } + + foreach ($secret as $key => $value) { + if (!in_array($key, $algorithm)) { + throw new InvalidArgumentException( + "All secrets must have a corresponding algorithm" + ); + } + } + + return $algorithm; + } + + private function bindClosure(?callable $closure, JwtAuthentication $target): ?\Closure + { + if ($closure) { + if ($closure instanceof \Closure) { + return $closure->bindTo($target); + } + + return \Closure::fromCallable($closure); + } + + return null; + } +} diff --git a/tests/JwtAuthenticationTest.php b/tests/JwtAuthenticationTest.php index 12c704f..73d5d00 100644 --- a/tests/JwtAuthenticationTest.php +++ b/tests/JwtAuthenticationTest.php @@ -39,6 +39,7 @@ use Tuupola\Http\Factory\ResponseFactory; use Tuupola\Http\Factory\ServerRequestFactory; use Tuupola\Http\Factory\StreamFactory; +use Tuupola\Middleware\JwtAuthentication\JwtAuthOptions; use Tuupola\Middleware\JwtAuthentication\RequestMethodRule; use Tuupola\Middleware\JwtAuthentication\RequestPathRule; @@ -56,7 +57,7 @@ class JwtAuthenticationTest extends TestCase "exp" => "1744352741", "aud" => "www.example.com", "sub" => "someone@example.com", - "scope" => ["read", "write", "delete"] + "scope" => ["read", "write", "delete"], ]; public static array $betaTokenArray = [ @@ -65,24 +66,27 @@ class JwtAuthenticationTest extends TestCase "exp" => "1744352741", "aud" => "www.example.com", "sub" => "someone@example.com", - "scope" => ["read"] + "scope" => ["read"], ]; public function testShouldReturn401WithoutToken(): void { - $request = (new ServerRequestFactory) - ->createServerRequest("GET", "https://example.com/api"); + $request = (new ServerRequestFactory())->createServerRequest( + "GET", + "https://example.com/api" + ); + + $default = function (ServerRequestInterface $request) { + $response = (new ResponseFactory())->createResponse(); - $default = function (RequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); $response->getBody()->write("Success"); return $response; }; - + $options = new JwtAuthOptions( + "supersecretkeyyoushouldnotcommittogithub" + ); $collection = new MiddlewareCollection([ - $auth = new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub" - ]) + new JwtAuthentication($options), ]); $response = $collection->dispatch($request, $default); @@ -93,21 +97,25 @@ public function testShouldReturn401WithoutToken(): void public function testShouldReturn200WithTokenFromHeader(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/api") ->withHeader("X-Token", "Bearer " . self::$acmeToken); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; - $collection = new MiddlewareCollection([ - new JwtAuthentication([ + $options = JwtAuthOptions::fromArray( + [ "secret" => "supersecretkeyyoushouldnotcommittogithub", - "header" => "X-Token" - ]) + "header" => "X-Token", + ] + ); + + $collection = new MiddlewareCollection([ + new JwtAuthentication($options), ]); $response = $collection->dispatch($request, $default); @@ -118,22 +126,26 @@ public function testShouldReturn200WithTokenFromHeader(): void public function testShouldReturn200WithTokenFromHeaderWithCustomRegexp(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/api") ->withHeader("X-Token", self::$acmeToken); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; - $collection = new MiddlewareCollection([ - new JwtAuthentication([ + $options = JwtAuthOptions::fromArray( + [ "secret" => "supersecretkeyyoushouldnotcommittogithub", "header" => "X-Token", - "regexp" => "/(.*)/" - ]) + "regexp" => "/(.*)/", + ] + ); + + $collection = new MiddlewareCollection([ + new JwtAuthentication($options), ]); $response = $collection->dispatch($request, $default); @@ -144,21 +156,25 @@ public function testShouldReturn200WithTokenFromHeaderWithCustomRegexp(): void public function testShouldReturn200WithTokenFromCookie(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/api") ->withCookieParams(["nekot" => self::$acmeToken]); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; - $collection = new MiddlewareCollection([ - new JwtAuthentication([ + $options = JwtAuthOptions::fromArray( + [ "secret" => "supersecretkeyyoushouldnotcommittogithub", "cookie" => "nekot", - ]) + ] + ); + + $collection = new MiddlewareCollection([ + new JwtAuthentication($options), ]); $response = $collection->dispatch($request, $default); @@ -169,21 +185,25 @@ public function testShouldReturn200WithTokenFromCookie(): void public function testShouldReturn200WithTokenFromBearerCookie(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/api") ->withCookieParams(["nekot" => "Bearer " . self::$acmeToken]); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; - $collection = new MiddlewareCollection([ - new JwtAuthentication([ + $options = JwtAuthOptions::fromArray( + [ "secret" => "supersecretkeyyoushouldnotcommittogithub", "cookie" => "nekot", - ]) + ] + ); + + $collection = new MiddlewareCollection([ + new JwtAuthentication($options), ]); $response = $collection->dispatch($request, $default); @@ -192,8 +212,7 @@ public function testShouldReturn200WithTokenFromBearerCookie(): void $this->assertEquals("Success", $response->getBody()); } - - public function testShouldReturn200WithSecretArray(): void + public function testShouldReturn200WithSecretArray() { $request = (new ServerRequestFactory) ->createServerRequest("GET", "https://example.com/api") @@ -205,13 +224,17 @@ public function testShouldReturn200WithSecretArray(): void return $response; }; - $collection = new MiddlewareCollection([ - new JwtAuthentication([ + $options = JwtAuthOptions::fromArray( + [ "secret" => [ - "acme" =>"supersecretkeyyoushouldnotcommittogithub", - "beta" =>"anothersecretkeyfornevertocommittogithub" + "acme" => "supersecretkeyyoushouldnotcommittogithub", + "beta" => "anothersecretkeyfornevertocommittogithub" ] - ]) + ] + ); + + $collection = new MiddlewareCollection([ + new JwtAuthentication($options) ]); $response = $collection->dispatch($request, $default); @@ -232,12 +255,16 @@ public function testShouldReturn401WithSecretArray(): void }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => [ - "xxxx" =>"supersecretkeyyoushouldnotcommittogithub", - "yyyy" =>"anothersecretkeyfornevertocommittogithub" - ] - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => [ + "xxxx" => "supersecretkeyyoushouldnotcommittogithub", + "yyyy" => "anothersecretkeyfornevertocommittogithub" + ] + ] + ) + ) ]); $response = $collection->dispatch($request, $default); @@ -257,14 +284,16 @@ public function testShouldReturn200WithSecretArrayAccess(): void return $response; }; - $secret = new ArrayAccessImpl(); + $secret = new \ArrayObject(); $secret["acme"] = "supersecretkeyyoushouldnotcommittogithub"; - $secret["beta"] ="anothersecretkeyfornevertocommittogithub"; + $secret["beta"] = "anothersecretkeyfornevertocommittogithub"; + + $options = JwtAuthOptions::fromArray([ + "secret" => $secret + ]); $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => $secret - ]) + new JwtAuthentication($options) ]); $response = $collection->dispatch($request, $default); @@ -284,14 +313,16 @@ public function testShouldReturn401WithSecretArrayAccess(): void return $response; }; - $secret = new ArrayAccessImpl(); + $secret = new \ArrayObject(); $secret["xxxx"] = "supersecretkeyyoushouldnotcommittogithub"; $secret["yyyy"] = "anothersecretkeyfornevertocommittogithub"; + $options = JwtAuthOptions::fromArray([ + "secret" => $secret + ]); + $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => $secret - ]) + new JwtAuthentication($options) ]); $response = $collection->dispatch($request, $default); @@ -301,48 +332,62 @@ public function testShouldReturn401WithSecretArrayAccess(): void public function testShouldAlterResponseWithAnonymousAfter(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/api") ->withHeader("Authorization", "Bearer " . self::$acmeToken); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub", - "after" => function ($response, $arguments) { - return $response->withHeader("X-Brawndo", "plants crave"); - } - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + "after" => function ($response, $arguments) { + return $response->withHeader( + "X-Brawndo", + "plants crave" + ); + }, + ] + ) + ), ]); $response = $collection->dispatch($request, $default); $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals("plants crave", (string) $response->getHeaderLine("X-Brawndo")); + $this->assertEquals( + "plants crave", + (string) $response->getHeaderLine("X-Brawndo") + ); } public function testShouldAlterResponseWithInvokableAfter(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/api") ->withHeader("Authorization", "Bearer " . self::$acmeToken); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub", - "after" => new TestAfterHandler - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + "after" => new TestAfterHandler(), + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -356,21 +401,25 @@ public function testShouldAlterResponseWithInvokableAfter(): void public function testShouldAlterResponseWithArrayNotationAfter(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/api") ->withHeader("Authorization", "Bearer " . self::$acmeToken); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub", - "after" => [TestAfterHandler::class, "after"] - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + "after" => [TestAfterHandler::class, "after"], + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -384,21 +433,25 @@ public function testShouldAlterResponseWithArrayNotationAfter(): void public function testShouldReturn401WithInvalidAlgorithm(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/api") ->withHeader("Authorization", "Bearer " . self::$acmeToken); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub", - "algorithm" => "nosuch", - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + "algorithm" => "nosuch", + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -409,20 +462,24 @@ public function testShouldReturn401WithInvalidAlgorithm(): void public function testShouldReturn200WithOptions(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/api") ->withMethod("OPTIONS"); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub" - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -433,20 +490,24 @@ public function testShouldReturn200WithOptions(): void public function testShouldReturn400WithInvalidToken(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/api") ->withHeader("Authorization", "Bearer invalid" . self::$acmeToken); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub" - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -457,20 +518,24 @@ public function testShouldReturn400WithInvalidToken(): void public function testShouldReturn400WithExpiredToken(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/api") ->withHeader("Authorization", "Bearer " . self::$expired); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub" - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -481,45 +546,57 @@ public function testShouldReturn400WithExpiredToken(): void public function testShouldReturn200WithoutTokenWithPath(): void { - $request = (new ServerRequestFactory) - ->createServerRequest("GET", "https://example.com/public"); + $request = (new ServerRequestFactory())->createServerRequest( + "GET", + "https://example.com/public" + ); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "path" => ["/api", "/foo"], - "secret" => "supersecretkeyyoushouldnotcommittogithub" - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "path" => ["/api", "/foo"], + "secret" => "supersecretkeyyoushouldnotcommittogithub", + ] + ) + ), ]); $response = $collection->dispatch($request, $default); $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals("Success", $response->getBody()); + $this->assertEquals("Success", $response->getBody()->__toString()); } public function testShouldReturn200WithoutTokenWithIgnore(): void { - $request = (new ServerRequestFactory) - ->createServerRequest("GET", "https://example.com/api/ping"); + $request = (new ServerRequestFactory())->createServerRequest( + "GET", + "https://example.com/api/ping" + ); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "path" => ["/api", "/foo"], - "ignore" => ["/api/ping"], - "secret" => "supersecretkeyyoushouldnotcommittogithub" - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "path" => ["/api", "/foo"], + "ignore" => ["/api/ping"], + "secret" => "supersecretkeyyoushouldnotcommittogithub", + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -532,20 +609,24 @@ public function testShouldNotAllowInsecure(): void { $this->expectException("RuntimeException"); - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "http://example.com/api") ->withHeader("Authorization", "Bearer " . self::$acmeToken); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub" - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -553,21 +634,25 @@ public function testShouldNotAllowInsecure(): void public function testShouldAllowInsecure(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "http://example.com/api") ->withHeader("Authorization", "Bearer " . self::$acmeToken); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub", - "secure" => false - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + "secure" => false, + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -578,20 +663,24 @@ public function testShouldAllowInsecure(): void public function testShouldRelaxInsecureInLocalhost(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "http://localhost/api") ->withHeader("Authorization", "Bearer " . self::$acmeToken); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub" - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -602,21 +691,25 @@ public function testShouldRelaxInsecureInLocalhost(): void public function testShouldRelaxInsecureInExampleCom(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "http://example.com/api") ->withHeader("Authorization", "Bearer " . self::$acmeToken); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub", - "relaxed" => ["example.com"], - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + "relaxed" => ["example.com"], + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -627,23 +720,27 @@ public function testShouldRelaxInsecureInExampleCom(): void public function testShouldAttachToken(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/api") ->withHeader("Authorization", "Bearer " . self::$acmeToken); $default = function (ServerRequestInterface $request) { $acmeToken = $request->getAttribute("token"); - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write($acmeToken["iss"]); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub" - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -654,24 +751,28 @@ public function testShouldAttachToken(): void public function testShouldAttachCustomToken(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/api") ->withHeader("Authorization", "Bearer " . self::$acmeToken); $default = function (ServerRequestInterface $request) { $acmeToken = $request->getAttribute("nekot"); - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write($acmeToken["iss"]); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub", - "attribute" => "nekot" - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + "attribute" => "nekot", + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -682,7 +783,7 @@ public function testShouldAttachCustomToken(): void public function testShouldCallAfterWithProperArguments(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/api") ->withHeader("Authorization", "Bearer " . self::$acmeToken); @@ -690,19 +791,23 @@ public function testShouldCallAfterWithProperArguments(): void $token = null; $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub", - "after" => function ($response, $arguments) use (&$decoded, &$token) { - $decoded = $arguments["decoded"]; - $token = $arguments["token"]; - } - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + "after" => function ($response, $arguments) use (&$decoded, &$token) { + $decoded = $arguments["decoded"]; + $token = $arguments["token"]; + }, + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -715,7 +820,7 @@ public function testShouldCallAfterWithProperArguments(): void public function testShouldCallBeforeWithProperArguments(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/api") ->withHeader("Authorization", "Bearer " . self::$acmeToken); @@ -723,19 +828,23 @@ public function testShouldCallBeforeWithProperArguments(): void $token = null; $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub", - "before" => function ($response, $arguments) use (&$decoded, &$token) { - $decoded = $arguments["decoded"]; - $token = $arguments["token"]; - } - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + "before" => function ($response, $arguments) use (&$decoded, &$token) { + $decoded = $arguments["decoded"]; + $token = $arguments["token"]; + }, + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -748,51 +857,68 @@ public function testShouldCallBeforeWithProperArguments(): void public function testShouldCallAnonymousErrorFunction(): void { - $request = (new ServerRequestFactory) - ->createServerRequest("GET", "https://example.com/api"); + $request = (new ServerRequestFactory())->createServerRequest( + "GET", + "https://example.com/api" + ); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommit", - "error" => function (ResponseInterface $response, $arguments) use (&$dummy) { - $response->getBody()->write("error"); - return $response - ->withHeader("X-Electrolytes", "Plants"); - } - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommit", + "error" => function (ResponseInterface $response, $arguments) use (&$dummy) { + $response->getBody()->write("error"); + return $response->withHeader( + "X-Electrolytes", + "Plants" + ); + }, + ] + ) + ), ]); $response = $collection->dispatch($request, $default); $this->assertEquals(401, $response->getStatusCode()); - $this->assertEquals("Plants", $response->getHeaderLine("X-Electrolytes")); + $this->assertEquals( + "Plants", + $response->getHeaderLine("X-Electrolytes") + ); $this->assertEquals("error", $response->getBody()); } public function testShouldCallInvokableErrorClass(): void { - $request = (new ServerRequestFactory) - ->createServerRequest("GET", "https://example.com/api"); + $request = (new ServerRequestFactory())->createServerRequest( + "GET", + "https://example.com/api" + ); $dummy = null; $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommit", - "error" => new TestErrorHandler - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommit", + "error" => new TestErrorHandler(), + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -804,22 +930,28 @@ public function testShouldCallInvokableErrorClass(): void public function testShouldCallArrayNotationError(): void { - $request = (new ServerRequestFactory) - ->createServerRequest("GET", "https://example.com/api"); + $request = (new ServerRequestFactory())->createServerRequest( + "GET", + "https://example.com/api" + ); $dummy = null; $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommit", - "error" => [TestErrorHandler::class, "error"] - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommit", + "error" => [TestErrorHandler::class, "error"], + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -831,26 +963,32 @@ public function testShouldCallArrayNotationError(): void public function testShouldCallErrorAndModifyBody(): void { - $request = (new ServerRequestFactory) - ->createServerRequest("GET", "https://example.com/api"); + $request = (new ServerRequestFactory())->createServerRequest( + "GET", + "https://example.com/api" + ); $dummy = null; $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub", - "error" => function (ResponseInterface $response, $arguments) use (&$dummy) { - $dummy = true; - $response->getBody()->write("Error"); - return $response; - } - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + "error" => function (ResponseInterface $response, $arguments) use (&$dummy) { + $dummy = true; + $response->getBody()->write("Error"); + return $response; + }, + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -862,21 +1000,26 @@ public function testShouldCallErrorAndModifyBody(): void public function testShouldAllowUnauthenticatedHttp(): void { - - $request = (new ServerRequestFactory) - ->createServerRequest("GET", "https://example.com/public/foo"); + $request = (new ServerRequestFactory())->createServerRequest( + "GET", + "https://example.com/public/foo" + ); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub", - "path" => ["/api", "/bar"], - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + "path" => ["/api", "/bar"], + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -887,25 +1030,31 @@ public function testShouldAllowUnauthenticatedHttp(): void public function testShouldReturn401FromAfter(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/api") ->withHeader("Authorization", "Bearer " . self::$acmeToken); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub", - "after" => function ($response, $arguments) { - return $response - ->withBody((new StreamFactory)->createStream()) - ->withStatus(401); - } - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + "after" => function ($response, $arguments) { + return $response + ->withBody( + (new StreamFactory())->createStream() + ) + ->withStatus(401); + }, + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -916,24 +1065,28 @@ public function testShouldReturn401FromAfter(): void public function testShouldModifyRequestUsingAnonymousBefore(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/") ->withHeader("Authorization", "Bearer " . self::$acmeToken); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $test = $request->getAttribute("test"); $response->getBody()->write($test); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub", - "before" => function ($request, $arguments) { - return $request->withAttribute("test", "test"); - } - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + "before" => function ($request, $arguments) { + return $request->withAttribute("test", "test"); + }, + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -944,22 +1097,26 @@ public function testShouldModifyRequestUsingAnonymousBefore(): void public function testShouldModifyRequestUsingInvokableBefore(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/") ->withHeader("Authorization", "Bearer " . self::$acmeToken); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $test = $request->getAttribute("test"); $response->getBody()->write($test); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub", - "before" => new TestBeforeHandler - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + "before" => new TestBeforeHandler(), + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -970,22 +1127,26 @@ public function testShouldModifyRequestUsingInvokableBefore(): void public function testShouldModifyRequestUsingArrayNotationBefore(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/") ->withHeader("Authorization", "Bearer " . self::$acmeToken); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $test = $request->getAttribute("test"); $response->getBody()->write($test); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub", - "before" => [TestBeforeHandler::class, "before"] - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + "before" => [TestBeforeHandler::class, "before"], + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -996,28 +1157,34 @@ public function testShouldModifyRequestUsingArrayNotationBefore(): void public function testShouldHandleRulesArrayBug84(): void { - $request = (new ServerRequestFactory) - ->createServerRequest("GET", "https://example.com/api"); + $request = (new ServerRequestFactory())->createServerRequest( + "GET", + "https://example.com/api" + ); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub", - "rules" => [ - new RequestPathRule([ - "path" => ["/api"], - "ignore" => ["/api/login"], - ]), - new RequestMethodRule([ - "ignore" => ["OPTIONS"], - ]) - ], - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + "rules" => [ + new RequestPathRule([ + "path" => ["/api"], + "ignore" => ["/api/login"], + ]), + new RequestMethodRule([ + "ignore" => ["OPTIONS"], + ]), + ], + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -1025,8 +1192,10 @@ public function testShouldHandleRulesArrayBug84(): void $this->assertEquals(401, $response->getStatusCode()); $this->assertEquals("", $response->getBody()); - $request = (new ServerRequestFactory) - ->createServerRequest("GET", "https://example.com/api/login"); + $request = (new ServerRequestFactory())->createServerRequest( + "GET", + "https://example.com/api/login" + ); $response = $collection->dispatch($request, $default); @@ -1036,20 +1205,26 @@ public function testShouldHandleRulesArrayBug84(): void public function testShouldHandleDefaultPathBug118(): void { - $request = (new ServerRequestFactory) - ->createServerRequest("GET", "https://example.com/api"); + $request = (new ServerRequestFactory())->createServerRequest( + "GET", + "https://example.com/api" + ); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub", - "ignore" => "/api/login", - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + "ignore" => ["/api/login"], + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -1057,8 +1232,10 @@ public function testShouldHandleDefaultPathBug118(): void $this->assertEquals(401, $response->getStatusCode()); $this->assertEquals("", $response->getBody()); - $request = (new ServerRequestFactory) - ->createServerRequest("GET", "https://example.com/api/login"); + $request = (new ServerRequestFactory())->createServerRequest( + "GET", + "https://example.com/api/login" + ); $response = $collection->dispatch($request, $default); @@ -1068,31 +1245,35 @@ public function testShouldHandleDefaultPathBug118(): void public function testShouldBindToMiddleware(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/") ->withHeader("Authorization", "Bearer " . self::$acmeToken); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $before = $request->getAttribute("before"); $response->getBody()->write($before); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub", - "before" => function ($request, $arguments) { - $before = get_class($this); - return $request->withAttribute("before", $before); - }, - "after" => function ($response, $arguments) { - $after = get_class($this); - $response->getBody()->write($after); - return $response; - } - - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + "before" => function (ServerRequestInterface $request, $arguments) { + $before = get_class($this); + var_dump($before); + return $request->withAttribute("before", $before); + }, + "after" => function ($response, $arguments) { + $after = get_class($this); + $response->getBody()->write($after); + return $response; + }, + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -1103,16 +1284,20 @@ public function testShouldBindToMiddleware(): void public function testShouldHandlePsr7(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/api") ->withHeader("X-Token", "Bearer " . self::$acmeToken); - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); - $auth = new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub", - "header" => "X-Token" - ]); + $auth = new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + "header" => "X-Token", + ] + ) + ); $next = function (ServerRequestInterface $request, ResponseInterface $response) { $response->getBody()->write("Success"); @@ -1127,24 +1312,30 @@ public function testShouldHandlePsr7(): void public function testShouldHaveUriInErrorHandlerIssue96(): void { - $request = (new ServerRequestFactory) - ->createServerRequest("GET", "https://example.com/api/foo?bar=pop"); + $request = (new ServerRequestFactory())->createServerRequest( + "GET", + "https://example.com/api/foo?bar=pop" + ); $dummy = null; $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub", - "error" => function (ResponseInterface $response, $arguments) use (&$dummy) { - $dummy = $arguments["uri"]; - } - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + "error" => function (ResponseInterface $response, $arguments) use (&$dummy) { + $dummy = $arguments["uri"]; + }, + ] + ) + ), ]); $response = $collection->dispatch($request, $default); @@ -1156,22 +1347,26 @@ public function testShouldHaveUriInErrorHandlerIssue96(): void public function testShouldUseCookieIfHeaderMissingIssue156(): void { - $request = (new ServerRequestFactory) + $request = (new ServerRequestFactory()) ->createServerRequest("GET", "https://example.com/api") ->withCookieParams(["token" => self::$acmeToken]); $default = function (ServerRequestInterface $request) { - $response = (new ResponseFactory)->createResponse(); + $response = (new ResponseFactory())->createResponse(); $response->getBody()->write("Success"); return $response; }; $collection = new MiddlewareCollection([ - new JwtAuthentication([ - "secret" => "supersecretkeyyoushouldnotcommittogithub", - "header" => "X-Token", - "regexp" => "/(.*)/", - ]) + new JwtAuthentication( + JwtAuthOptions::fromArray( + [ + "secret" => "supersecretkeyyoushouldnotcommittogithub", + "header" => "X-Token", + "regexp" => "/(.*)/", + ] + ) + ), ]); $response = $collection->dispatch($request, $default);