mirror of
https://github.com/elyby/accounts.git
synced 2025-05-31 14:11:46 +05:30
Introduce revokation mechanism
This commit is contained in:
@@ -36,7 +36,7 @@ class Component extends BaseComponent {
|
||||
public $privateKeyPass;
|
||||
|
||||
/**
|
||||
* @var string|\Defuse\Crypto\Key
|
||||
* @var string
|
||||
*/
|
||||
public $encryptionKey;
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ namespace api\components\User;
|
||||
|
||||
use common\models\Account;
|
||||
use common\models\AccountSession;
|
||||
use common\models\OauthClient;
|
||||
use Webmozart\Assert\Assert;
|
||||
use yii\web\User as YiiUserComponent;
|
||||
|
||||
/**
|
||||
@@ -78,6 +80,15 @@ class Component extends YiiUserComponent {
|
||||
}
|
||||
|
||||
if (!($mode & self::KEEP_MINECRAFT_SESSIONS)) {
|
||||
/** @var \common\models\OauthSession|null $minecraftSession */
|
||||
$minecraftSession = $account->getSessions()
|
||||
->andWhere(['client_id' => OauthClient::UNAUTHORIZED_MINECRAFT_GAME_LAUNCHER])
|
||||
->one();
|
||||
if ($minecraftSession !== null) {
|
||||
$minecraftSession->revoked_at = time();
|
||||
Assert::true($minecraftSession->save());
|
||||
}
|
||||
|
||||
foreach ($account->minecraftAccessKeys as $minecraftAccessKey) {
|
||||
$minecraftAccessKey->delete();
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace api\components\User;
|
||||
use api\components\Tokens\TokenReader;
|
||||
use Carbon\Carbon;
|
||||
use common\models\Account;
|
||||
use common\models\OauthClient;
|
||||
use common\models\OauthSession;
|
||||
use Exception;
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\ValidationData;
|
||||
@@ -50,9 +52,25 @@ class JwtIdentity implements IdentityInterface {
|
||||
throw new UnauthorizedHttpException('Incorrect token');
|
||||
}
|
||||
|
||||
$tokenReader = new TokenReader($token);
|
||||
$accountId = $tokenReader->getAccountId();
|
||||
$iat = $token->getClaim('iat');
|
||||
if ($tokenReader->getMinecraftClientToken() !== null && self::isRevoked($accountId, OauthClient::UNAUTHORIZED_MINECRAFT_GAME_LAUNCHER, $iat)) {
|
||||
throw new UnauthorizedHttpException('Token has been revoked');
|
||||
}
|
||||
|
||||
if ($tokenReader->getClientId() !== null && self::isRevoked($accountId, $tokenReader->getClientId(), $iat)) {
|
||||
throw new UnauthorizedHttpException('Token has been revoked');
|
||||
}
|
||||
|
||||
return new self($token);
|
||||
}
|
||||
|
||||
private static function isRevoked(int $accountId, string $clientId, int $iat): bool {
|
||||
$session = OauthSession::findOne(['account_id' => $accountId, 'client_id' => $clientId]);
|
||||
return $session !== null && $session->revoked_at !== null && $session->revoked_at > $iat;
|
||||
}
|
||||
|
||||
public function getToken(): Token {
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ class AuthenticationForm extends ApiForm {
|
||||
$account = $loginForm->getAccount();
|
||||
$token = Yii::$app->tokensFactory->createForMinecraftAccount($account, $this->clientToken);
|
||||
$dataModel = new AuthenticateData($account, (string)$token, $this->clientToken);
|
||||
// TODO: issue session in the oauth_sessions
|
||||
|
||||
Authserver::info("User with id = {$account->id}, username = '{$account->username}' and email = '{$account->email}' successfully logged in.");
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ class ComponentTest extends TestCase {
|
||||
$component->terminateSessions($account, Component::KEEP_SITE_SESSIONS);
|
||||
$this->assertEmpty($account->getMinecraftAccessKeys()->all());
|
||||
$this->assertNotEmpty($account->getSessions()->all());
|
||||
// TODO: write test about invalidating new minecraft access tokens based on JWT
|
||||
|
||||
// All sessions should be removed except the current one
|
||||
$component->terminateSessions($account, Component::KEEP_CURRENT_SESSION);
|
||||
|
||||
@@ -7,6 +7,8 @@ use api\components\User\JwtIdentity;
|
||||
use api\tests\unit\TestCase;
|
||||
use Carbon\Carbon;
|
||||
use common\tests\fixtures\AccountFixture;
|
||||
use common\tests\fixtures\OauthClientFixture;
|
||||
use common\tests\fixtures\OauthSessionFixture;
|
||||
use yii\web\UnauthorizedHttpException;
|
||||
|
||||
class JwtIdentityTest extends TestCase {
|
||||
@@ -14,6 +16,8 @@ class JwtIdentityTest extends TestCase {
|
||||
public function _fixtures(): array {
|
||||
return [
|
||||
'accounts' => AccountFixture::class,
|
||||
'oauthClients' => OauthClientFixture::class,
|
||||
'oauthSessions' => OauthSessionFixture::class,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -46,6 +50,14 @@ class JwtIdentityTest extends TestCase {
|
||||
'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJlbHktc2NvcGVzIjoiYWNjb3VudHNfd2ViX3VzZXIiLCJpYXQiOjE1NjQ2MTc3NDIsImV4cCI6MTU2NDYxNDE0Miwic3ViIjoiZWx5fDEifQ._6hj6XUSmSLibgT9ZE1Pokf4oI9r-d6tEc1z2J-fBlr1710Qiso5yNcXqb3Z_xy7Qtemyq8jOlOZA8DvmkVBrg',
|
||||
'Incorrect token',
|
||||
];
|
||||
yield 'revoked by oauth client' => [
|
||||
'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJlbHktc2NvcGVzIjoiYWNjb3VudF9pbmZvLG1pbmVjcmFmdF9zZXJ2ZXJfc2Vzc2lvbiIsImlhdCI6MTU2NDYxMDUwMCwic3ViIjoiZWx5fDEiLCJhdWQiOiJjbGllbnR8dGxhdW5jaGVyIn0.YzUzvnREEoQPu8CvU6WLdysUU0bC_xzigQPs2LK1su38uysSYgSbPzNOZYkQnvcmVLehHY-ON44x-oA8Os-9ZA',
|
||||
'Token has been revoked',
|
||||
];
|
||||
yield 'revoked by unauthorized minecraft launcher' => [
|
||||
'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJlbHktc2NvcGVzIjoibWluZWNyYWZ0X3NlcnZlcl9zZXNzaW9uIiwiZWx5LWNsaWVudC10b2tlbiI6IllBTVhneTBBcEI5Z2dUL1VYNjNJaTdKcGtNd2ZwTmxaaE8yVVVEeEZ3YTFmZ2g4dksyN0RtV25vN2xqbk1pWWJwQ1VuS09YVnR2V1YrVVg1dWRQVVFsK04xY3BBZlJBL2ErZW1BZz09IiwiaWF0IjoxNTY0NjEwNTAwLCJzdWIiOiJlbHl8MSJ9.mxFgf4M1QSG4_Zd3sGoJUx9L9_XbjHd4T8-CWIVzmSPp2_9OHjq-CIFEwSwlfoz3QGN7NV0TpC8-PfRvjd93eQ',
|
||||
'Token has been revoked',
|
||||
];
|
||||
yield 'invalid signature' => [
|
||||
'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJlbHktc2NvcGVzIjoiYWNjb3VudHNfd2ViX3VzZXIiLCJpYXQiOjE1NjQ2MTA1NDIsImV4cCI6MTU2NDYxNDE0Miwic3ViIjoiZWx5fDEifQ.yth31f2PyhUkYSfBlizzUXWIgOvxxk8gNP-js0z8g1OT5rig40FPTIkgsZRctAwAAlj6QoIWW7-hxLTcSb2vmw',
|
||||
'Incorrect token',
|
||||
|
||||
Reference in New Issue
Block a user