From b645f30baba6784e5286ff7c92375e536b4678ec Mon Sep 17 00:00:00 2001 From: Oleg Baturin Date: Tue, 16 Sep 2025 16:20:05 +0700 Subject: [PATCH 1/7] refactor middleware authentication failure handler --- composer.json | 1 + config/di-web.php | 14 +++++++ .../AuthenticationFailureHandler.php | 5 +-- src/AuthenticationFailureHandlerInterface.php | 16 ++++++++ src/Middleware/Authentication.php | 21 ++++------ tests/AuthenticationFailureHandlerTest.php | 2 +- tests/AuthenticationMiddlewareTest.php | 24 +++++------ tests/ConfigTest.php | 41 +++++++++++++++++++ 8 files changed, 94 insertions(+), 30 deletions(-) create mode 100644 config/di-web.php rename src/{Handler => }/AuthenticationFailureHandler.php (82%) create mode 100644 src/AuthenticationFailureHandlerInterface.php create mode 100644 tests/ConfigTest.php diff --git a/composer.json b/composer.json index 67b090b..065715a 100644 --- a/composer.json +++ b/composer.json @@ -44,6 +44,7 @@ "roave/infection-static-analysis-plugin": "^1.25", "spatie/phpunit-watcher": "^1.23.6", "vimeo/psalm": "^4.30 || ^5.26.1 || ^6.10", + "yiisoft/di": "^1.4", "yiisoft/yii-debug": "dev-master || dev-php80" }, "autoload": { diff --git a/config/di-web.php b/config/di-web.php new file mode 100644 index 0000000..f4b5931 --- /dev/null +++ b/config/di-web.php @@ -0,0 +1,14 @@ + AuthenticationFailureHandler::class, +]; diff --git a/src/Handler/AuthenticationFailureHandler.php b/src/AuthenticationFailureHandler.php similarity index 82% rename from src/Handler/AuthenticationFailureHandler.php rename to src/AuthenticationFailureHandler.php index d9396ea..b248c91 100644 --- a/src/Handler/AuthenticationFailureHandler.php +++ b/src/AuthenticationFailureHandler.php @@ -2,18 +2,17 @@ declare(strict_types=1); -namespace Yiisoft\Auth\Handler; +namespace Yiisoft\Auth; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; -use Psr\Http\Server\RequestHandlerInterface; use Yiisoft\Http\Status; /** * Default authentication failure handler. Responds with "401 Unauthorized" HTTP status code. */ -final class AuthenticationFailureHandler implements RequestHandlerInterface +final class AuthenticationFailureHandler implements AuthenticationFailureHandlerInterface { public function __construct(private ResponseFactoryInterface $responseFactory) { diff --git a/src/AuthenticationFailureHandlerInterface.php b/src/AuthenticationFailureHandlerInterface.php new file mode 100644 index 0000000..e9aba10 --- /dev/null +++ b/src/AuthenticationFailureHandlerInterface.php @@ -0,0 +1,16 @@ +failureHandler = $authenticationFailureHandler ?? new AuthenticationFailureHandler( - $responseFactory - ); } public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface diff --git a/tests/AuthenticationFailureHandlerTest.php b/tests/AuthenticationFailureHandlerTest.php index b9d8f61..353a34f 100644 --- a/tests/AuthenticationFailureHandlerTest.php +++ b/tests/AuthenticationFailureHandlerTest.php @@ -8,7 +8,7 @@ use Nyholm\Psr7\ServerRequest; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ServerRequestInterface; -use Yiisoft\Auth\Handler\AuthenticationFailureHandler; +use Yiisoft\Auth\AuthenticationFailureHandler; use Yiisoft\Http\Method; use Yiisoft\Http\Status; diff --git a/tests/AuthenticationMiddlewareTest.php b/tests/AuthenticationMiddlewareTest.php index 186ced2..ebfb5c7 100644 --- a/tests/AuthenticationMiddlewareTest.php +++ b/tests/AuthenticationMiddlewareTest.php @@ -12,6 +12,7 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; +use Yiisoft\Auth\AuthenticationFailureHandlerInterface; use Yiisoft\Auth\AuthenticationMethodInterface; use Yiisoft\Auth\IdentityInterface; use Yiisoft\Auth\Middleware\Authentication; @@ -52,7 +53,7 @@ function (ServerRequestInterface $request) use ($identity) { } ); - $auth = new Authentication($this->authenticationMethod, $this->responseFactory); + $auth = new Authentication($this->authenticationMethod, $this->createAuthenticationFailureHandler()); $auth->process($request, $handler); } @@ -81,7 +82,7 @@ public function testShouldSkipCheckForOptionalPath(string $path): void ->expects($this->once()) ->method('handle'); - $auth = (new Authentication($this->authenticationMethod, $this->responseFactory)) + $auth = (new Authentication($this->authenticationMethod, $this->createAuthenticationFailureHandler())) ->withOptionalPatterns([$path]); $auth->process($request, $handler); } @@ -109,7 +110,7 @@ public function testShouldNotExecuteHandlerAndReturn401OnAuthenticationFailure() ->expects($this->never()) ->method('handle'); - $auth = new Authentication($this->authenticationMethod, $this->responseFactory); + $auth = new Authentication($this->authenticationMethod, $this->createAuthenticationFailureHandler()); $response = $auth->process($request, $handler); $this->assertEquals(401, $response->getStatusCode()); $this->assertEquals($headerValue, $response->getHeaderLine($header)); @@ -138,33 +139,32 @@ public function testCustomAuthenticationFailureResponse(): void ->expects($this->never()) ->method('handle'); - $failureResponse = 'test custom response'; + $failureResponseBody = 'test custom response'; $auth = new Authentication( $this->authenticationMethod, - $this->responseFactory, - $this->createAuthenticationFailureHandler($failureResponse) + $this->createAuthenticationFailureHandler($failureResponseBody) ); $response = $auth->process($request, $handler); $this->assertEquals(401, $response->getStatusCode()); $this->assertEquals($headerValue, $response->getHeaderLine($header)); - $this->assertEquals($failureResponse, (string)$response->getBody()); + $this->assertEquals($failureResponseBody, (string)$response->getBody()); } public function testImmutability(): void { $original = new Authentication( $this->authenticationMethod, - $this->responseFactory + $this->createAuthenticationFailureHandler() ); $this->assertNotSame($original, $original->withOptionalPatterns(['test'])); } - private function createAuthenticationFailureHandler(string $failureResponse): RequestHandlerInterface + private function createAuthenticationFailureHandler(string $failureResponseBody = 'Authentication is failed'): AuthenticationFailureHandlerInterface { - return new class ($failureResponse, new Psr17Factory()) implements RequestHandlerInterface { - public function __construct(private string $failureResponse, private ResponseFactoryInterface $responseFactory) + return new class ($failureResponseBody, new Psr17Factory()) implements AuthenticationFailureHandlerInterface { + public function __construct(private string $failureResponseBody, private ResponseFactoryInterface $responseFactory) { } @@ -173,7 +173,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface $response = $this->responseFactory->createResponse(Status::UNAUTHORIZED); $response ->getBody() - ->write($this->failureResponse); + ->write($this->failureResponseBody); return $response; } }; diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php new file mode 100644 index 0000000..c98feca --- /dev/null +++ b/tests/ConfigTest.php @@ -0,0 +1,41 @@ +createContainer(); + + $failureHandler = $container->get(AuthenticationFailureHandlerInterface::class); + $this->assertInstanceOf(AuthenticationFailureHandler::class, $failureHandler); + } + + private function createContainer(): Container + { + return new Container( + ContainerConfig::create()->withDefinitions([ + ResponseFactoryInterface::class => Psr17Factory::class, + ...$this->getContainerDefinitions(), + ]) + ); + } + + private function getContainerDefinitions(): array + { + return require dirname(__DIR__) . '/config/di-web.php'; + } +} From 35ca455a2f4bfa59123eab049c1d09509fe67204 Mon Sep 17 00:00:00 2001 From: Oleg Baturin Date: Tue, 16 Sep 2025 16:21:19 +0700 Subject: [PATCH 2/7] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61db628..416174d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## 3.2.1 under development -- no changes in this release. +- Enh #101: Refactor `Authentication` middleware authentication failure handling (@olegbaturin) ## 3.2.0 September 11, 2025 From 2bf510a158f08fb54c69e251febd6f0566590d86 Mon Sep 17 00:00:00 2001 From: Oleg Baturin Date: Tue, 16 Sep 2025 17:28:42 +0700 Subject: [PATCH 3/7] bump min php ver to 8.1 --- .github/workflows/bc.yml | 2 +- .github/workflows/build.yml | 2 +- .github/workflows/composer-require-checker.yml | 2 +- .github/workflows/static.yml | 2 +- CHANGELOG.md | 1 + composer.json | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/bc.yml b/.github/workflows/bc.yml index f8cc564..00041a9 100644 --- a/.github/workflows/bc.yml +++ b/.github/workflows/bc.yml @@ -30,4 +30,4 @@ jobs: os: >- ['ubuntu-latest'] php: >- - ['8.0'] + ['8.1'] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 68fac01..3584e6c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,4 +31,4 @@ jobs: os: >- ['ubuntu-latest', 'windows-latest'] php: >- - ['8.0', '8.1', '8.2', '8.3', '8.4'] + ['8.1', '8.2', '8.3', '8.4'] diff --git a/.github/workflows/composer-require-checker.yml b/.github/workflows/composer-require-checker.yml index 632bfd4..a93390b 100644 --- a/.github/workflows/composer-require-checker.yml +++ b/.github/workflows/composer-require-checker.yml @@ -31,4 +31,4 @@ jobs: os: >- ['ubuntu-latest'] php: >- - ['8.0', '8.1', '8.2', '8.3', '8.4'] + ['8.1', '8.2', '8.3', '8.4'] diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index 0a323eb..d03874d 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -29,4 +29,4 @@ jobs: os: >- ['ubuntu-latest'] php: >- - ['8.0', '8.1', '8.2', '8.3', '8.4'] + ['8.1', '8.2', '8.3', '8.4'] diff --git a/CHANGELOG.md b/CHANGELOG.md index 416174d..ef419b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 3.2.1 under development - Enh #101: Refactor `Authentication` middleware authentication failure handling (@olegbaturin) +- Chg #101: Change PHP constraint in composer.json to 8.1 - 8.4 (@olegbaturin) ## 3.2.0 September 11, 2025 diff --git a/composer.json b/composer.json index 065715a..c32b607 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ } ], "require": { - "php": "8.0 - 8.4", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.0 || ^2.0", "psr/http-server-handler": "^1.0", From cfbad1dedc18a64225d30d819f4504069eed10cb Mon Sep 17 00:00:00 2001 From: Oleg Baturin Date: Wed, 17 Sep 2025 16:14:22 +0700 Subject: [PATCH 4/7] update composer.json config-plugin options --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index c32b607..e66c60b 100644 --- a/composer.json +++ b/composer.json @@ -62,6 +62,7 @@ "source-directory": "config" }, "config-plugin": { + "di-web": "di-web.php", "params": "params.php" } }, From c83a7c5e4d050cf953f0ade4c6db8ee89871a0aa Mon Sep 17 00:00:00 2001 From: Oleg Baturin Date: Sat, 27 Sep 2025 17:19:21 +0700 Subject: [PATCH 5/7] update php ver constraint format --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index e66c60b..dd48470 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ } ], "require": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", + "php": "8.1 - 8.4", "psr/http-factory": "^1.0", "psr/http-message": "^1.0 || ^2.0", "psr/http-server-handler": "^1.0", From 0811e9b8263e8617bbcb95dbd74f7d5200f02d85 Mon Sep 17 00:00:00 2001 From: Oleg Baturin Date: Tue, 30 Sep 2025 13:46:14 +0700 Subject: [PATCH 6/7] fix the grammar --- tests/AuthenticationMiddlewareTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/AuthenticationMiddlewareTest.php b/tests/AuthenticationMiddlewareTest.php index ebfb5c7..33f2d6b 100644 --- a/tests/AuthenticationMiddlewareTest.php +++ b/tests/AuthenticationMiddlewareTest.php @@ -161,7 +161,7 @@ public function testImmutability(): void $this->assertNotSame($original, $original->withOptionalPatterns(['test'])); } - private function createAuthenticationFailureHandler(string $failureResponseBody = 'Authentication is failed'): AuthenticationFailureHandlerInterface + private function createAuthenticationFailureHandler(string $failureResponseBody = 'Authentication failed'): AuthenticationFailureHandlerInterface { return new class ($failureResponseBody, new Psr17Factory()) implements AuthenticationFailureHandlerInterface { public function __construct(private string $failureResponseBody, private ResponseFactoryInterface $responseFactory) From 594bcfcbb689cdc920432e08542d61282e915126 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Fri, 9 Jan 2026 12:32:36 +0300 Subject: [PATCH 7/7] fix --- tests/AuthenticationMiddlewareTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/AuthenticationMiddlewareTest.php b/tests/AuthenticationMiddlewareTest.php index 658d2f1..19f6664 100644 --- a/tests/AuthenticationMiddlewareTest.php +++ b/tests/AuthenticationMiddlewareTest.php @@ -163,8 +163,10 @@ public function testImmutability(): void private function createAuthenticationFailureHandler(string $failureResponseBody = 'Authentication failed'): AuthenticationFailureHandlerInterface { return new class ($failureResponseBody, new Psr17Factory()) implements AuthenticationFailureHandlerInterface { - public function __construct(private string $failureResponseBody, private ResponseFactoryInterface $responseFactory) - { + public function __construct( + private readonly string $failureResponseBody, + private readonly ResponseFactoryInterface $responseFactory, + ) { } public function handle(ServerRequestInterface $request): ResponseInterface