Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- 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: Segregate the methods of the AuthenticationMethodInterface (@klsoft-web)

## 3.2.1 December 17, 2025

Expand Down
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ Configure a middleware and add it to your middleware stack:

```php
$identityRepository = getIdentityWithTokenRepository(); // \Yiisoft\Auth\IdentityRepositoryInterface
$authenticationMethod = new \Yiisoft\Auth\Method\HttpBasic($identityRepository);
$authenticator = new \Yiisoft\Auth\Method\HttpBasic($identityRepository);
$failureHandler = new \Yiisoft\Auth\Handler\AuthenticationFailureHandler($responseFactory);

$middleware = new \Yiisoft\Auth\Middleware\Authentication(
$authenticationMethod,
$responseFactory, // PSR-17 ResponseFactoryInterface
$failureHandler // optional, \Yiisoft\Auth\Handler\AuthenticationFailureHandler by default
$authenticator,
$failureHandler
);

$middlewareDispatcher->addMiddleware($middleware);
Expand All @@ -47,7 +47,7 @@ $middlewareDispatcher->addMiddleware($middleware);
In order to get an identity instance in the following middleware use `getAttribute()` method of the request instance:

```php
public function actionIndex(\Psr\Http\Message\ServerRequestInterface $request): \Psr\Http\Message\ResponseInterface
public function index(\Psr\Http\Message\ServerRequestInterface $request): \Psr\Http\Message\ResponseInterface
{
$identity = $request->getAttribute(\Yiisoft\Auth\Middleware\Authentication::class);
// ...
Expand All @@ -60,7 +60,7 @@ Basic HTTP authentication is typically used for entering login and password in t
Credentials are passed as `$_SERVER['PHP_AUTH_USER']` and `$_SERVER['PHP_AUTH_PW']`.

```php
$authenticationMethod = (new \Yiisoft\Auth\Method\HttpBasic($identityRepository))
$authenticator = (new \Yiisoft\Auth\Method\HttpBasic($identityRepository))
->withRealm('Admin')
->withAuthenticationCallback(static function (
?string $username,
Expand All @@ -79,15 +79,15 @@ Custom authentication callback set in the above is the same as default behavior
Bearer HTTP authentication is typically used in APIs. Authentication token is passed in `WWW-Authenticate` header.

```php
$authenticationMethod = new \Yiisoft\Auth\Method\HttpBearer($identityRepository);
$authenticator = new \Yiisoft\Auth\Method\HttpBearer($identityRepository);
```

### Custom HTTP header authentication

Custom HTTP header could be used if you do not want to leverage bearer token authentication:

```php
$authenticationMethod = (new \Yiisoft\Auth\Method\HttpHeader($identityRepository))
$authenticator = (new \Yiisoft\Auth\Method\HttpHeader($identityRepository))
->withHeaderName('X-Api-Key')
->withPattern('/(.*)/'); // default
```
Expand All @@ -100,14 +100,14 @@ This authentication method is mainly used by clients unable to send headers. In
we advise not to use it.

```php
$authenticationMethod = (new \Yiisoft\Auth\Method\QueryParameter($identityRepository))
$authenticator = (new \Yiisoft\Auth\Method\QueryParameter($identityRepository))
->withParameterName('token');
```

### HTTP cookie authentication

```php
$authenticationMethod = (new \Yiisoft\Auth\Method\HttpCookie($identityRepository))
$authenticator = (new \Yiisoft\Auth\Method\HttpCookie($identityRepository))
->withCookieName('access-token');
```

Expand All @@ -118,7 +118,7 @@ Typical authentication for websites by storing a token in a browser cookie.
To use multiple authentication methods, use `Yiisoft\Auth\Method\Composite`:

```php
$authenticationMethod = new \Yiisoft\Auth\Method\Composite([
$authenticator = new \Yiisoft\Auth\Method\Composite([
$bearerAuthenticationMethod,
$basicAuthenticationMethod
]);
Expand Down
25 changes: 2 additions & 23 deletions src/AuthenticationMethodInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,9 @@

namespace Yiisoft\Auth;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

/**
* The interface that should be implemented by individual authentication methods.
* @deprecated Use AuthenticatorInterface and optionally ChallengeInterface.
*/
interface AuthenticationMethodInterface
interface AuthenticationMethodInterface extends AuthenticatorInterface, ChallengeInterface
{
/**
* Authenticates the identity based on information available from request.
*
* @param ServerRequestInterface $request Request to get identity information from.
*
* @return IdentityInterface|null An instance of identity or null if there is no match.
*/
public function authenticate(ServerRequestInterface $request): ?IdentityInterface;

/**
* Adds challenge to response upon authentication failure.
* For example, some appropriate HTTP headers may be added.
*
* @param ResponseInterface $response Response to modify.
*
* @return ResponseInterface Modified response.
*/
public function challenge(ResponseInterface $response): ResponseInterface;
}
22 changes: 22 additions & 0 deletions src/AuthenticatorInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Auth;

use Psr\Http\Message\ServerRequestInterface;

/**
* The interface that should be implemented by individual authentication methods.
*/
interface AuthenticatorInterface
{
/**
* Authenticates the identity based on information available from request.
*
* @param ServerRequestInterface $request Request to get identity information from.
*
* @return IdentityInterface|null An instance of identity or null if there is no match.
*/
public function authenticate(ServerRequestInterface $request): ?IdentityInterface;
}
23 changes: 23 additions & 0 deletions src/ChallengeInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Auth;

use Psr\Http\Message\ResponseInterface;

/**
* The interface that should be implemented by response upon authentication failure.
*/
interface ChallengeInterface
{
/**
* Adds challenge to response upon authentication failure.
* For example, some appropriate HTTP headers may be added.
*
* @param ResponseInterface $response Response to modify.
*
* @return ResponseInterface Modified response.
*/
public function challenge(ResponseInterface $response): ResponseInterface;
}
15 changes: 9 additions & 6 deletions src/Method/Composite.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Yiisoft\Auth\AuthenticationMethodInterface;
use Yiisoft\Auth\AuthenticatorInterface;
use Yiisoft\Auth\ChallengeInterface;
use Yiisoft\Auth\IdentityInterface;
use RuntimeException;

/**
* Composite allows multiple authentication methods at the same time.
*/
final class Composite implements AuthenticationMethodInterface
final class Composite implements AuthenticatorInterface, ChallengeInterface

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be kept for this release.

{
/**
* @param AuthenticationMethodInterface[] $methods
* @param AuthenticatorInterface[] $methods
*/
public function __construct(
private readonly array $methods,
Expand All @@ -25,8 +26,8 @@
public function authenticate(ServerRequestInterface $request): ?IdentityInterface
{
foreach ($this->methods as $method) {
if (!$method instanceof AuthenticationMethodInterface) {
throw new RuntimeException('Authentication method must be an instance of ' . AuthenticationMethodInterface::class . '.');
if (!$method instanceof AuthenticatorInterface) {
throw new RuntimeException('Authentication method must be an instance of ' . AuthenticatorInterface::class . '.');

Check warning on line 30 in src/Method/Composite.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "ConcatOperandRemoval": @@ @@ { foreach ($this->methods as $method) { if (!$method instanceof AuthenticatorInterface) { - throw new RuntimeException('Authentication method must be an instance of ' . AuthenticatorInterface::class . '.'); + throw new RuntimeException('Authentication method must be an instance of ' . AuthenticatorInterface::class); } $identity = $method->authenticate($request);

Check warning on line 30 in src/Method/Composite.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "Concat": @@ @@ { foreach ($this->methods as $method) { if (!$method instanceof AuthenticatorInterface) { - throw new RuntimeException('Authentication method must be an instance of ' . AuthenticatorInterface::class . '.'); + throw new RuntimeException('Authentication method must be an instance of ' . '.' . AuthenticatorInterface::class); } $identity = $method->authenticate($request);

Check warning on line 30 in src/Method/Composite.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "ConcatOperandRemoval": @@ @@ { foreach ($this->methods as $method) { if (!$method instanceof AuthenticatorInterface) { - throw new RuntimeException('Authentication method must be an instance of ' . AuthenticatorInterface::class . '.'); + throw new RuntimeException('Authentication method must be an instance of ' . '.'); } $identity = $method->authenticate($request);

Check warning on line 30 in src/Method/Composite.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "ConcatOperandRemoval": @@ @@ { foreach ($this->methods as $method) { if (!$method instanceof AuthenticatorInterface) { - throw new RuntimeException('Authentication method must be an instance of ' . AuthenticatorInterface::class . '.'); + throw new RuntimeException(AuthenticatorInterface::class . '.'); } $identity = $method->authenticate($request);

Check warning on line 30 in src/Method/Composite.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "Concat": @@ @@ { foreach ($this->methods as $method) { if (!$method instanceof AuthenticatorInterface) { - throw new RuntimeException('Authentication method must be an instance of ' . AuthenticatorInterface::class . '.'); + throw new RuntimeException(AuthenticatorInterface::class . 'Authentication method must be an instance of ' . '.'); } $identity = $method->authenticate($request);
}

$identity = $method->authenticate($request);
Expand All @@ -41,7 +42,9 @@
public function challenge(ResponseInterface $response): ResponseInterface
{
foreach ($this->methods as $method) {
$response = $method->challenge($response);
if($method instanceof ChallengeInterface) {
$response = $method->challenge($response);
}
}
return $response;
}
Expand Down
5 changes: 3 additions & 2 deletions src/Method/HttpBasic.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Yiisoft\Auth\AuthenticationMethodInterface;
use Yiisoft\Auth\AuthenticatorInterface;
use Yiisoft\Auth\ChallengeInterface;
use Yiisoft\Auth\IdentityInterface;
use Yiisoft\Auth\IdentityWithTokenRepositoryInterface;
use Yiisoft\Http\Header;
Expand All @@ -28,7 +29,7 @@
* RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
* ```
*/
final class HttpBasic implements AuthenticationMethodInterface
final class HttpBasic implements AuthenticatorInterface, ChallengeInterface

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That should be capped for this release.

{
private string $realm = 'api';
private ?string $tokenType = null;
Expand Down Expand Up @@ -162,7 +163,7 @@
{
return array_map(
static fn($value) => $value === '' ? null : $value,
explode(':', base64_decode(substr($authToken, 6)), 2),

Check warning on line 166 in src/Method/HttpBasic.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "DecrementInteger": @@ @@ { return array_map( static fn($value) => $value === '' ? null : $value, - explode(':', base64_decode(substr($authToken, 6)), 2), + explode(':', base64_decode(substr($authToken, 5)), 2), ); }
);
}

Expand Down
3 changes: 2 additions & 1 deletion src/Method/HttpBearer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
namespace Yiisoft\Auth\Method;

use Psr\Http\Message\ResponseInterface;
use Yiisoft\Auth\ChallengeInterface;
use Yiisoft\Http\Header;

/**
* Authentication method based on HTTP Bearer token.
*
* @see https://tools.ietf.org/html/rfc6750
*/
final class HttpBearer extends HttpHeader
final class HttpBearer extends HttpHeader implements ChallengeInterface
{
protected string $headerName = Header::AUTHORIZATION;

Expand Down
10 changes: 2 additions & 8 deletions src/Method/HttpCookie.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@

namespace Yiisoft\Auth\Method;

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;

Expand All @@ -15,7 +14,7 @@
*
* @see https://tools.ietf.org/html/rfc6265
*/
final class HttpCookie implements AuthenticationMethodInterface
final class HttpCookie implements AuthenticatorInterface

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be kept for this release.

{
private string $cookieName = 'access-token';
private ?string $tokenType = null;
Expand All @@ -35,11 +34,6 @@ public function authenticate(ServerRequestInterface $request): ?IdentityInterfac
return $this->identityRepository->findIdentityByToken($authToken, $this->tokenType);
}

public function challenge(ResponseInterface $response): ResponseInterface
{
return $response;
}

/**
* @psalm-immutable
*/
Expand Down
10 changes: 2 additions & 8 deletions src/Method/HttpHeader.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
namespace Yiisoft\Auth\Method;

use JetBrains\PhpStorm\Language;
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;

Expand All @@ -20,7 +19,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 AuthenticatorInterface
{
protected string $headerName = 'X-Api-Key';

Expand All @@ -43,11 +42,6 @@ public function authenticate(ServerRequestInterface $request): ?IdentityInterfac
return null;
}

public function challenge(ResponseInterface $response): ResponseInterface
{
return $response;
}

/**
* @param string $name The HTTP header name.
*
Expand Down
10 changes: 2 additions & 8 deletions src/Method/QueryParameter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@

namespace Yiisoft\Auth\Method;

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;

Expand All @@ -15,7 +14,7 @@
/**
* QueryParameter supports the authentication based on the access token passed through a query parameter.
*/
final class QueryParameter implements AuthenticationMethodInterface
final class QueryParameter implements AuthenticatorInterface
{
private string $parameterName = 'access-token';
private ?string $tokenType = null;
Expand All @@ -32,11 +31,6 @@ public function authenticate(ServerRequestInterface $request): ?IdentityInterfac
return null;
}

public function challenge(ResponseInterface $response): ResponseInterface
{
return $response;
}

/**
* @param string $name The parameter name for passing the access token.
*
Expand Down
Loading
Loading