mirror of
https://github.com/elyby/accounts.git
synced 2024-12-02 11:41:05 +05:30
124 lines
4.2 KiB
PHP
124 lines
4.2 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace api\components\OAuth2\Grants;
|
|
|
|
use api\components\OAuth2\CryptTrait;
|
|
use api\components\Tokens\TokenReader;
|
|
use Carbon\Carbon;
|
|
use common\models\OauthSession;
|
|
use InvalidArgumentException;
|
|
use Lcobucci\JWT\ValidationData;
|
|
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
|
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
|
use League\OAuth2\Server\Grant\RefreshTokenGrant as BaseRefreshTokenGrant;
|
|
use Psr\Http\Message\ServerRequestInterface;
|
|
use Yii;
|
|
|
|
class RefreshTokenGrant extends BaseRefreshTokenGrant {
|
|
use CryptTrait;
|
|
|
|
/**
|
|
* Previously, refresh tokens were stored in Redis.
|
|
* If received refresh token is matches the legacy token template,
|
|
* restore the information from the legacy storage.
|
|
*
|
|
* @param ServerRequestInterface $request
|
|
* @param string $clientId
|
|
*
|
|
* @return array
|
|
* @throws OAuthServerException
|
|
*/
|
|
protected function validateOldRefreshToken(ServerRequestInterface $request, $clientId): array {
|
|
$refreshToken = $this->getRequestParameter('refresh_token', $request);
|
|
if ($refreshToken !== null && mb_strlen($refreshToken) === 40) {
|
|
return $this->validateLegacyRefreshToken($refreshToken);
|
|
}
|
|
|
|
return $this->validateAccessToken($refreshToken);
|
|
}
|
|
|
|
/**
|
|
* Currently we're not rotating refresh tokens.
|
|
* So we overriding this method to always return null, which means,
|
|
* that refresh_token will not be issued.
|
|
*
|
|
* @param AccessTokenEntityInterface $accessToken
|
|
*
|
|
* @return RefreshTokenEntityInterface|null
|
|
*/
|
|
protected function issueRefreshToken(AccessTokenEntityInterface $accessToken): ?RefreshTokenEntityInterface {
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @param string $refreshToken
|
|
* @return array
|
|
* @throws OAuthServerException
|
|
*/
|
|
private function validateLegacyRefreshToken(string $refreshToken): array {
|
|
$result = Yii::$app->redis->get("oauth:refresh:tokens:{$refreshToken}");
|
|
if ($result === null) {
|
|
throw OAuthServerException::invalidRefreshToken('Token has been revoked');
|
|
}
|
|
|
|
try {
|
|
[
|
|
'access_token_id' => $accessTokenId,
|
|
'session_id' => $sessionId,
|
|
] = json_decode($result, true, 512, JSON_THROW_ON_ERROR);
|
|
} catch (\Exception $e) {
|
|
throw OAuthServerException::invalidRefreshToken('Cannot decrypt the refresh token', $e);
|
|
}
|
|
|
|
/** @var OauthSession|null $relatedSession */
|
|
$relatedSession = OauthSession::findOne(['legacy_id' => $sessionId]);
|
|
if ($relatedSession === null) {
|
|
throw OAuthServerException::invalidRefreshToken('Token has been revoked');
|
|
}
|
|
|
|
return [
|
|
'client_id' => $relatedSession->client_id,
|
|
'refresh_token_id' => $refreshToken,
|
|
'access_token_id' => $accessTokenId,
|
|
'scopes' => $relatedSession->getScopes(),
|
|
'user_id' => $relatedSession->account_id,
|
|
'expire_time' => null,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param string $jwt
|
|
* @return array
|
|
* @throws OAuthServerException
|
|
*/
|
|
private function validateAccessToken(string $jwt): array {
|
|
try {
|
|
$token = Yii::$app->tokens->parse($jwt);
|
|
} catch (InvalidArgumentException $e) {
|
|
throw OAuthServerException::invalidRefreshToken('Cannot decrypt the refresh token', $e);
|
|
}
|
|
|
|
if (!Yii::$app->tokens->verify($token)) {
|
|
throw OAuthServerException::invalidRefreshToken('Cannot decrypt the refresh token');
|
|
}
|
|
|
|
if (!$token->validate(new ValidationData(Carbon::now()->getTimestamp()))) {
|
|
throw OAuthServerException::invalidRefreshToken('Token has expired');
|
|
}
|
|
|
|
$reader = new TokenReader($token);
|
|
|
|
return [
|
|
'client_id' => $reader->getClientId(),
|
|
'refresh_token_id' => '', // This value used only to invalidate old token
|
|
'access_token_id' => '', // This value used only to invalidate old token
|
|
'scopes' => $reader->getScopes(),
|
|
'user_id' => $reader->getAccountId(),
|
|
'expire_time' => null,
|
|
];
|
|
}
|
|
|
|
}
|