From 422d5c4fd48c8be5c3006bc7c99b783a8301eb20 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Wed, 30 Nov 2016 02:19:14 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A5=D1=80=D0=B0=D0=BD=D0=B8=D0=BB=D0=B8?= =?UTF-8?q?=D1=89=D0=B5=20access=5Ftoken=20=D0=B2=D1=8B=D0=BD=D0=B5=D1=81?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20=D0=B2=20redis=20=D0=9F=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D0=B0=20=D0=BB=D0=BE=D0=B3=D0=B8?= =?UTF-8?q?=D0=BA=D0=B0=20=D1=81=D0=B2=D1=8F=D0=B7=D0=B8=20=D0=BC=D0=BE?= =?UTF-8?q?=D0=B4=D0=B5=D0=BB=D0=B5=D0=B9=20=D0=B4=D0=BB=D1=8F=20oAuth=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D1=86=D0=B5=D1=81=D1=81=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/components/ApiUser/Identity.php | 6 +- .../OAuth2/Entities/AccessTokenEntity.php | 19 +++ .../OAuth2/Entities/AuthCodeEntity.php | 4 + .../OAuth2/Entities/RefreshTokenEntity.php | 34 +++++ .../OAuth2/Grants/RefreshTokenGrant.php | 128 ++++++++++++++++++ .../OAuth2/Storage/AccessTokenStorage.php | 81 ++++------- .../OAuth2/Storage/AuthCodeStorage.php | 68 ++++------ .../OAuth2/Storage/RefreshTokenStorage.php | 45 +++--- .../OAuth2/Storage/SessionStorage.php | 39 ++---- common/components/Redis/Key.php | 9 +- common/models/OauthAccessToken.php | 1 + common/models/OauthSession.php | 9 ++ 12 files changed, 297 insertions(+), 146 deletions(-) diff --git a/api/components/ApiUser/Identity.php b/api/components/ApiUser/Identity.php index 3000b93..d3e4fab 100644 --- a/api/components/ApiUser/Identity.php +++ b/api/components/ApiUser/Identity.php @@ -10,9 +10,9 @@ use yii\web\IdentityInterface; use yii\web\UnauthorizedHttpException; /** - * @property Account $account - * @property OauthClient $client - * @property OauthSession $session + * @property Account $account + * @property OauthClient $client + * @property OauthSession $session * @property OauthAccessToken $accessToken */ class Identity implements IdentityInterface { diff --git a/api/components/OAuth2/Entities/AccessTokenEntity.php b/api/components/OAuth2/Entities/AccessTokenEntity.php index 3f92c5b..183704d 100644 --- a/api/components/OAuth2/Entities/AccessTokenEntity.php +++ b/api/components/OAuth2/Entities/AccessTokenEntity.php @@ -1,6 +1,8 @@ sessionId; } + public function setSessionId($sessionId) { + $this->sessionId = $sessionId; + } + /** * @inheritdoc * @return static @@ -22,4 +28,17 @@ class AccessTokenEntity extends \League\OAuth2\Server\Entity\AccessTokenEntity { return $this; } + public function getSession() { + if ($this->session instanceof OriginalSessionEntity) { + return $this->session; + } + + $sessionStorage = $this->server->getSessionStorage(); + if (!$sessionStorage instanceof SessionStorage) { + throw new ErrorException('SessionStorage must be instance of ' . SessionStorage::class); + } + + return $sessionStorage->getById($this->sessionId); + } + } diff --git a/api/components/OAuth2/Entities/AuthCodeEntity.php b/api/components/OAuth2/Entities/AuthCodeEntity.php index 892189e..28bfc2b 100644 --- a/api/components/OAuth2/Entities/AuthCodeEntity.php +++ b/api/components/OAuth2/Entities/AuthCodeEntity.php @@ -22,4 +22,8 @@ class AuthCodeEntity extends \League\OAuth2\Server\Entity\AuthCodeEntity { return $this; } + public function setSessionId(string $sessionId) { + $this->sessionId = $sessionId; + } + } diff --git a/api/components/OAuth2/Entities/RefreshTokenEntity.php b/api/components/OAuth2/Entities/RefreshTokenEntity.php index bd4607f..2404fa7 100644 --- a/api/components/OAuth2/Entities/RefreshTokenEntity.php +++ b/api/components/OAuth2/Entities/RefreshTokenEntity.php @@ -1,10 +1,44 @@ session instanceof SessionEntity) { + return $this->session; + } + + $sessionStorage = $this->server->getSessionStorage(); + if (!$sessionStorage instanceof SessionStorage) { + throw new ErrorException('SessionStorage must be instance of ' . SessionStorage::class); + } + + return $sessionStorage->getById($this->sessionId); + } + + public function getSessionId() : int { + return $this->sessionId; + } + + public function setSession(OriginalSessionEntity $session) { + parent::setSession($session); + $this->setSessionId($session->getId()); + + return $this; + } + + public function setSessionId(int $sessionId) { + $this->sessionId = $sessionId; + } + } diff --git a/api/components/OAuth2/Grants/RefreshTokenGrant.php b/api/components/OAuth2/Grants/RefreshTokenGrant.php index e1fd3ce..d98b3d6 100644 --- a/api/components/OAuth2/Grants/RefreshTokenGrant.php +++ b/api/components/OAuth2/Grants/RefreshTokenGrant.php @@ -2,6 +2,12 @@ namespace api\components\OAuth2\Grants; use api\components\OAuth2\Entities; +use ErrorException; +use League\OAuth2\Server\Entity\ClientEntity as OriginalClientEntity; +use League\OAuth2\Server\Entity\RefreshTokenEntity as OriginalRefreshTokenEntity; +use League\OAuth2\Server\Event; +use League\OAuth2\Server\Exception; +use League\OAuth2\Server\Util\SecureKey; class RefreshTokenGrant extends \League\OAuth2\Server\Grant\RefreshTokenGrant { @@ -19,4 +25,126 @@ class RefreshTokenGrant extends \League\OAuth2\Server\Grant\RefreshTokenGrant { return new Entities\SessionEntity($this->server); } + /** + * Метод таки пришлось переписать по той причине, что нынче мы храним access_token в redis с expire значением, + * так что он может банально несуществовать на тот момент, когда к нему через refresh_token попытаются обратиться. + * Поэтому мы расширили логику RefreshTokenEntity и она теперь знает о сессии, в рамках которой была создана + * + * @inheritdoc + */ + public function completeFlow() { + $clientId = $this->server->getRequest()->request->get('client_id', $this->server->getRequest()->getUser()); + if (is_null($clientId)) { + throw new Exception\InvalidRequestException('client_id'); + } + + $clientSecret = $this->server->getRequest()->request->get( + 'client_secret', + $this->server->getRequest()->getPassword() + ); + if ($this->shouldRequireClientSecret() && is_null($clientSecret)) { + throw new Exception\InvalidRequestException('client_secret'); + } + + // Validate client ID and client secret + $client = $this->server->getClientStorage()->get( + $clientId, + $clientSecret, + null, + $this->getIdentifier() + ); + + if (($client instanceof OriginalClientEntity) === false) { + $this->server->getEventEmitter()->emit(new Event\ClientAuthenticationFailedEvent($this->server->getRequest())); + throw new Exception\InvalidClientException(); + } + + $oldRefreshTokenParam = $this->server->getRequest()->request->get('refresh_token', null); + if ($oldRefreshTokenParam === null) { + throw new Exception\InvalidRequestException('refresh_token'); + } + + // Validate refresh token + $oldRefreshToken = $this->server->getRefreshTokenStorage()->get($oldRefreshTokenParam); + if (($oldRefreshToken instanceof OriginalRefreshTokenEntity) === false) { + throw new Exception\InvalidRefreshException(); + } + + // Ensure the old refresh token hasn't expired + if ($oldRefreshToken->isExpired()) { + throw new Exception\InvalidRefreshException(); + } + + /** @var Entities\AccessTokenEntity|null $oldAccessToken */ + $oldAccessToken = $oldRefreshToken->getAccessToken(); + if ($oldAccessToken instanceof Entities\AccessTokenEntity) { + // Get the scopes for the original session + $session = $oldAccessToken->getSession(); + } else { + if (!$oldRefreshToken instanceof Entities\RefreshTokenEntity) { + throw new ErrorException('oldRefreshToken must be instance of ' . Entities\RefreshTokenEntity::class); + } + + $session = $oldRefreshToken->getSession(); + } + + $scopes = $this->formatScopes($session->getScopes()); + + // Get and validate any requested scopes + $requestedScopesString = $this->server->getRequest()->request->get('scope', ''); + $requestedScopes = $this->validateScopes($requestedScopesString, $client); + + // If no new scopes are requested then give the access token the original session scopes + if (count($requestedScopes) === 0) { + $newScopes = $scopes; + } else { + // The OAuth spec says that a refreshed access token can have the original scopes or fewer so ensure + // the request doesn't include any new scopes + foreach ($requestedScopes as $requestedScope) { + if (!isset($scopes[$requestedScope->getId()])) { + throw new Exception\InvalidScopeException($requestedScope->getId()); + } + } + + $newScopes = $requestedScopes; + } + + // Generate a new access token and assign it the correct sessions + $newAccessToken = $this->createAccessTokenEntity(); + $newAccessToken->setId(SecureKey::generate()); + $newAccessToken->setExpireTime($this->getAccessTokenTTL() + time()); + $newAccessToken->setSession($session); + + foreach ($newScopes as $newScope) { + $newAccessToken->associateScope($newScope); + } + + // Expire the old token and save the new one + ($oldAccessToken instanceof Entities\AccessTokenEntity) && $oldAccessToken->expire(); + $newAccessToken->save(); + + $this->server->getTokenType()->setSession($session); + $this->server->getTokenType()->setParam('access_token', $newAccessToken->getId()); + $this->server->getTokenType()->setParam('expires_in', $this->getAccessTokenTTL()); + + if ($this->shouldRotateRefreshTokens()) { + // Expire the old refresh token + $oldRefreshToken->expire(); + + // Generate a new refresh token + $newRefreshToken = $this->createRefreshTokenEntity(); + $newRefreshToken->setId(SecureKey::generate()); + $newRefreshToken->setExpireTime($this->getRefreshTokenTTL() + time()); + $newRefreshToken->setAccessToken($newAccessToken); + $newRefreshToken->save(); + + $this->server->getTokenType()->setParam('refresh_token', $newRefreshToken->getId()); + } else { + $oldRefreshToken->setAccessToken($newAccessToken); + $oldRefreshToken->save(); + } + + return $this->server->getTokenType()->generateResponse(); + } + } diff --git a/api/components/OAuth2/Storage/AccessTokenStorage.php b/api/components/OAuth2/Storage/AccessTokenStorage.php index 513dcb1..fdeb14c 100644 --- a/api/components/OAuth2/Storage/AccessTokenStorage.php +++ b/api/components/OAuth2/Storage/AccessTokenStorage.php @@ -2,87 +2,66 @@ namespace api\components\OAuth2\Storage; use api\components\OAuth2\Entities\AccessTokenEntity; -use common\models\OauthAccessToken; +use common\components\Redis\Key; +use common\components\Redis\Set; use League\OAuth2\Server\Entity\AccessTokenEntity as OriginalAccessTokenEntity; use League\OAuth2\Server\Entity\ScopeEntity; use League\OAuth2\Server\Storage\AbstractStorage; use League\OAuth2\Server\Storage\AccessTokenInterface; -use yii\db\Exception; +use yii\helpers\Json; class AccessTokenStorage extends AbstractStorage implements AccessTokenInterface { - private $cache = []; + public $dataTable = 'oauth_access_tokens'; - /** - * @param string $token - * @return OauthAccessToken|null - */ - private function getTokenModel($token) { - if (!isset($this->cache[$token])) { - $this->cache[$token] = OauthAccessToken::findOne($token); - } - - return $this->cache[$token]; - } - - /** - * @inheritdoc - */ public function get($token) { - $model = $this->getTokenModel($token); - if ($model === null) { - return null; - } - - /** @var SessionStorage $sessionStorage */ - $sessionStorage = $this->server->getSessionStorage(); + $result = Json::decode((new Key($this->dataTable, $token))->getValue()); $token = new AccessTokenEntity($this->server); - $token->setId($model->access_token); - $token->setExpireTime($model->expire_time); - $token->setSession($sessionStorage->getById($model->session_id)); + $token->setId($result['id']); + $token->setExpireTime($result['expire_time']); + $token->setSessionId($result['session_id']); return $token; } - /** - * @inheritdoc - */ public function getScopes(OriginalAccessTokenEntity $token) { + $scopes = $this->scopes($token->getId()); $entities = []; - foreach($this->getTokenModel($token->getId())->getScopes() as $scope) { - $entities[] = (new ScopeEntity($this->server))->hydrate(['id' => $scope]); + foreach($scopes as $scope) { + if ($this->server->getScopeStorage()->get($scope) !== null) { + $entities[] = (new ScopeEntity($this->server))->hydrate(['id' => $scope]); + } } return $entities; } - /** - * @inheritdoc - */ public function create($token, $expireTime, $sessionId) { - $model = new OauthAccessToken(); - $model->access_token = $token; - $model->expire_time = $expireTime; - $model->session_id = $sessionId; + $payload = Json::encode([ + 'id' => $token, + 'expire_time' => $expireTime, + 'session_id' => $sessionId, + ]); - if (!$model->save()) { - throw new Exception('Cannot save ' . OauthAccessToken::class . ' model.'); - } + $this->key($token)->setValue($payload)->expireAt($expireTime); } - /** - * @inheritdoc - */ public function associateScope(OriginalAccessTokenEntity $token, ScopeEntity $scope) { - $this->getTokenModel($token->getId())->getScopes()->add($scope->getId()); + $this->scopes($token->getId())->add($scope->getId())->expireAt($token->getExpireTime()); } - /** - * @inheritdoc - */ public function delete(OriginalAccessTokenEntity $token) { - $this->getTokenModel($token->getId())->delete(); + $this->key($token->getId())->delete(); + $this->scopes($token->getId())->delete(); + } + + private function key(string $token) : Key { + return new Key($this->dataTable, $token); + } + + private function scopes(string $token) : Set { + return new Set($this->dataTable, $token, 'scopes'); } } diff --git a/api/components/OAuth2/Storage/AuthCodeStorage.php b/api/components/OAuth2/Storage/AuthCodeStorage.php index a6a888d..77d7f51 100644 --- a/api/components/OAuth2/Storage/AuthCodeStorage.php +++ b/api/components/OAuth2/Storage/AuthCodeStorage.php @@ -8,81 +8,65 @@ use League\OAuth2\Server\Entity\AuthCodeEntity as OriginalAuthCodeEntity; use League\OAuth2\Server\Entity\ScopeEntity; use League\OAuth2\Server\Storage\AbstractStorage; use League\OAuth2\Server\Storage\AuthCodeInterface; +use yii\helpers\Json; class AuthCodeStorage extends AbstractStorage implements AuthCodeInterface { public $dataTable = 'oauth_auth_codes'; - public $ttl = 3600; // 1h - - /** - * @inheritdoc - */ public function get($code) { - $result = json_decode((new Key($this->dataTable, $code))->getValue(), true); - if (!$result) { + $result = Json::decode((new Key($this->dataTable, $code))->getValue()); + if ($result === null) { return null; } - if ($result['expire_time'] < time()) { - return null; - } - - /** @var SessionStorage $sessionStorage */ - $sessionStorage = $this->server->getSessionStorage(); - $entity = new AuthCodeEntity($this->server); $entity->setId($result['id']); - $entity->setRedirectUri($result['client_redirect_uri']); $entity->setExpireTime($result['expire_time']); - $entity->setSession($sessionStorage->getById($result['session_id'])); + $entity->setSessionId($result['session_id']); + $entity->setRedirectUri($result['client_redirect_uri']); return $entity; } - /** - * @inheritdoc - */ public function create($token, $expireTime, $sessionId, $redirectUri) { - $payload = [ + $payload = Json::encode([ 'id' => $token, 'expire_time' => $expireTime, 'session_id' => $sessionId, 'client_redirect_uri' => $redirectUri, - ]; + ]); - (new Key($this->dataTable, $token))->setValue($payload)->expire($this->ttl); + $this->key($token)->setValue($payload)->expireAt($expireTime); } - /** - * @inheritdoc - */ public function getScopes(OriginalAuthCodeEntity $token) { - $result = new Set($this->dataTable, $token->getId(), 'scopes'); - $response = []; - foreach ($result as $scope) { - // TODO: нужно проверить все выданные скоупы на их существование - $response[] = (new ScopeEntity($this->server))->hydrate(['id' => $scope]); + $scopes = $this->scopes($token->getId()); + $scopesEntities = []; + foreach ($scopes as $scope) { + if ($this->server->getScopeStorage()->get($scope) !== null) { + $scopesEntities[] = (new ScopeEntity($this->server))->hydrate(['id' => $scope]); + } } - return $response; + return $scopesEntities; } - /** - * @inheritdoc - */ public function associateScope(OriginalAuthCodeEntity $token, ScopeEntity $scope) { - (new Set($this->dataTable, $token->getId(), 'scopes'))->add($scope->getId())->expire($this->ttl); + $this->scopes($token->getId())->add($scope->getId())->expireAt($token->getExpireTime()); } - /** - * @inheritdoc - */ public function delete(OriginalAuthCodeEntity $token) { - // Удаляем ключ - (new Set($this->dataTable, $token->getId()))->delete(); - // Удаляем список скоупов для ключа - (new Set($this->dataTable, $token->getId(), 'scopes'))->delete(); + $this->key($token->getId())->delete(); + $this->scopes($token->getId())->delete(); + } + + private function key(string $token) : Key { + return new Key($this->dataTable, $token); + } + + private function scopes(string $token) : Set { + return new Set($this->dataTable, $token, 'scopes'); } } diff --git a/api/components/OAuth2/Storage/RefreshTokenStorage.php b/api/components/OAuth2/Storage/RefreshTokenStorage.php index a1a31cf..2321e76 100644 --- a/api/components/OAuth2/Storage/RefreshTokenStorage.php +++ b/api/components/OAuth2/Storage/RefreshTokenStorage.php @@ -3,47 +3,58 @@ namespace api\components\OAuth2\Storage; use api\components\OAuth2\Entities\RefreshTokenEntity; use common\components\Redis\Key; +use common\components\Redis\Set; +use common\models\OauthSession; +use ErrorException; use League\OAuth2\Server\Entity\RefreshTokenEntity as OriginalRefreshTokenEntity; use League\OAuth2\Server\Storage\AbstractStorage; use League\OAuth2\Server\Storage\RefreshTokenInterface; +use Yii; +use yii\helpers\Json; class RefreshTokenStorage extends AbstractStorage implements RefreshTokenInterface { public $dataTable = 'oauth_refresh_tokens'; - /** - * @inheritdoc - */ public function get($token) { - $result = json_decode((new Key($this->dataTable, $token))->getValue(), true); - if (!$result) { - return null; - } + $result = Json::decode((new Key($this->dataTable, $token))->getValue()); $entity = new RefreshTokenEntity($this->server); $entity->setId($result['id']); $entity->setAccessTokenId($result['access_token_id']); + $entity->setSessionId($result['session_id']); return $entity; } - /** - * @inheritdoc - */ public function create($token, $expireTime, $accessToken) { - $payload = [ + $sessionId = $this->server->getAccessTokenStorage()->get($accessToken)->getSession()->getId(); + $payload = Json::encode([ 'id' => $token, 'access_token_id' => $accessToken, - ]; + 'session_id' => $sessionId, + ]); - (new Key($this->dataTable, $token))->setValue($payload); + $this->key($token)->setValue($payload); + $this->sessionHash($sessionId)->add($token); } - /** - * @inheritdoc - */ public function delete(OriginalRefreshTokenEntity $token) { - (new Key($this->dataTable, $token->getId()))->delete(); + if (!$token instanceof RefreshTokenEntity) { + throw new ErrorException('Token must be instance of ' . RefreshTokenEntity::class); + } + + $this->key($token->getId())->delete(); + $this->sessionHash($token->getSessionId())->remove($token->getId()); + } + + public function sessionHash(string $sessionId) : Set { + $tableName = Yii::$app->db->getSchema()->getRawTableName(OauthSession::tableName()); + return new Set($tableName, $sessionId, 'refresh_tokens'); + } + + private function key(string $token) : Key { + return new Key($this->dataTable, $token); } } diff --git a/api/components/OAuth2/Storage/SessionStorage.php b/api/components/OAuth2/Storage/SessionStorage.php index 06e66ff..8777f96 100644 --- a/api/components/OAuth2/Storage/SessionStorage.php +++ b/api/components/OAuth2/Storage/SessionStorage.php @@ -11,13 +11,10 @@ use League\OAuth2\Server\Entity\ScopeEntity; use League\OAuth2\Server\Entity\SessionEntity as OriginalSessionEntity; use League\OAuth2\Server\Storage\AbstractStorage; use League\OAuth2\Server\Storage\SessionInterface; -use yii\db\ActiveQuery; use yii\db\Exception; class SessionStorage extends AbstractStorage implements SessionInterface { - private $cache = []; - /** * @param string $sessionId * @return SessionEntity|null @@ -26,23 +23,10 @@ class SessionStorage extends AbstractStorage implements SessionInterface { return $this->hydrate($this->getSessionModel($sessionId)); } - /** - * @inheritdoc - */ public function getByAccessToken(OriginalAccessTokenEntity $accessToken) { - /** @var OauthSession|null $model */ - $model = OauthSession::find()->innerJoinWith([ - 'accessTokens' => function(ActiveQuery $query) use ($accessToken) { - $query->andWhere(['access_token' => $accessToken->getId()]); - }, - ])->one(); - - return $this->hydrate($model); + throw new ErrorException('This method is not implemented and should not be used'); } - /** - * @inheritdoc - */ public function getByAuthCode(OriginalAuthCodeEntity $authCode) { if (!$authCode instanceof AuthCodeEntity) { throw new ErrorException('This module assumes that $authCode typeof ' . AuthCodeEntity::class); @@ -51,22 +35,17 @@ class SessionStorage extends AbstractStorage implements SessionInterface { return $this->getById($authCode->getSessionId()); } - /** - * {@inheritdoc} - */ public function getScopes(OriginalSessionEntity $session) { $result = []; foreach ($this->getSessionModel($session->getId())->getScopes() as $scope) { - // TODO: нужно проверить все выданные скоупы на их существование - $result[] = (new ScopeEntity($this->server))->hydrate(['id' => $scope]); + if ($this->server->getScopeStorage()->get($scope) !== null) { + $result[] = (new ScopeEntity($this->server))->hydrate(['id' => $scope]); + } } return $result; } - /** - * @inheritdoc - */ public function create($ownerType, $ownerId, $clientId, $clientRedirectUri = null) { $sessionId = OauthSession::find() ->select('id') @@ -93,19 +72,17 @@ class SessionStorage extends AbstractStorage implements SessionInterface { return $sessionId; } - /** - * @inheritdoc - */ public function associateScope(OriginalSessionEntity $session, ScopeEntity $scope) { $this->getSessionModel($session->getId())->getScopes()->add($scope->getId()); } private function getSessionModel(string $sessionId) : OauthSession { - if (!isset($this->cache[$sessionId])) { - $this->cache[$sessionId] = OauthSession::findOne($sessionId); + $session = OauthSession::findOne($sessionId); + if ($session === null) { + throw new ErrorException('Cannot find oauth session'); } - return $this->cache[$sessionId]; + return $session; } private function hydrate(OauthSession $sessionModel) { diff --git a/common/components/Redis/Key.php b/common/components/Redis/Key.php index dbecc5d..fb43152 100644 --- a/common/components/Redis/Key.php +++ b/common/components/Redis/Key.php @@ -24,7 +24,7 @@ class Key { } public function setValue($value) { - $this->getRedis()->set($this->key, json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + $this->getRedis()->set($this->key, $value); return $this; } @@ -37,11 +37,16 @@ class Key { return (bool)$this->getRedis()->exists($this->key); } - public function expire($ttl) { + public function expire(int $ttl) { $this->getRedis()->expire($this->key, $ttl); return $this; } + public function expireAt(int $unixTimestamp) { + $this->getRedis()->expireat($this->key, $unixTimestamp); + return $this; + } + public function __construct(...$key) { if (empty($key)) { throw new InvalidArgumentException('You must specify at least one key.'); diff --git a/common/models/OauthAccessToken.php b/common/models/OauthAccessToken.php index 660ac7a..7364a79 100644 --- a/common/models/OauthAccessToken.php +++ b/common/models/OauthAccessToken.php @@ -15,6 +15,7 @@ use yii\db\ActiveRecord; * * Отношения: * @property OauthSession $session + * @deprecated */ class OauthAccessToken extends ActiveRecord { diff --git a/common/models/OauthSession.php b/common/models/OauthSession.php index 730c903..675cd31 100644 --- a/common/models/OauthSession.php +++ b/common/models/OauthSession.php @@ -2,6 +2,7 @@ namespace common\models; use common\components\Redis\Set; +use Yii; use yii\db\ActiveRecord; /** @@ -46,6 +47,14 @@ class OauthSession extends ActiveRecord { } $this->getScopes()->delete(); + /** @var \api\components\OAuth2\Storage\RefreshTokenStorage $refreshTokensStorage */ + $refreshTokensStorage = Yii::$app->oauth->getAuthServer()->getRefreshTokenStorage(); + $refreshTokensSet = $refreshTokensStorage->sessionHash($this->id); + foreach ($refreshTokensSet->members() as $refreshTokenId) { + $refreshTokensStorage->delete($refreshTokensStorage->get($refreshTokenId)); + } + + $refreshTokensSet->delete(); return true; }