Implemented Rs256 jwt encryption algorithm

This commit is contained in:
valik
2019-07-25 16:29:08 +03:00
parent 3dbf29d34c
commit 3f9ee42539
13 changed files with 111 additions and 9 deletions

View File

@@ -9,12 +9,15 @@ use DateInterval;
use DateTime;
use Emarref\Jwt\Algorithm\AlgorithmInterface;
use Emarref\Jwt\Algorithm\Hs256;
use Emarref\Jwt\Algorithm\Rs256;
use Emarref\Jwt\Claim;
use Emarref\Jwt\Encryption\Factory as EncryptionFactory;
use Emarref\Jwt\Exception\VerificationException;
use Emarref\Jwt\HeaderParameter\Custom;
use Emarref\Jwt\Token;
use Emarref\Jwt\Verification\Context as VerificationContext;
use Exception;
use Webmozart\Assert\Assert;
use Yii;
use yii\base\InvalidConfigException;
use yii\web\UnauthorizedHttpException;
@@ -43,6 +46,10 @@ class Component extends YiiUserComponent {
public $secret;
public $publicKey;
public $privateKey;
public $expirationTimeout = 'PT1H';
public $sessionTimeout = 'P7D';
@@ -54,8 +61,16 @@ class Component extends YiiUserComponent {
public function init() {
parent::init();
if (!$this->secret) {
throw new InvalidConfigException('secret must be specified');
Assert::notEmpty($this->secret, 'secret must be specified');
Assert::notEmpty($this->publicKey, 'public key must be specified');
Assert::notEmpty($this->privateKey, 'private key must be specified');
if (!($this->publicKey = file_get_contents($this->publicKey))) {
throw new InvalidConfigException('invalid public key');
}
if (!($this->privateKey = file_get_contents($this->privateKey))) {
throw new InvalidConfigException('invalid private key');
}
}
@@ -138,7 +153,18 @@ class Component extends YiiUserComponent {
throw new VerificationException('Incorrect token encoding', 0, $e);
}
$context = new VerificationContext(EncryptionFactory::create($this->getAlgorithm()));
$algorithm = $this->getAlgorithm();
$version = $notVerifiedToken->getHeader()->findParameterByName('v');
if ($version === null) {
$algorithm = new Hs256($this->secret);
}
$encryption = EncryptionFactory::create($algorithm);
if ($version !== null) {
$encryption->setPublicKey($this->publicKey);
}
$context = new VerificationContext($encryption);
$context->setSubject(self::JWT_SUBJECT_PREFIX);
$jwt->verify($notVerifiedToken, $context);
@@ -205,15 +231,18 @@ class Component extends YiiUserComponent {
}
public function getAlgorithm(): AlgorithmInterface {
return new Hs256($this->secret);
return new Rs256();
}
protected function serializeToken(Token $token): string {
return (new Jwt())->serialize($token, EncryptionFactory::create($this->getAlgorithm()));
$encryption = EncryptionFactory::create($this->getAlgorithm())->setPrivateKey($this->privateKey);
return (new Jwt())->serialize($token, $encryption);
}
protected function createToken(Account $account): Token {
$token = new Token();
$token->addHeader(new Custom('v', 1));
foreach ($this->getClaims($account) as $claim) {
$token->addClaim($claim);
}

View File

@@ -11,6 +11,8 @@ return [
'user' => [
'class' => api\components\User\Component::class,
'secret' => getenv('JWT_USER_SECRET'),
'publicKey' => getenv('JWT_PUBLIC_KEY') ?: '/data/certs/public.crt',
'privateKey' => getenv('JWT_PRIVATE_KEY') ?: '/data/certs/private.key',
],
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,

View File

@@ -189,6 +189,8 @@ class ComponentTest extends TestCase {
'enableSession' => false,
'loginUrl' => null,
'secret' => 'secret',
'publicKey' => 'data/certs/public.crt',
'privateKey' => 'data/certs/private.key',
];
}

View File

@@ -8,6 +8,7 @@ use common\tests\_support\ProtectedCaller;
use common\tests\fixtures\AccountFixture;
use Emarref\Jwt\Claim;
use Emarref\Jwt\Encryption\Factory as EncryptionFactory;
use Emarref\Jwt\HeaderParameter\Custom;
use Emarref\Jwt\Token;
use Yii;
@@ -33,10 +34,11 @@ class JwtIdentityTest extends TestCase {
*/
public function testFindIdentityByAccessTokenWithExpiredToken() {
$token = new Token();
$token->addHeader(new Custom('v', 1));
$token->addClaim(new Claim\IssuedAt(1464593193));
$token->addClaim(new Claim\Expiration(1464596793));
$token->addClaim(new Claim\Subject('ely|' . $this->tester->grabFixture('accounts', 'admin')['id']));
$expiredToken = (new Jwt())->serialize($token, EncryptionFactory::create(Yii::$app->user->getAlgorithm()));
$expiredToken = (new Jwt())->serialize($token, EncryptionFactory::create(Yii::$app->user->getAlgorithm())->setPrivateKey(Yii::$app->user->privateKey));
JwtIdentity::findIdentityByAccessToken($expiredToken);
}

View File

@@ -63,6 +63,8 @@ class LogoutFormTest extends TestCase {
'enableSession' => false,
'loginUrl' => null,
'secret' => 'secret',
'publicKey' => 'data/certs/public.crt',
'privateKey' => 'data/certs/private.key',
];
}

View File

@@ -61,6 +61,8 @@ class ChangePasswordFormTest extends TestCase {
'enableSession' => false,
'loginUrl' => null,
'secret' => 'secret',
'publicKey' => 'data/certs/public.crt',
'privateKey' => 'data/certs/private.key',
]]);
$component->shouldNotReceive('terminateSessions');
@@ -121,6 +123,8 @@ class ChangePasswordFormTest extends TestCase {
'enableSession' => false,
'loginUrl' => null,
'secret' => 'secret',
'publicKey' => 'data/certs/public.crt',
'privateKey' => 'data/certs/private.key',
]]);
$component->shouldReceive('terminateSessions')->once()->withArgs([$account, Component::KEEP_CURRENT_SESSION]);

View File

@@ -24,6 +24,8 @@ class EnableTwoFactorAuthFormTest extends TestCase {
'enableSession' => false,
'loginUrl' => null,
'secret' => 'secret',
'publicKey' => 'data/certs/public.crt',
'privateKey' => 'data/certs/private.key',
]]);
$component->shouldReceive('terminateSessions')->withArgs([$account, Component::KEEP_CURRENT_SESSION]);