Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring #38

Merged
merged 15 commits into from Feb 10, 2021
30 changes: 20 additions & 10 deletions README.md
@@ -1,6 +1,6 @@
<p align="center">
<a href="https://github.com/yiisoft" target="_blank">
<img src="https://github.com/yiisoft.png" height="100px">
<img src="https://yiisoft.github.io/docs/images/yii_logo.svg" height="100px">
</a>
<h1 align="center">Yii Auth JWT</h1>
<br>
Expand Down Expand Up @@ -33,10 +33,19 @@ composer require yiisoft/auth-jwt --prefer-dist

### Configuring within Yii

1. Set JWT secret in your `params.php` config file:
1. Set JWT parameters in your `params.php` config file:
```php
'yiisoft/auth-jwt' => [
'secret' => 'your-secret'
'algorithms' => [
// your signature algorithms
],
'serializers' => [
// your token serializers
],
'key' => [
'secret' => 'your-secret',
'file' => 'your-certificate-file',
],
],
```
2. Setup definitions, required for `\Yiisoft\Auth\Middleware\Authentication` middleware in a config, for example,
Expand All @@ -54,10 +63,10 @@ composer require yiisoft/auth-jwt --prefer-dist
use Yiisoft\Auth\Jwt\JwtMethod;

return [
TokenManagerInterface::class => [
'__class' => TokenManager::class,
KeyFactoryInterface::class => [
'__class' => FromSecret::class,
'__construct()' => [
'secret' => $params['yiisoft/auth-jwt']['secret']
$params['yiisoft/auth-jwt']['key']['secret']
],
],

Expand All @@ -77,9 +86,9 @@ You can configure `Authentication` middleware manually:
/** @var \Yiisoft\Auth\IdentityRepositoryInterface $identityRepository */
$identityRepository = getIdentityRepository();

$tokenManager = $container->get(\Yiisoft\Auth\Jwt\TokenManagerInterface::class);
$tokenRepository = $container->get(\Yiisoft\Auth\Jwt\TokenRepositoryInterface::class);

$authenticationMethod = new \Yiisoft\Auth\Jwt\JwtMethod($identityRepository, $tokenManager);
$authenticationMethod = new \Yiisoft\Auth\Jwt\JwtMethod($identityRepository, $tokenRepository);

$middleware = new \Yiisoft\Auth\Middleware\Authentication(
$authenticationMethod,
Expand All @@ -98,10 +107,11 @@ The package is tested with [PHPUnit](https://phpunit.de/). To run tests:

## Mutation testing

The package tests are checked with [Infection](https://infection.github.io/) mutation framework. To run it:
The package tests are checked with [Infection](https://infection.github.io/) mutation framework with
[Infection Static Analysis Plugin](https://github.com/Roave/infection-static-analysis-plugin). To run it:

```shell
./vendor/bin/infection
./vendor/bin/roave-infection-static-analysis-plugin
```

## Static analysis
Expand Down
32 changes: 28 additions & 4 deletions config/common.php
Expand Up @@ -2,14 +2,38 @@

declare(strict_types=1);

use Yiisoft\Auth\Jwt\TokenManager;
use Yiisoft\Auth\Jwt\TokenManagerInterface;
use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Signature\Serializer\JWSSerializerManager;
use Yiisoft\Auth\Jwt\KeyFactory\FromSecret;
use Yiisoft\Auth\Jwt\KeyFactoryInterface;
use Yiisoft\Auth\Jwt\TokenFactory;
use Yiisoft\Auth\Jwt\TokenFactoryInterface;
use Yiisoft\Auth\Jwt\TokenRepository;
use Yiisoft\Auth\Jwt\TokenRepositoryInterface;
use Yiisoft\Injector\Injector;

/**
* @var $params array
*/
return [
TokenManagerInterface::class => static function () use ($params) {
return new TokenManager($params['yiisoft/auth-jwt']['secret']);
KeyFactoryInterface::class => [
'__class' => FromSecret::class,
'__construct()' => [$params['yiisoft/auth-jwt']['key']['secret']],
],
AlgorithmManager::class => static function (Injector $injector) use ($params) {
$algorithms = array_map(
static fn ($algorithm) => is_string($algorithm) ? $injector->make($algorithm) : $algorithm,
$params['yiisoft/auth-jwt']['algorithms'] ?? []
);
return $injector->make(AlgorithmManager::class, ['algorithms' => $algorithms]);
},
JWSSerializerManager::class => static function (Injector $injector) use ($params) {
$serializers = array_map(
static fn ($serializer) => is_string($serializer) ? $injector->make($serializer) : $serializer,
$params['yiisoft/auth-jwt']['serializers'] ?? []
);
return $injector->make(JWSSerializerManager::class, ['serializers' => $serializers]);
},
TokenFactoryInterface::class => TokenFactory::class,
TokenRepositoryInterface::class => TokenRepository::class,
];
15 changes: 14 additions & 1 deletion config/params.php
Expand Up @@ -2,8 +2,21 @@

declare(strict_types=1);

use Jose\Component\Signature\Algorithm\HS256;
use Jose\Component\Signature\Serializer\CompactSerializer;

return [
'yiisoft/auth-jwt' => [
'secret' => '',
'algorithms' => [
HS256::class,
],
'serializers' => [
CompactSerializer::class,
],
'key' => [
'secret' => '',
'file' => '',
'password' => '',
],
],
];
10 changes: 5 additions & 5 deletions src/JwtMethod.php
Expand Up @@ -36,20 +36,20 @@ final class JwtMethod implements AuthenticationMethodInterface
private array $claimCheckers;

private IdentityRepositoryInterface $identityRepository;
private TokenManagerInterface $tokenManager;
private TokenRepositoryInterface $tokenRepository;

/**
* @param IdentityRepositoryInterface $identityRepository Repository to get identity from.
* @param TokenManagerInterface $tokenManager Token manager to obtain claims from.
* @param TokenRepositoryInterface $tokenRepository Token manager to obtain claims from.
* @param ClaimChecker[]|null $claimCheckers Claim checkers. If not specified, {@see ExpirationTimeChecker} is used.
*/
public function __construct(
IdentityRepositoryInterface $identityRepository,
TokenManagerInterface $tokenManager,
TokenRepositoryInterface $tokenRepository,
?array $claimCheckers = null
) {
$this->identityRepository = $identityRepository;
$this->tokenManager = $tokenManager;
$this->tokenRepository = $tokenRepository;
$this->claimCheckers = $claimCheckers ?? [new ExpirationTimeChecker()];
}

Expand All @@ -60,7 +60,7 @@ public function authenticate(ServerRequestInterface $request): ?IdentityInterfac
return null;
}

$claims = $this->tokenManager->getClaims($token);
$claims = $this->tokenRepository->getClaims($token, $name);
if ($claims === null || !isset($claims[$this->identifier])) {
return null;
}
Expand Down
29 changes: 29 additions & 0 deletions src/KeyFactory/FromCertificateFile.php
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Auth\Jwt\KeyFactory;

use Jose\Component\Core\JWK;
use Jose\Component\KeyManagement\JWKFactory;
use Yiisoft\Auth\Jwt\KeyFactoryInterface;

/**
* Creates JWK from a certificate file.
*
* @codeCoverageIgnore
*/
final class FromCertificateFile implements KeyFactoryInterface
{
private string $file;

public function __construct(string $file)
{
$this->file = $file;
}

public function create(array $additionalValues = []): JWK
{
return JWKFactory::createFromCertificateFile($this->file, $additionalValues);
}
}
31 changes: 31 additions & 0 deletions src/KeyFactory/FromKeyFile.php
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Auth\Jwt\KeyFactory;

use Jose\Component\Core\JWK;
use Jose\Component\KeyManagement\JWKFactory;
use Yiisoft\Auth\Jwt\KeyFactoryInterface;

/**
* Creates JWK from a password-protected key file.
*
* @codeCoverageIgnore
*/
final class FromKeyFile implements KeyFactoryInterface
{
private string $file;
private string $password;

public function __construct(string $file, string $password)
{
$this->file = $file;
$this->password = $password;
}

public function create(array $additionalValues = []): JWK
{
return JWKFactory::createFromKeyFile($this->file, $this->password, $additionalValues);
}
}
31 changes: 31 additions & 0 deletions src/KeyFactory/FromPKCS12CertificateFile.php
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Auth\Jwt\KeyFactory;

use Jose\Component\Core\JWK;
use Jose\Component\KeyManagement\JWKFactory;
use Yiisoft\Auth\Jwt\KeyFactoryInterface;

/**
* Creates JWK from a PKCS12 certificate file.
*
* @codeCoverageIgnore
*/
final class FromPKCS12CertificateFile implements KeyFactoryInterface
{
private string $file;
private string $secret;

public function __construct(string $file, string $secret)
{
$this->file = $file;
$this->secret = $secret;
}

public function create(array $additionalValues = []): JWK
{
return JWKFactory::createFromPKCS12CertificateFile($this->file, $this->secret, $additionalValues);
}
}
27 changes: 27 additions & 0 deletions src/KeyFactory/FromSecret.php
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Auth\Jwt\KeyFactory;

use Jose\Component\Core\JWK;
use Jose\Component\KeyManagement\JWKFactory;
use Yiisoft\Auth\Jwt\KeyFactoryInterface;

/**
* Creates JWK from a secret.
*/
final class FromSecret implements KeyFactoryInterface
{
private string $secret;

public function __construct(string $secret)
{
$this->secret = $secret;
}

public function create(array $additionalValues = []): JWK
{
return JWKFactory::createFromSecret($this->secret, $additionalValues);
}
}
24 changes: 24 additions & 0 deletions src/KeyFactoryInterface.php
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Auth\Jwt;

use Jose\Component\Core\JWK;

/**
* Key factory creates a JSON Web Key.
*
* @see https://tools.ietf.org/html/rfc7517
*/
interface KeyFactoryInterface
{
/**
* Create a key with additional values.
*
* @param array $additionalValues
*
* @return JWK JSON Web Key.
*/
public function create(array $additionalValues = []): JWK;
}
60 changes: 60 additions & 0 deletions src/TokenFactory.php
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Auth\Jwt;

use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Signature\JWSBuilder;
use Jose\Component\Signature\Serializer\JWSSerializerManager;
use Yiisoft\Json\Json;

/**
* Token factory creates a token signed with JSON Web Signature.
*/
final class TokenFactory implements TokenFactoryInterface
{
private KeyFactoryInterface $keyFactory;

/**
* @param AlgorithmManager $algorithmManager Algorithms manager for signing JSON Web Signature.
*
* @see https://tools.ietf.org/html/rfc7515
*/
private AlgorithmManager $algorithmManager;

/**
* @param JWSSerializerManager $serializerManager JSON Web Signature serializer manager.
*
* @see https://tools.ietf.org/html/rfc7515
*/
private JWSSerializerManager $serializerManager;

/**
* @param KeyFactoryInterface $keyFactory A factory to create a JSON Web Key.
* @param AlgorithmManager $algorithmManager Algorithms manager for signing JSON Web Signature.
* @param JWSSerializerManager $serializerManager JSON Web Signature serializer manager.
*/
public function __construct(
KeyFactoryInterface $keyFactory,
AlgorithmManager $algorithmManager,
JWSSerializerManager $serializerManager
) {
$this->keyFactory = $keyFactory;
$this->algorithmManager = $algorithmManager;
$this->serializerManager = $serializerManager;
}

public function create(array $payload, string $format, ?int $signatureIndex = null): string
{
$jwsBuilder = new JWSBuilder($this->algorithmManager);
$jws = $jwsBuilder->create()->withPayload(Json::encode($payload));
$jwk = $this->keyFactory->create();

foreach ($this->algorithmManager->list() as $algorithm) {
$jws = $jws->addSignature($jwk, ['alg' => $algorithm]);
}

return $this->serializerManager->serialize($format, $jws->build(), $signatureIndex);
}
}