Introduce revokation mechanism

This commit is contained in:
ErickSkrauch
2019-12-10 01:38:09 +03:00
parent ba7fad84a0
commit 016a193263
11 changed files with 103 additions and 20 deletions

View File

@@ -36,7 +36,7 @@ class Component extends BaseComponent {
public $privateKeyPass;
/**
* @var string|\Defuse\Crypto\Key
* @var string
*/
public $encryptionKey;

View File

@@ -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();
}

View File

@@ -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;
}

View File

@@ -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.");

View File

@@ -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);

View File

@@ -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',