mirror of
				https://github.com/elyby/accounts.git
				synced 2025-05-31 14:11:46 +05:30 
			
		
		
		
	Implemented Rs256 jwt encryption algorithm
This commit is contained in:
		| @@ -7,6 +7,8 @@ EMAILS_RENDERER_HOST=http://emails-renderer:3000 | ||||
|  | ||||
| ## Security params | ||||
| JWT_USER_SECRET= | ||||
| JWT_PUBLIC_KEY= | ||||
| JWT_PRIVATE_KEY= | ||||
|  | ||||
| ## External services | ||||
| RECAPTCHA_PUBLIC= | ||||
|   | ||||
| @@ -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); | ||||
|         } | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -189,6 +189,8 @@ class ComponentTest extends TestCase { | ||||
|             'enableSession' => false, | ||||
|             'loginUrl' => null, | ||||
|             'secret' => 'secret', | ||||
|             'publicKey' => 'data/certs/public.crt', | ||||
|             'privateKey' => 'data/certs/private.key', | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
|   | ||||
| @@ -63,6 +63,8 @@ class LogoutFormTest extends TestCase { | ||||
|             'enableSession' => false, | ||||
|             'loginUrl' => null, | ||||
|             'secret' => 'secret', | ||||
|             'publicKey' => 'data/certs/public.crt', | ||||
|             'privateKey' => 'data/certs/private.key', | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -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]); | ||||
|  | ||||
|   | ||||
| @@ -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]); | ||||
|  | ||||
|   | ||||
| @@ -13,7 +13,11 @@ use const common\LATEST_RULES_VERSION; | ||||
| class AccountOwnerTest extends TestCase { | ||||
|  | ||||
|     public function testIdentityIsNull() { | ||||
|         $component = mock(Component::class . '[findIdentityByAccessToken]', [['secret' => 'secret']]); | ||||
|         $component = mock(Component::class . '[findIdentityByAccessToken]', [[ | ||||
|             'secret' => 'secret', | ||||
|             'publicKey' => 'data/certs/public.crt', | ||||
|             'privateKey' => 'data/certs/private.key', | ||||
|         ]]); | ||||
|         $component->shouldDeferMissing(); | ||||
|         $component->shouldReceive('findIdentityByAccessToken')->andReturn(null); | ||||
|  | ||||
| @@ -34,7 +38,11 @@ class AccountOwnerTest extends TestCase { | ||||
|         $identity = mock(IdentityInterface::class); | ||||
|         $identity->shouldReceive('getAccount')->andReturn($account); | ||||
|  | ||||
|         $component = mock(Component::class . '[findIdentityByAccessToken]', [['secret' => 'secret']]); | ||||
|         $component = mock(Component::class . '[findIdentityByAccessToken]', [[ | ||||
|             'secret' => 'secret', | ||||
|             'publicKey' => 'data/certs/public.crt', | ||||
|             'privateKey' => 'data/certs/private.key', | ||||
|         ]]); | ||||
|         $component->shouldDeferMissing(); | ||||
|         $component->shouldReceive('findIdentityByAccessToken')->withArgs(['token'])->andReturn($identity); | ||||
|  | ||||
|   | ||||
| @@ -34,7 +34,11 @@ class OauthClientOwnerTest extends TestCase { | ||||
|         $identity->shouldReceive('getAccount')->andReturn($account); | ||||
|  | ||||
|         /** @var Component|\Mockery\MockInterface $component */ | ||||
|         $component = mock(Component::class . '[findIdentityByAccessToken]', [['secret' => 'secret']]); | ||||
|         $component = mock(Component::class . '[findIdentityByAccessToken]', [[ | ||||
|             'secret' => 'secret', | ||||
|             'publicKey' => 'data/certs/public.crt', | ||||
|             'privateKey' => 'data/certs/private.key', | ||||
|         ]]); | ||||
|         $component->shouldDeferMissing(); | ||||
|         $component->shouldReceive('findIdentityByAccessToken')->withArgs(['token'])->andReturn($identity); | ||||
|  | ||||
|   | ||||
							
								
								
									
										1
									
								
								data/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								data/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,2 +1,3 @@ | ||||
| * | ||||
| !certs/* | ||||
| !.gitignore | ||||
|   | ||||
							
								
								
									
										28
									
								
								data/certs/private.key
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								data/certs/private.key
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| -----BEGIN PRIVATE KEY----- | ||||
| MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDbTqmRpLg3XjDH | ||||
| 3Z97uHdNq4F5j77Rp+M7ctyfUhtb+U7VWppjk2Dxyp2/iPzKK3K0lC91zlnxO4HT | ||||
| jFCWTIQzSfiFx/Z6nbUXYFZunzRkbt6UgXjUhnYLSIVvNDneph/BZTSxNThky7a8 | ||||
| weng1+1e7cYcYx7pJWUXB9XINEKdyZ/pF+kB8UPK/LCLY4jFTm7t+N1Rm1R6VpEy | ||||
| VqhwoDTefkiP9H/QZBp4Ihy48v/NTtgHdsc3Yz+//M6km39MmxIh4wBrZiIictzg | ||||
| 5Xmd1vXamDYFbGZHpKRuujCSufZaglrjGvgaAq1lSS+Cwc5eNCDTlw8OWGJyeSMy | ||||
| AvYKK5pnAgMBAAECggEBAKcg02kCtsC7L0GhS6Dle0XdpdYWDb2IzErJxghEckUt | ||||
| QT6mxXGNJxwc5QrKQptvcQLcyy5kC3cjelTVYbSoqzbK8HJDaTsYZKFj8XpsKWlA | ||||
| dK+H26Vasyr2IXoVuuRKhXjEv9ssS8XE2YYP4URQSb1GRuvrPes/bEKY3fqsmPfU | ||||
| /rpaUNG9OvskfIDzT+VoEe5RfPW0+uchHZHypWdnhSxLC/oH8KjcUxmCdQ3q46fT | ||||
| 2GhDJnDLXC8MGbyUp7Nw+eSg+4UTCjaNqV7c4vOSXqSBPch7nYFf1YqYuseok21t | ||||
| UK1G55JrBfsUAmldSi1UVdnAanVRNZiC2LsdDe9PpUECgYEA7kVk7nFqtHqx6EOz | ||||
| 4p6AeDlslrPEWz996AgV1qezBboGlpPkDv+of5cOG4ZMpDJD5KbSIJXTPC06G+3V | ||||
| VgYpg7cYO9il3I5vaxo64dC9Ib5HQe8UTreVI5763S7Zq7V0jWKOzrlKzA/KQl3x | ||||
| 1kHXS5levDp1uuwAdRBn6DvXnv0CgYEA66ALVI1BUU+OhqSGRQu9pZATfyB5hJaD | ||||
| 1iICiOgl1LRwMJW7/uWUTQ+h5H3lYDmyf+y9/8x8jTfEVZYEwV2bw9wzII87YA9R | ||||
| pKQl+HMlynrgYWZ2Z94mRFs3poxU8AgpU9MDN84b2cHyP3TGhQjkdtdyFE4lcCiQ | ||||
| yQqnWa+BBjMCgYEArKeKQKHcoVT7D4PnmIIkM3ng7r7qvPggAv/A219/gNnQplIa | ||||
| AqhM78+EgHtrk9t8iPY88zG99DANmGlZmlEyyefl3o/ZeB2aLPC/1BvOwOHBfsyA | ||||
| WZ37qukrfRTS0/LTtxPAyZlI0t9qP3cVo5zoJjbHh/uQjdcvaaRutsCOOP0CgYEA | ||||
| 10TB9T6UdVgM6+A2N7CxVCicV2HxA3yL+D/cNv55SaqMcSbrucY/xmPI0btfq5kr | ||||
| BorhT2mgRVi0zEiiEZOXMsrj/xQ899cnDRdXBXUWCrZWd0YoWV7xcTQxVL0TALVE | ||||
| JKw9XWe1tC3oR6dFk9d6+0R8miaHN8An/zT3jg21AFcCgYEAslWiTkT1ULAAhlHa | ||||
| KLbSW1slYJR8/i9mwIDOoD2BvVJUSqbowAogD4mXRm6S77AxoQX4nygzE6XscR4V | ||||
| h+fINRJeh7yrFk5x/GUjh7tQo9EITjY89X0s35hZ27i61l66eZ5u06j4xE5+Y424 | ||||
| HMsBjKAmKFNPebTWFcAlXXaeCPU= | ||||
| -----END PRIVATE KEY----- | ||||
							
								
								
									
										16
									
								
								data/certs/public.crt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								data/certs/public.crt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| -----BEGIN CERTIFICATE----- | ||||
| MIICljCCAX4CCQDA6sdDyK1Y/zANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJC | ||||
| WTAeFw0xOTA3MjQxMDI5NTdaFw0yMTA3MjMxMDI5NTdaMA0xCzAJBgNVBAYTAkJZ | ||||
| MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA206pkaS4N14wx92fe7h3 | ||||
| TauBeY++0afjO3Lcn1IbW/lO1VqaY5Ng8cqdv4j8yitytJQvdc5Z8TuB04xQlkyE | ||||
| M0n4hcf2ep21F2BWbp80ZG7elIF41IZ2C0iFbzQ53qYfwWU0sTU4ZMu2vMHp4Nft | ||||
| Xu3GHGMe6SVlFwfVyDRCncmf6RfpAfFDyvywi2OIxU5u7fjdUZtUelaRMlaocKA0 | ||||
| 3n5Ij/R/0GQaeCIcuPL/zU7YB3bHN2M/v/zOpJt/TJsSIeMAa2YiInLc4OV5ndb1 | ||||
| 2pg2BWxmR6Skbrowkrn2WoJa4xr4GgKtZUkvgsHOXjQg05cPDlhicnkjMgL2Ciua | ||||
| ZwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQB+i6Q3Ltg5MPEqHZ3GCpsFMV+xWKp5 | ||||
| TSgguFr422az9v/Da01VHOX884D0dZt1r6W+zzfIQzIXpRqQkl4YuS1N17Q/KN3E | ||||
| 7rJ0R7gsXM7+KiGVrZyoZlxRaRXCiErUWBOxamIPy07zOWLnWa1kZZNDvgiurMbF | ||||
| yaREQargFM8G91zkA6XiMXFoermARYB6RLtyHD0EC3I2DSZpOuMD9Kg1k/uw6f3W | ||||
| xwsQY6kpzoZkYfTqoM4ky16yNPRf9vsej2dYlRr1YPWWQOicY1TrwFJMKoogylTD | ||||
| lN61u8WED7Z8M00F6FYuuFffzt2Si9GrYeTuf8ZShpKiDqK0P22oiAao | ||||
| -----END CERTIFICATE----- | ||||
		Reference in New Issue
	
	Block a user