From 3f32eb545ed635927a5ad33b135e98aeef74ae6a Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Wed, 5 Aug 2026 17:09:42 +0300 Subject: [PATCH 1/3] deprecated interface --- composer.json | 9 ++++++--- psalm.xml | 2 +- src/Debug/AuthenticationMethodInterfaceProxy.php | 3 +++ src/Method/Composite.php | 2 ++ src/Method/HttpBasic.php | 2 ++ src/Method/HttpBearer.php | 2 ++ src/Method/HttpCookie.php | 2 ++ src/Method/HttpHeader.php | 2 ++ src/Method/QueryParameter.php | 2 ++ 9 files changed, 22 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 90f51d2..aebabbf 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,7 @@ "rector/rector": "^2.3.0", "roave/infection-static-analysis-plugin": "^1.35", "spatie/phpunit-watcher": "^1.24.4", - "vimeo/psalm": "^5.26.1 || ^6.10", + "vimeo/psalm": "^5.26.1 || ^6.16.1", "yiisoft/yii-debug": "dev-master" }, "autoload": { @@ -66,8 +66,11 @@ } }, "scripts": { - "test": "phpunit --testdox --no-interaction", - "test-watch": "phpunit-watcher watch" + "test": "phpunit", + "test-watch": "phpunit-watcher watch", + "cs-fix": "php-cs-fixer fix", + "rector": "rector", + "psalm": "psalm" }, "config": { "sort-packages": true, diff --git a/psalm.xml b/psalm.xml index 2b54b69..7056d93 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,6 +1,6 @@ Date: Wed, 5 Aug 2026 17:14:22 +0300 Subject: [PATCH 2/3] Fix `HttpHeader` authentication to correctly handle header value `"0"` --- CHANGELOG.md | 1 + src/Method/HttpHeader.php | 2 +- tests/Method/HttpHeaderTest.php | 21 +++++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bb027c..83c6d20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - New #113: Split `AuthenticationMethodInterface` into focused authentication and challenge interfaces (@samdark, @vjik) - Bug #116: Fix authentication scheme in `HttpBearer` challenge according to RFC 6750 (@samdark) +- Bug #123: Fix `HttpHeader` authentication to correctly handle header value `"0"` (@vjik) - Chg #104: Bump minimal PHP version to 8.1 (@vjik) - Enh #104: Explicitly mark readonly properties (@vjik) - Enh #105: Explicitly import classes and functions in "use" section (@mspirkov) diff --git a/src/Method/HttpHeader.php b/src/Method/HttpHeader.php index 4af0464..4e81c30 100644 --- a/src/Method/HttpHeader.php +++ b/src/Method/HttpHeader.php @@ -102,7 +102,7 @@ protected function getAuthenticationToken(ServerRequestInterface $request): ?str { $authHeaders = $request->getHeader($this->headerName); $authHeader = reset($authHeaders); - if (!empty($authHeader)) { + if ($authHeader !== false && $authHeader !== '') { if (preg_match($this->pattern, $authHeader, $matches)) { $authHeader = $matches[1]; } else { diff --git a/tests/Method/HttpHeaderTest.php b/tests/Method/HttpHeaderTest.php index 0519c1c..7759f45 100644 --- a/tests/Method/HttpHeaderTest.php +++ b/tests/Method/HttpHeaderTest.php @@ -143,6 +143,27 @@ public function testWithTokenType(): void ); } + public function testZeroValueHeader(): void + { + $identityRepository = new FakeIdentityRepository($this->createIdentity()); + $result = (new HttpHeader($identityRepository))->authenticate( + $this->createRequest(['X-Api-Key' => '0']), + ); + + $this->assertNotNull($result); + $this->assertEquals('test-id', $result->getId()); + $this->assertEquals( + [ + 'findIdentityByToken' + => [ + 'token' => '0', + 'type' => null, + ], + ], + $identityRepository->getCallParams(), + ); + } + private function createIdentity(): IdentityInterface { return new FakeIdentity('test-id'); From dc20778fe42c93ff1f9754dbf6cc1d38254c1ce7 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Wed, 5 Aug 2026 17:25:49 +0300 Subject: [PATCH 3/3] psalm 1 --- composer.json | 2 +- psalm.xml | 2 +- src/Method/HttpBasic.php | 27 +++++++++++++++++++++++---- src/Method/HttpCookie.php | 3 +++ src/Middleware/Authentication.php | 4 ++-- 5 files changed, 30 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index aebabbf..8a2b26e 100644 --- a/composer.json +++ b/composer.json @@ -70,7 +70,7 @@ "test-watch": "phpunit-watcher watch", "cs-fix": "php-cs-fixer fix", "rector": "rector", - "psalm": "psalm" + "psalm": "psalm --no-cache" }, "config": { "sort-packages": true, diff --git a/psalm.xml b/psalm.xml index 7056d93..ef5353f 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,6 +1,6 @@ getServerParams()['PHP_AUTH_USER'] ?? null; - $password = $request->getServerParams()['PHP_AUTH_PW'] ?? null; + /** + * @var string[] $serverParams + */ + $serverParams = $request->getServerParams(); + $username = $serverParams['PHP_AUTH_USER'] ?? null; + $password = $serverParams['PHP_AUTH_PW'] ?? null; if ($username !== null || $password !== null) { return [$username, $password]; } @@ -158,9 +169,17 @@ private function getTokenFromHeaders(ServerRequestInterface $request): ?string return $header; } - return $request->getServerParams()['REDIRECT_HTTP_AUTHORIZATION'] ?? null; + /** + * @var string[] $serverParams + */ + $serverParams = $request->getServerParams(); + + return $serverParams['REDIRECT_HTTP_AUTHORIZATION'] ?? null; } + /** + * @psalm-return array{0: ?string, 1?: ?string} + */ private function extractCredentialsFromHeader(#[SensitiveParameter] string $authToken): array { return array_map( diff --git a/src/Method/HttpCookie.php b/src/Method/HttpCookie.php index 88cff0a..40c00b2 100644 --- a/src/Method/HttpCookie.php +++ b/src/Method/HttpCookie.php @@ -69,6 +69,9 @@ public function withTokenType(?string $type): self private function getAuthenticationToken(ServerRequestInterface $request): ?string { + /** + * @var string[] $cookies + */ $cookies = $request->getCookieParams(); return $cookies[$this->cookieName] ?? null; diff --git a/src/Middleware/Authentication.php b/src/Middleware/Authentication.php index 67f3d55..0db017d 100644 --- a/src/Middleware/Authentication.php +++ b/src/Middleware/Authentication.php @@ -27,7 +27,7 @@ final class Authentication implements MiddlewareInterface private RequestHandlerInterface $failureHandler; /** - * @var array Patterns to match to consider the given request URI path optional. + * @var string[] Patterns to match to consider the given request URI path optional. */ private array $optionalPatterns = []; /** @@ -62,7 +62,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface } /** - * @param array $optional Patterns to match to consider the given request URI path optional. + * @param string[] $optional Patterns to match to consider the given request URI path optional. * * @see WildcardPattern */