diff --git a/CHANGELOG.md b/CHANGELOG.md index 7111c2e..9bb027c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,12 @@ ## 3.2.2 under development +- 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) - 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) - Enh #107: Remove unnecessary files from Composer package (@mspirkov) -- Enh #113: Split `AuthenticationMethodInterface` into focused authentication and challenge interfaces (@samdark) ## 3.2.1 December 17, 2025 diff --git a/README.md b/README.md index 457c0d0..46c880a 100644 --- a/README.md +++ b/README.md @@ -133,10 +133,10 @@ $authenticationMethod = new \Yiisoft\Auth\Method\Composite([ - `\Yiisoft\Auth\IdentityWithTokenRepositoryInterface` could be additionally implemented by your application identity repository class in case token-based authentication is needed. Typically, that is `UserIdentity`. - `\Yiisoft\Auth\AuthenticatorInterface` should be implemented to provide your own authenticator. -- `\Yiisoft\Auth\ChallengeInterface` could be additionally implemented by an authenticator that needs to modify the - authentication failure response, for example, to add an HTTP authentication challenge. -- `\Yiisoft\Auth\AuthenticationMethodInterface` combines both interfaces and is deprecated. Existing implementations - remain compatible; new implementations should use the focused interfaces. +- `\Yiisoft\Auth\AuthenticatorWithChallengeInterface` could be implemented instead by an authenticator that also + needs to modify the authentication failure response, for example, to add an HTTP authentication challenge. +- `\Yiisoft\Auth\AuthenticationMethodInterface` is equivalent to `AuthenticatorWithChallengeInterface` and is + deprecated. Existing implementations remain compatible; new implementations should use the focused interfaces. ## Documentation diff --git a/src/AuthenticationMethodInterface.php b/src/AuthenticationMethodInterface.php index e8bc694..adeb27a 100644 --- a/src/AuthenticationMethodInterface.php +++ b/src/AuthenticationMethodInterface.php @@ -5,6 +5,7 @@ namespace Yiisoft\Auth; /** - * @deprecated Implement {@see AuthenticatorInterface} and, if a challenge is needed, {@see ChallengeInterface}. + * @deprecated Implement {@see AuthenticatorInterface} or, if a challenge is needed, + * {@see AuthenticatorWithChallengeInterface}. */ -interface AuthenticationMethodInterface extends AuthenticatorInterface, ChallengeInterface {} +interface AuthenticationMethodInterface extends AuthenticatorWithChallengeInterface {} diff --git a/src/AuthenticatorInterface.php b/src/AuthenticatorInterface.php index 0e02505..24b54b6 100644 --- a/src/AuthenticatorInterface.php +++ b/src/AuthenticatorInterface.php @@ -8,6 +8,9 @@ /** * Authenticates an identity using information available in a request. + * + * Implement this interface only if the authenticator does not need to add a challenge to the response upon + * authentication failure. Otherwise, implement {@see AuthenticatorWithChallengeInterface} instead. */ interface AuthenticatorInterface { diff --git a/src/AuthenticatorWithChallengeInterface.php b/src/AuthenticatorWithChallengeInterface.php new file mode 100644 index 0000000..988107b --- /dev/null +++ b/src/AuthenticatorWithChallengeInterface.php @@ -0,0 +1,23 @@ +methods as $method) { - if ($method instanceof ChallengeInterface) { + if ($method instanceof AuthenticatorWithChallengeInterface) { $response = $method->challenge($response); } } diff --git a/src/Method/HttpBasic.php b/src/Method/HttpBasic.php index 52c000d..7fa06d0 100644 --- a/src/Method/HttpBasic.php +++ b/src/Method/HttpBasic.php @@ -7,6 +7,7 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Yiisoft\Auth\AuthenticationMethodInterface; +use Yiisoft\Auth\AuthenticatorWithChallengeInterface; use Yiisoft\Auth\IdentityInterface; use Yiisoft\Auth\IdentityWithTokenRepositoryInterface; use Yiisoft\Http\Header; @@ -28,7 +29,7 @@ * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L] * ``` */ -final class HttpBasic implements AuthenticationMethodInterface +final class HttpBasic implements AuthenticationMethodInterface, AuthenticatorWithChallengeInterface { private string $realm = 'api'; private ?string $tokenType = null; diff --git a/src/Method/HttpBearer.php b/src/Method/HttpBearer.php index dac3909..6812cc8 100644 --- a/src/Method/HttpBearer.php +++ b/src/Method/HttpBearer.php @@ -5,6 +5,7 @@ namespace Yiisoft\Auth\Method; use Psr\Http\Message\ResponseInterface; +use Yiisoft\Auth\AuthenticatorWithChallengeInterface; use Yiisoft\Http\Header; /** @@ -12,7 +13,7 @@ * * @see https://tools.ietf.org/html/rfc6750 */ -final class HttpBearer extends HttpHeader +final class HttpBearer extends HttpHeader implements AuthenticatorWithChallengeInterface { protected string $headerName = Header::AUTHORIZATION; diff --git a/src/Method/HttpCookie.php b/src/Method/HttpCookie.php index 14d1a9b..494a0b4 100644 --- a/src/Method/HttpCookie.php +++ b/src/Method/HttpCookie.php @@ -7,6 +7,7 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Yiisoft\Auth\AuthenticationMethodInterface; +use Yiisoft\Auth\AuthenticatorInterface; use Yiisoft\Auth\IdentityInterface; use Yiisoft\Auth\IdentityWithTokenRepositoryInterface; @@ -15,7 +16,7 @@ * * @see https://tools.ietf.org/html/rfc6265 */ -final class HttpCookie implements AuthenticationMethodInterface +final class HttpCookie implements AuthenticationMethodInterface, AuthenticatorInterface { private string $cookieName = 'access-token'; private ?string $tokenType = null; @@ -35,6 +36,10 @@ public function authenticate(ServerRequestInterface $request): ?IdentityInterfac return $this->identityRepository->findIdentityByToken($authToken, $this->tokenType); } + /** + * @deprecated No-op kept only for compatibility with the deprecated {@see AuthenticationMethodInterface}. + * HTTP cookie authentication does not need a challenge. + */ public function challenge(ResponseInterface $response): ResponseInterface { return $response; diff --git a/src/Method/HttpHeader.php b/src/Method/HttpHeader.php index 91f7ea3..8b455fd 100644 --- a/src/Method/HttpHeader.php +++ b/src/Method/HttpHeader.php @@ -8,6 +8,7 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Yiisoft\Auth\AuthenticationMethodInterface; +use Yiisoft\Auth\AuthenticatorInterface; use Yiisoft\Auth\IdentityInterface; use Yiisoft\Auth\IdentityWithTokenRepositoryInterface; @@ -20,7 +21,7 @@ * {@see IdentityWithTokenRepositoryInterface::findIdentityByToken()} * and passes the value of the `X-Api-Key` header. This implementation is used mainly for authenticating API clients. */ -class HttpHeader implements AuthenticationMethodInterface +class HttpHeader implements AuthenticationMethodInterface, AuthenticatorInterface { protected string $headerName = 'X-Api-Key'; @@ -43,6 +44,10 @@ public function authenticate(ServerRequestInterface $request): ?IdentityInterfac return null; } + /** + * @deprecated No-op kept only for compatibility with the deprecated {@see AuthenticationMethodInterface}. + * HTTP header authentication does not need a challenge. + */ public function challenge(ResponseInterface $response): ResponseInterface { return $response; diff --git a/src/Method/QueryParameter.php b/src/Method/QueryParameter.php index ff543ad..0004cd8 100644 --- a/src/Method/QueryParameter.php +++ b/src/Method/QueryParameter.php @@ -7,6 +7,7 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Yiisoft\Auth\AuthenticationMethodInterface; +use Yiisoft\Auth\AuthenticatorInterface; use Yiisoft\Auth\IdentityInterface; use Yiisoft\Auth\IdentityWithTokenRepositoryInterface; @@ -15,7 +16,7 @@ /** * QueryParameter supports the authentication based on the access token passed through a query parameter. */ -final class QueryParameter implements AuthenticationMethodInterface +final class QueryParameter implements AuthenticationMethodInterface, AuthenticatorInterface { private string $parameterName = 'access-token'; private ?string $tokenType = null; @@ -32,6 +33,10 @@ public function authenticate(ServerRequestInterface $request): ?IdentityInterfac return null; } + /** + * @deprecated No-op kept only for compatibility with the deprecated {@see AuthenticationMethodInterface}. + * Query parameter authentication does not need a challenge. + */ public function challenge(ResponseInterface $response): ResponseInterface { return $response; diff --git a/src/Middleware/Authentication.php b/src/Middleware/Authentication.php index cbe88d7..67f3d55 100644 --- a/src/Middleware/Authentication.php +++ b/src/Middleware/Authentication.php @@ -10,7 +10,7 @@ use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; use Yiisoft\Auth\AuthenticatorInterface; -use Yiisoft\Auth\ChallengeInterface; +use Yiisoft\Auth\AuthenticatorWithChallengeInterface; use Yiisoft\Auth\Handler\AuthenticationFailureHandler; use Yiisoft\Strings\WildcardPattern; @@ -53,7 +53,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface if ($identity === null && !$this->isOptional($request)) { $response = $this->failureHandler->handle($request); - return $this->authenticationMethod instanceof ChallengeInterface + return $this->authenticationMethod instanceof AuthenticatorWithChallengeInterface ? $this->authenticationMethod->challenge($response) : $response; }