mirror of
https://github.com/elyby/accounts.git
synced 2024-11-06 08:11:24 +05:30
Upgrade oauth2-server to 8.0.0 version, rewrite repositories and entities, start rewriting tests. Intermediate commit [skip ci]
This commit is contained in:
parent
23a220637c
commit
0b63dc2d84
@ -1,10 +1,13 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\components\OAuth2;
|
||||
|
||||
use api\components\OAuth2\Keys\EmptyKey;
|
||||
use api\components\OAuth2\Repositories;
|
||||
use DateInterval;
|
||||
use League\OAuth2\Server\AuthorizationServer;
|
||||
use League\OAuth2\Server\Storage\AccessTokenInterface;
|
||||
use League\OAuth2\Server\Storage\RefreshTokenInterface;
|
||||
use League\OAuth2\Server\Storage\SessionInterface;
|
||||
use League\OAuth2\Server\Grant;
|
||||
use yii\base\Component as BaseComponent;
|
||||
|
||||
/**
|
||||
@ -19,18 +22,27 @@ class Component extends BaseComponent {
|
||||
|
||||
public function getAuthServer(): AuthorizationServer {
|
||||
if ($this->_authServer === null) {
|
||||
$authServer = new AuthorizationServer();
|
||||
$authServer->setAccessTokenStorage(new Storage\AccessTokenStorage());
|
||||
$authServer->setClientStorage(new Storage\ClientStorage());
|
||||
$authServer->setScopeStorage(new Storage\ScopeStorage());
|
||||
$authServer->setSessionStorage(new Storage\SessionStorage());
|
||||
$authServer->setAuthCodeStorage(new Storage\AuthCodeStorage());
|
||||
$authServer->setRefreshTokenStorage(new Storage\RefreshTokenStorage());
|
||||
$authServer->setAccessTokenTTL(86400); // 1d
|
||||
$clientsRepo = new Repositories\ClientRepository();
|
||||
$accessTokensRepo = new Repositories\AccessTokenRepository();
|
||||
$scopesRepo = new Repositories\ScopeRepository();
|
||||
$authCodesRepo = new Repositories\AuthCodeRepository();
|
||||
$refreshTokensRepo = new Repositories\RefreshTokenRepository();
|
||||
|
||||
$authServer->addGrantType(new Grants\AuthCodeGrant());
|
||||
$authServer->addGrantType(new Grants\RefreshTokenGrant());
|
||||
$authServer->addGrantType(new Grants\ClientCredentialsGrant());
|
||||
$accessTokenTTL = new DateInterval('P1D');
|
||||
|
||||
$authServer = new AuthorizationServer(
|
||||
$clientsRepo,
|
||||
$accessTokensRepo,
|
||||
$scopesRepo,
|
||||
new EmptyKey(),
|
||||
'123' // TODO: extract to the variable
|
||||
);
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$authCodeGrant = new Grant\AuthCodeGrant($authCodesRepo, $refreshTokensRepo, new DateInterval('PT10M'));
|
||||
$authCodeGrant->disableRequireCodeChallengeForPublicClients();
|
||||
$authServer->enableGrantType($authCodeGrant, $accessTokenTTL);
|
||||
$authServer->enableGrantType(new Grant\RefreshTokenGrant($refreshTokensRepo), $accessTokenTTL);
|
||||
$authServer->enableGrantType(new Grant\ClientCredentialsGrant(), $accessTokenTTL);
|
||||
|
||||
$this->_authServer = $authServer;
|
||||
}
|
||||
@ -38,16 +50,4 @@ class Component extends BaseComponent {
|
||||
return $this->_authServer;
|
||||
}
|
||||
|
||||
public function getAccessTokenStorage(): AccessTokenInterface {
|
||||
return $this->getAuthServer()->getAccessTokenStorage();
|
||||
}
|
||||
|
||||
public function getRefreshTokenStorage(): RefreshTokenInterface {
|
||||
return $this->getAuthServer()->getRefreshTokenStorage();
|
||||
}
|
||||
|
||||
public function getSessionStorage(): SessionInterface {
|
||||
return $this->getAuthServer()->getSessionStorage();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace api\components\OAuth2\Entities;
|
||||
|
||||
use api\components\OAuth2\Storage\SessionStorage;
|
||||
use api\components\OAuth2\Repositories\SessionStorage;
|
||||
use ErrorException;
|
||||
use League\OAuth2\Server\Entity\SessionEntity as OriginalSessionEntity;
|
||||
|
||||
|
@ -1,29 +1,18 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\components\OAuth2\Entities;
|
||||
|
||||
use League\OAuth2\Server\Entity\SessionEntity as OriginalSessionEntity;
|
||||
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
|
||||
use League\OAuth2\Server\Entities\Traits\AuthCodeTrait;
|
||||
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||
use League\OAuth2\Server\Entities\Traits\TokenEntityTrait;
|
||||
|
||||
class AuthCodeEntity extends \League\OAuth2\Server\Entity\AuthCodeEntity {
|
||||
class AuthCodeEntity implements AuthCodeEntityInterface {
|
||||
use EntityTrait;
|
||||
use AuthCodeTrait;
|
||||
use TokenEntityTrait;
|
||||
|
||||
protected $sessionId;
|
||||
|
||||
public function getSessionId() {
|
||||
return $this->sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @return static
|
||||
*/
|
||||
public function setSession(OriginalSessionEntity $session) {
|
||||
parent::setSession($session);
|
||||
$this->sessionId = $session->getId();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setSessionId(string $sessionId) {
|
||||
$this->sessionId = $sessionId;
|
||||
}
|
||||
// TODO: constructor
|
||||
|
||||
}
|
||||
|
@ -1,32 +1,21 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\components\OAuth2\Entities;
|
||||
|
||||
class ClientEntity extends \League\OAuth2\Server\Entity\ClientEntity {
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Entities\Traits\ClientTrait;
|
||||
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||
|
||||
private $isTrusted;
|
||||
class ClientEntity implements ClientEntityInterface {
|
||||
use EntityTrait;
|
||||
use ClientTrait;
|
||||
|
||||
public function setId(string $id) {
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function setName(string $name) {
|
||||
public function __construct(string $id, string $name, $redirectUri, bool $isTrusted = false) {
|
||||
$this->identifier = $id;
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function setSecret(string $secret) {
|
||||
$this->secret = $secret;
|
||||
}
|
||||
|
||||
public function setRedirectUri($redirectUri) {
|
||||
$this->redirectUri = $redirectUri;
|
||||
}
|
||||
|
||||
public function setIsTrusted(bool $isTrusted) {
|
||||
$this->isTrusted = $isTrusted;
|
||||
}
|
||||
|
||||
public function isTrusted(): bool {
|
||||
return $this->isTrusted;
|
||||
$this->isConfidential = $isTrusted;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,43 +3,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace api\components\OAuth2\Entities;
|
||||
|
||||
use api\components\OAuth2\Storage\SessionStorage;
|
||||
use League\OAuth2\Server\Entity\SessionEntity as OriginalSessionEntity;
|
||||
use Webmozart\Assert\Assert;
|
||||
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
||||
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||
use League\OAuth2\Server\Entities\Traits\RefreshTokenTrait;
|
||||
|
||||
class RefreshTokenEntity extends \League\OAuth2\Server\Entity\RefreshTokenEntity {
|
||||
|
||||
private $sessionId;
|
||||
|
||||
public function isExpired(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getSession(): SessionEntity {
|
||||
if ($this->session instanceof SessionEntity) {
|
||||
return $this->session;
|
||||
}
|
||||
|
||||
/** @var SessionStorage $sessionStorage */
|
||||
$sessionStorage = $this->server->getSessionStorage();
|
||||
Assert::isInstanceOf($sessionStorage, SessionStorage::class);
|
||||
|
||||
return $sessionStorage->getById($this->sessionId);
|
||||
}
|
||||
|
||||
public function getSessionId(): int {
|
||||
return $this->sessionId;
|
||||
}
|
||||
|
||||
public function setSession(OriginalSessionEntity $session): self {
|
||||
parent::setSession($session);
|
||||
$this->setSessionId((int)$session->getId());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setSessionId(int $sessionId): void {
|
||||
$this->sessionId = $sessionId;
|
||||
}
|
||||
class RefreshTokenEntity implements RefreshTokenEntityInterface {
|
||||
use EntityTrait;
|
||||
use RefreshTokenTrait;
|
||||
|
||||
}
|
||||
|
@ -1,10 +1,18 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\components\OAuth2\Entities;
|
||||
|
||||
class ScopeEntity extends \League\OAuth2\Server\Entity\ScopeEntity {
|
||||
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||
use League\OAuth2\Server\Entities\Traits\ScopeTrait;
|
||||
|
||||
public function setId(string $id) {
|
||||
$this->id = $id;
|
||||
class ScopeEntity implements ScopeEntityInterface {
|
||||
use EntityTrait;
|
||||
use ScopeTrait;
|
||||
|
||||
public function __construct(string $id) {
|
||||
$this->identifier = $id;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,22 +0,0 @@
|
||||
<?php
|
||||
namespace api\components\OAuth2\Exception;
|
||||
|
||||
use League\OAuth2\Server\Exception\OAuthException;
|
||||
|
||||
class AcceptRequiredException extends OAuthException {
|
||||
|
||||
public $httpStatusCode = 401;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $errorType = 'accept_required';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct('Client must accept authentication request.');
|
||||
}
|
||||
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
<?php
|
||||
namespace api\components\OAuth2\Exception;
|
||||
|
||||
class AccessDeniedException extends \League\OAuth2\Server\Exception\AccessDeniedException {
|
||||
|
||||
public function __construct($redirectUri = null) {
|
||||
parent::__construct();
|
||||
$this->redirectUri = $redirectUri;
|
||||
}
|
||||
|
||||
}
|
@ -6,7 +6,7 @@ use api\components\OAuth2\Entities\AuthCodeEntity;
|
||||
use api\components\OAuth2\Entities\ClientEntity;
|
||||
use api\components\OAuth2\Entities\RefreshTokenEntity;
|
||||
use api\components\OAuth2\Entities\SessionEntity;
|
||||
use api\components\OAuth2\Storage\ScopeStorage;
|
||||
use api\components\OAuth2\Repositories\ScopeStorage;
|
||||
use api\components\OAuth2\Utils\Scopes;
|
||||
use League\OAuth2\Server\Entity\AuthCodeEntity as BaseAuthCodeEntity;
|
||||
use League\OAuth2\Server\Entity\ClientEntity as BaseClientEntity;
|
||||
|
18
api/components/OAuth2/Keys/EmptyKey.php
Normal file
18
api/components/OAuth2/Keys/EmptyKey.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\components\OAuth2\Keys;
|
||||
|
||||
use League\OAuth2\Server\CryptKeyInterface;
|
||||
|
||||
class EmptyKey implements CryptKeyInterface {
|
||||
|
||||
public function getKeyPath(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getPassPhrase(): ?string {
|
||||
return '';
|
||||
}
|
||||
|
||||
}
|
56
api/components/OAuth2/Repositories/AccessTokenRepository.php
Normal file
56
api/components/OAuth2/Repositories/AccessTokenRepository.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\components\OAuth2\Repositories;
|
||||
|
||||
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||
|
||||
class AccessTokenRepository implements AccessTokenRepositoryInterface {
|
||||
|
||||
/**
|
||||
* Create a new access token
|
||||
*
|
||||
* @param ClientEntityInterface $clientEntity
|
||||
* @param \League\OAuth2\Server\Entities\ScopeEntityInterface $scopes
|
||||
* @param mixed $userIdentifier
|
||||
*
|
||||
* @return AccessTokenEntityInterface
|
||||
*/
|
||||
public function getNewToken(ClientEntityInterface $clientEntity, array $scopes, $userIdentifier = null) {
|
||||
// TODO: Implement getNewToken() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists a new access token to permanent storage.
|
||||
*
|
||||
* @param AccessTokenEntityInterface $accessTokenEntity
|
||||
*
|
||||
* @throws \League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException
|
||||
*/
|
||||
public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity) {
|
||||
// TODO: Implement persistNewAccessToken() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke an access token.
|
||||
*
|
||||
* @param string $tokenId
|
||||
*/
|
||||
public function revokeAccessToken($tokenId) {
|
||||
// TODO: Implement revokeAccessToken() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the access token has been revoked.
|
||||
*
|
||||
* @param string $tokenId
|
||||
*
|
||||
* @return bool Return true if this token has been revoked
|
||||
*/
|
||||
public function isAccessTokenRevoked($tokenId) {
|
||||
// TODO: Implement isAccessTokenRevoked() method.
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace api\components\OAuth2\Storage;
|
||||
namespace api\components\OAuth2\Repositories;
|
||||
|
||||
use api\components\OAuth2\Entities\AccessTokenEntity;
|
||||
use common\components\Redis\Key;
|
26
api/components/OAuth2/Repositories/AuthCodeRepository.php
Normal file
26
api/components/OAuth2/Repositories/AuthCodeRepository.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\components\OAuth2\Repositories;
|
||||
|
||||
use api\components\OAuth2\Entities\AuthCodeEntity;
|
||||
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
|
||||
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
|
||||
|
||||
class AuthCodeRepository implements AuthCodeRepositoryInterface {
|
||||
|
||||
public function getNewAuthCode(): AuthCodeEntityInterface {
|
||||
return new AuthCodeEntity();
|
||||
}
|
||||
|
||||
public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity): void {
|
||||
}
|
||||
|
||||
public function revokeAuthCode($codeId): void {
|
||||
}
|
||||
|
||||
public function isAuthCodeRevoked($codeId): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace api\components\OAuth2\Storage;
|
||||
namespace api\components\OAuth2\Repositories;
|
||||
|
||||
use api\components\OAuth2\Entities\AuthCodeEntity;
|
||||
use common\components\Redis\Key;
|
41
api/components/OAuth2/Repositories/ClientRepository.php
Normal file
41
api/components/OAuth2/Repositories/ClientRepository.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\components\OAuth2\Repositories;
|
||||
|
||||
use api\components\OAuth2\Entities\ClientEntity;
|
||||
use common\models\OauthClient;
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
|
||||
|
||||
class ClientRepository implements ClientRepositoryInterface {
|
||||
|
||||
public function getClientEntity($clientId): ?ClientEntityInterface {
|
||||
$client = $this->findModel($clientId);
|
||||
if ($client === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ClientEntity($client->id, $client->name, $client->redirect_uri, (bool)$client->is_trusted);
|
||||
}
|
||||
|
||||
public function validateClient($clientId, $clientSecret, $grantType): bool {
|
||||
$client = $this->findModel($clientId);
|
||||
if ($client === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($clientSecret !== null && $clientSecret !== $client->secret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: there is missing behavior of checking redirectUri. Is it now bundled into grant?
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function findModel(string $id): ?OauthClient {
|
||||
return OauthClient::findOne(['id' => $id]);
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace api\components\OAuth2\Storage;
|
||||
namespace api\components\OAuth2\Repositories;
|
||||
|
||||
use api\components\OAuth2\Entities\ClientEntity;
|
||||
use api\components\OAuth2\Entities\SessionEntity;
|
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\components\OAuth2\Repositories;
|
||||
|
||||
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
||||
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||
|
||||
class RefreshTokenRepository implements RefreshTokenRepositoryInterface {
|
||||
|
||||
/**
|
||||
* Creates a new refresh token
|
||||
*
|
||||
* @return RefreshTokenEntityInterface|null
|
||||
*/
|
||||
public function getNewRefreshToken(): RefreshTokenEntityInterface {
|
||||
// TODO: Implement getNewRefreshToken() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new refresh token_name.
|
||||
*
|
||||
* @param RefreshTokenEntityInterface $refreshTokenEntity
|
||||
*
|
||||
* @throws \League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException
|
||||
*/
|
||||
public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity) {
|
||||
// TODO: Implement persistNewRefreshToken() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke the refresh token.
|
||||
*
|
||||
* @param string $tokenId
|
||||
*/
|
||||
public function revokeRefreshToken($tokenId) {
|
||||
// TODO: Implement revokeRefreshToken() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the refresh token has been revoked.
|
||||
*
|
||||
* @param string $tokenId
|
||||
*
|
||||
* @return bool Return true if this token has been revoked
|
||||
*/
|
||||
public function isRefreshTokenRevoked($tokenId) {
|
||||
// TODO: Implement isRefreshTokenRevoked() method.
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace api\components\OAuth2\Storage;
|
||||
namespace api\components\OAuth2\Repositories;
|
||||
|
||||
use api\components\OAuth2\Entities\RefreshTokenEntity;
|
||||
use common\components\Redis\Key;
|
37
api/components/OAuth2/Repositories/ScopeRepository.php
Normal file
37
api/components/OAuth2/Repositories/ScopeRepository.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\components\OAuth2\Repositories;
|
||||
|
||||
use api\components\OAuth2\Entities\ScopeEntity;
|
||||
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
|
||||
|
||||
class ScopeRepository implements ScopeRepositoryInterface {
|
||||
|
||||
public function getScopeEntityByIdentifier($identifier): ?ScopeEntityInterface {
|
||||
// TODO: validate not exists scopes
|
||||
return new ScopeEntity($identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a client, grant type and optional user identifier validate the set of scopes requested are valid and optionally
|
||||
* append additional scopes or remove requested scopes.
|
||||
*
|
||||
* @param ScopeEntityInterface $scopes
|
||||
* @param string $grantType
|
||||
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $clientEntity
|
||||
* @param null|string $userIdentifier
|
||||
*
|
||||
* @return ScopeEntityInterface
|
||||
*/
|
||||
public function finalizeScopes(
|
||||
array $scopes,
|
||||
$grantType,
|
||||
\League\OAuth2\Server\Entities\ClientEntityInterface $clientEntity,
|
||||
$userIdentifier = null
|
||||
): array {
|
||||
// TODO: Implement finalizeScopes() method.
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace api\components\OAuth2\Storage;
|
||||
namespace api\components\OAuth2\Repositories;
|
||||
|
||||
use api\components\OAuth2\Entities\ClientEntity;
|
||||
use api\components\OAuth2\Entities\ScopeEntity;
|
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace api\components\OAuth2\Storage;
|
||||
namespace api\components\OAuth2\Repositories;
|
||||
|
||||
use api\components\OAuth2\Entities\AuthCodeEntity;
|
||||
use api\components\OAuth2\Entities\SessionEntity;
|
@ -28,6 +28,7 @@ class OAuth2Identity implements IdentityInterface {
|
||||
*/
|
||||
public static function findIdentityByAccessToken($token, $type = null): IdentityInterface {
|
||||
/** @var AccessTokenEntity|null $model */
|
||||
// TODO: rework
|
||||
$model = Yii::$app->oauth->getAccessTokenStorage()->get($token);
|
||||
if ($model === null) {
|
||||
throw new UnauthorizedHttpException('Incorrect token');
|
||||
|
@ -1,4 +1,6 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\modules\oauth\controllers;
|
||||
|
||||
use api\controllers\Controller;
|
||||
@ -55,10 +57,7 @@ class AuthorizationController extends Controller {
|
||||
}
|
||||
|
||||
private function createOauthProcess(): OauthProcess {
|
||||
$server = Yii::$app->oauth->authServer;
|
||||
$server->setRequest(null); // Enforce request recreation (test environment bug)
|
||||
|
||||
return new OauthProcess($server);
|
||||
return new OauthProcess(Yii::$app->oauth->authServer);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,19 +1,18 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\modules\oauth\models;
|
||||
|
||||
use api\components\OAuth2\Exception\AcceptRequiredException;
|
||||
use api\components\OAuth2\Exception\AccessDeniedException;
|
||||
use api\components\OAuth2\Grants\AuthCodeGrant;
|
||||
use api\components\OAuth2\Grants\AuthorizeParams;
|
||||
use api\rbac\Permissions as P;
|
||||
use common\models\Account;
|
||||
use common\models\OauthClient;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use GuzzleHttp\Psr7\ServerRequest;
|
||||
use League\OAuth2\Server\AuthorizationServer;
|
||||
use League\OAuth2\Server\Exception\InvalidGrantException;
|
||||
use League\OAuth2\Server\Exception\OAuthException;
|
||||
use League\OAuth2\Server\Grant\GrantTypeInterface;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Yii;
|
||||
use yii\helpers\ArrayHelper;
|
||||
|
||||
class OauthProcess {
|
||||
|
||||
@ -50,16 +49,13 @@ class OauthProcess {
|
||||
*/
|
||||
public function validate(): array {
|
||||
try {
|
||||
$authParams = $this->getAuthorizationCodeGrant()->checkAuthorizeParams();
|
||||
$client = $authParams->getClient();
|
||||
$request = $this->getRequest();
|
||||
$authRequest = $this->server->validateAuthorizationRequest($request);
|
||||
$client = $authRequest->getClient();
|
||||
/** @var OauthClient $clientModel */
|
||||
$clientModel = $this->findClient($client->getId());
|
||||
$response = $this->buildSuccessResponse(
|
||||
Yii::$app->request->getQueryParams(),
|
||||
$clientModel,
|
||||
$authParams->getScopes()
|
||||
);
|
||||
} catch (OAuthException $e) {
|
||||
$clientModel = $this->findClient($client->getIdentifier());
|
||||
$response = $this->buildSuccessResponse($request, $clientModel, $authRequest->getScopes());
|
||||
} catch (OAuthServerException $e) {
|
||||
$response = $this->buildErrorResponse($e);
|
||||
}
|
||||
|
||||
@ -88,33 +84,37 @@ class OauthProcess {
|
||||
public function complete(): array {
|
||||
try {
|
||||
Yii::$app->statsd->inc('oauth.complete.attempt');
|
||||
$grant = $this->getAuthorizationCodeGrant();
|
||||
$authParams = $grant->checkAuthorizeParams();
|
||||
|
||||
$request = $this->getRequest();
|
||||
$authRequest = $this->server->validateAuthorizationRequest($request);
|
||||
/** @var Account $account */
|
||||
$account = Yii::$app->user->identity->getAccount();
|
||||
/** @var \common\models\OauthClient $clientModel */
|
||||
$clientModel = $this->findClient($authParams->getClient()->getId());
|
||||
/** @var OauthClient $clientModel */
|
||||
$clientModel = $this->findClient($authRequest->getClient()->getIdentifier());
|
||||
|
||||
if (!$this->canAutoApprove($account, $clientModel, $authParams)) {
|
||||
if (!$this->canAutoApprove($account, $clientModel, $authRequest)) {
|
||||
Yii::$app->statsd->inc('oauth.complete.approve_required');
|
||||
$isAccept = Yii::$app->request->post('accept');
|
||||
if ($isAccept === null) {
|
||||
throw new AcceptRequiredException();
|
||||
|
||||
$accept = ((array)$request->getParsedBody())['accept'] ?? null;
|
||||
if ($accept === null) {
|
||||
throw $this->createAcceptRequiredException();
|
||||
}
|
||||
|
||||
if (!$isAccept) {
|
||||
throw new AccessDeniedException($authParams->getRedirectUri());
|
||||
if (!in_array($accept, [1, '1', true, 'true'], true)) {
|
||||
throw OAuthServerException::accessDenied(null, $authRequest->getRedirectUri());
|
||||
}
|
||||
}
|
||||
|
||||
$redirectUri = $grant->newAuthorizeRequest('user', $account->id, $authParams);
|
||||
$responseObj = $this->server->completeAuthorizationRequest($authRequest, new Response(200));
|
||||
|
||||
$response = [
|
||||
'success' => true,
|
||||
'redirectUri' => $redirectUri,
|
||||
'redirectUri' => $responseObj->getHeader('Location'), // TODO: ensure that this is correct type and behavior
|
||||
];
|
||||
|
||||
Yii::$app->statsd->inc('oauth.complete.success');
|
||||
} catch (OAuthException $e) {
|
||||
if (!$e instanceof AcceptRequiredException) {
|
||||
} catch (OAuthServerException $e) {
|
||||
if ($e->getErrorType() === 'accept_required') {
|
||||
Yii::$app->statsd->inc('oauth.complete.fail');
|
||||
}
|
||||
|
||||
@ -146,19 +146,28 @@ class OauthProcess {
|
||||
* @return array
|
||||
*/
|
||||
public function getToken(): array {
|
||||
$grantType = Yii::$app->request->post('grant_type', 'null');
|
||||
$request = $this->getRequest();
|
||||
$params = (array)$request->getParsedBody();
|
||||
$grantType = $params['grant_type'] ?? 'null';
|
||||
try {
|
||||
Yii::$app->statsd->inc("oauth.issueToken_{$grantType}.attempt");
|
||||
$response = $this->server->issueAccessToken();
|
||||
$clientId = Yii::$app->request->post('client_id');
|
||||
|
||||
$responseObj = new Response(200);
|
||||
$this->server->respondToAccessTokenRequest($request, $responseObj);
|
||||
$clientId = $params['client_id'];
|
||||
|
||||
// TODO: build response from the responseObj
|
||||
$response = [];
|
||||
|
||||
Yii::$app->statsd->inc("oauth.issueToken_client.{$clientId}");
|
||||
Yii::$app->statsd->inc("oauth.issueToken_{$grantType}.success");
|
||||
} catch (OAuthException $e) {
|
||||
} catch (OAuthServerException $e) {
|
||||
Yii::$app->statsd->inc("oauth.issueToken_{$grantType}.fail");
|
||||
Yii::$app->response->statusCode = $e->httpStatusCode;
|
||||
Yii::$app->response->statusCode = $e->getHttpStatusCode();
|
||||
|
||||
$response = [
|
||||
'error' => $e->errorType,
|
||||
'message' => $e->getMessage(),
|
||||
'error' => $e->getErrorType(),
|
||||
'message' => $e->getMessage(), // TODO: use hint field?
|
||||
];
|
||||
}
|
||||
|
||||
@ -166,7 +175,7 @@ class OauthProcess {
|
||||
}
|
||||
|
||||
private function findClient(string $clientId): ?OauthClient {
|
||||
return OauthClient::findOne($clientId);
|
||||
return OauthClient::findOne(['id' => $clientId]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -175,11 +184,11 @@ class OauthProcess {
|
||||
*
|
||||
* @param Account $account
|
||||
* @param OauthClient $client
|
||||
* @param AuthorizeParams $oauthParams
|
||||
* @param AuthorizationRequest $request
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function canAutoApprove(Account $account, OauthClient $client, AuthorizeParams $oauthParams): bool {
|
||||
private function canAutoApprove(Account $account, OauthClient $client, AuthorizationRequest $request): bool {
|
||||
if ($client->is_trusted) {
|
||||
return true;
|
||||
}
|
||||
@ -188,7 +197,7 @@ class OauthProcess {
|
||||
$session = $account->getOauthSessions()->andWhere(['client_id' => $client->id])->one();
|
||||
if ($session !== null) {
|
||||
$existScopes = $session->getScopes()->members();
|
||||
if (empty(array_diff(array_keys($oauthParams->getScopes()), $existScopes))) {
|
||||
if (empty(array_diff(array_keys($request->getScopes()), $existScopes))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -197,17 +206,17 @@ class OauthProcess {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $queryParams
|
||||
* @param ServerRequestInterface $request
|
||||
* @param OauthClient $client
|
||||
* @param \api\components\OAuth2\Entities\ScopeEntity[] $scopes
|
||||
* @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function buildSuccessResponse(array $queryParams, OauthClient $client, array $scopes): array {
|
||||
private function buildSuccessResponse(ServerRequestInterface $request, OauthClient $client, array $scopes): array {
|
||||
return [
|
||||
'success' => true,
|
||||
// We return only those keys which are related to the OAuth2 standard parameters
|
||||
'oAuth' => array_intersect_key($queryParams, array_flip([
|
||||
'oAuth' => array_intersect_key($request->getQueryParams(), array_flip([
|
||||
'client_id',
|
||||
'redirect_uri',
|
||||
'response_type',
|
||||
@ -217,55 +226,57 @@ class OauthProcess {
|
||||
'client' => [
|
||||
'id' => $client->id,
|
||||
'name' => $client->name,
|
||||
'description' => ArrayHelper::getValue($queryParams, 'description', $client->description),
|
||||
'description' => $request->getQueryParams()['description'] ?? $client->description,
|
||||
],
|
||||
'session' => [
|
||||
'scopes' => $this->fixScopesNames(array_keys($scopes)),
|
||||
'scopes' => $this->buildScopesArray($scopes),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function fixScopesNames(array $scopes): array {
|
||||
foreach ($scopes as &$scope) {
|
||||
if (isset(self::INTERNAL_PERMISSIONS_TO_PUBLIC_SCOPES[$scope])) {
|
||||
$scope = self::INTERNAL_PERMISSIONS_TO_PUBLIC_SCOPES[$scope];
|
||||
}
|
||||
/**
|
||||
* @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes
|
||||
* @return array
|
||||
*/
|
||||
private function buildScopesArray(array $scopes): array {
|
||||
$result = [];
|
||||
foreach ($scopes as $scope) {
|
||||
$result[] = self::INTERNAL_PERMISSIONS_TO_PUBLIC_SCOPES[$scope->getIdentifier()] ?? $scope->getIdentifier();
|
||||
}
|
||||
|
||||
return $scopes;
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function buildErrorResponse(OAuthException $e): array {
|
||||
private function buildErrorResponse(OAuthServerException $e): array {
|
||||
$response = [
|
||||
'success' => false,
|
||||
'error' => $e->errorType,
|
||||
'parameter' => $e->parameter,
|
||||
'statusCode' => $e->httpStatusCode,
|
||||
'error' => $e->getErrorType(),
|
||||
// 'parameter' => $e->parameter, // TODO: if this is necessary, the parameter can be extracted from the hint
|
||||
'statusCode' => $e->getHttpStatusCode(),
|
||||
];
|
||||
|
||||
if ($e->shouldRedirect()) {
|
||||
if ($e->hasRedirect()) {
|
||||
$response['redirectUri'] = $e->getRedirectUri();
|
||||
}
|
||||
|
||||
if ($e->httpStatusCode !== 200) {
|
||||
Yii::$app->response->setStatusCode($e->httpStatusCode);
|
||||
if ($e->getHttpStatusCode() !== 200) {
|
||||
Yii::$app->response->setStatusCode($e->getHttpStatusCode());
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function getGrant(string $grantType = null): GrantTypeInterface {
|
||||
return $this->server->getGrantType($grantType ?? Yii::$app->request->get('grant_type'));
|
||||
private function getRequest(): ServerRequestInterface {
|
||||
return ServerRequest::fromGlobals();
|
||||
}
|
||||
|
||||
private function getAuthorizationCodeGrant(): AuthCodeGrant {
|
||||
/** @var GrantTypeInterface $grantType */
|
||||
$grantType = $this->getGrant('authorization_code');
|
||||
if (!$grantType instanceof AuthCodeGrant) {
|
||||
throw new InvalidGrantException('authorization_code grant have invalid realisation');
|
||||
}
|
||||
|
||||
return $grantType;
|
||||
private function createAcceptRequiredException(): OAuthServerException {
|
||||
return new OAuthServerException(
|
||||
'Client must accept authentication request.',
|
||||
0,
|
||||
'accept_required',
|
||||
401
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,40 +1,72 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\tests\_pages;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
class OauthRoute extends BasePage {
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function validate(array $queryParams): void {
|
||||
$this->getActor()->sendGET('/api/oauth2/v1/validate', $queryParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function complete(array $queryParams = [], array $postParams = []): void {
|
||||
$this->getActor()->sendPOST('/api/oauth2/v1/complete?' . http_build_query($queryParams), $postParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function issueToken(array $postParams = []): void {
|
||||
$this->getActor()->sendPOST('/api/oauth2/v1/token', $postParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function createClient(string $type, array $postParams): void {
|
||||
$this->getActor()->sendPOST('/api/v1/oauth2/' . $type, $postParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function updateClient(string $clientId, array $params): void {
|
||||
$this->getActor()->sendPUT('/api/v1/oauth2/' . $clientId, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function deleteClient(string $clientId): void {
|
||||
$this->getActor()->sendDELETE('/api/v1/oauth2/' . $clientId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function resetClient(string $clientId, bool $regenerateSecret = false): void {
|
||||
$this->getActor()->sendPOST("/api/v1/oauth2/{$clientId}/reset" . ($regenerateSecret ? '?regenerateSecret' : ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function getClient(string $clientId): void {
|
||||
$this->getActor()->sendGET("/api/v1/oauth2/{$clientId}");
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function getPerAccount(int $accountId): void {
|
||||
$this->getActor()->sendGET("/api/v1/accounts/{$accountId}/oauth2/clients");
|
||||
}
|
||||
|
@ -1,13 +1,15 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\tests\functional\_steps;
|
||||
|
||||
use api\components\OAuth2\Storage\ScopeStorage as S;
|
||||
use api\components\OAuth2\Repositories\ScopeStorage as S;
|
||||
use api\tests\_pages\OauthRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class OauthSteps extends FunctionalTester {
|
||||
|
||||
public function getAuthCode(array $permissions = []) {
|
||||
public function getAuthCode(array $permissions = []): string {
|
||||
$this->amAuthenticated();
|
||||
$route = new OauthRoute($this);
|
||||
$route->complete([
|
||||
@ -23,21 +25,21 @@ class OauthSteps extends FunctionalTester {
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
public function getAccessToken(array $permissions = []) {
|
||||
public function getAccessToken(array $permissions = []): string {
|
||||
$authCode = $this->getAuthCode($permissions);
|
||||
$response = $this->issueToken($authCode);
|
||||
|
||||
return $response['access_token'];
|
||||
}
|
||||
|
||||
public function getRefreshToken(array $permissions = []) {
|
||||
public function getRefreshToken(array $permissions = []): string {
|
||||
$authCode = $this->getAuthCode(array_merge([S::OFFLINE_ACCESS], $permissions));
|
||||
$response = $this->issueToken($authCode);
|
||||
|
||||
return $response['refresh_token'];
|
||||
}
|
||||
|
||||
public function issueToken($authCode) {
|
||||
public function issueToken($authCode): array {
|
||||
$route = new OauthRoute($this);
|
||||
$route->issueToken([
|
||||
'code' => $authCode,
|
||||
@ -50,7 +52,7 @@ class OauthSteps extends FunctionalTester {
|
||||
return json_decode($this->grabResponse(), true);
|
||||
}
|
||||
|
||||
public function getAccessTokenByClientCredentialsGrant(array $permissions = [], $useTrusted = true) {
|
||||
public function getAccessTokenByClientCredentialsGrant(array $permissions = [], $useTrusted = true): string {
|
||||
$route = new OauthRoute($this);
|
||||
$route->issueToken([
|
||||
'client_id' => $useTrusted ? 'trusted-client' : 'default-client',
|
||||
|
@ -1,4 +1,6 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\tests\functional\oauth;
|
||||
|
||||
use api\rbac\Permissions as P;
|
||||
@ -18,61 +20,6 @@ class AuthCodeCest {
|
||||
|
||||
public function testValidateRequest(FunctionalTester $I) {
|
||||
$this->testOauthParamsValidation($I, 'validate');
|
||||
|
||||
$I->wantTo('validate and obtain information about new auth request');
|
||||
$this->route->validate($this->buildQueryParams(
|
||||
'ely',
|
||||
'http://ely.by',
|
||||
'code',
|
||||
[P::MINECRAFT_SERVER_SESSION, 'account_info', 'account_email'],
|
||||
'test-state'
|
||||
));
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
'oAuth' => [
|
||||
'client_id' => 'ely',
|
||||
'redirect_uri' => 'http://ely.by',
|
||||
'response_type' => 'code',
|
||||
'scope' => 'minecraft_server_session,account_info,account_email',
|
||||
'state' => 'test-state',
|
||||
],
|
||||
'client' => [
|
||||
'id' => 'ely',
|
||||
'name' => 'Ely.by',
|
||||
'description' => 'Всем знакомое елуби',
|
||||
],
|
||||
'session' => [
|
||||
'scopes' => [
|
||||
'minecraft_server_session',
|
||||
'account_info',
|
||||
'account_email',
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testValidateWithDescriptionReplaceRequest(FunctionalTester $I) {
|
||||
$I->amAuthenticated();
|
||||
$I->wantTo('validate and get information with description replacement');
|
||||
$this->route->validate($this->buildQueryParams(
|
||||
'ely',
|
||||
'http://ely.by',
|
||||
'code',
|
||||
null,
|
||||
null,
|
||||
[
|
||||
'description' => 'all familiar eliby',
|
||||
]
|
||||
));
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'client' => [
|
||||
'description' => 'all familiar eliby',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testCompleteValidationAction(FunctionalTester $I) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace api\tests\functional\oauth;
|
||||
|
||||
use api\components\OAuth2\Storage\ScopeStorage as S;
|
||||
use api\components\OAuth2\Repositories\ScopeStorage as S;
|
||||
use api\rbac\Permissions as P;
|
||||
use api\tests\_pages\OauthRoute;
|
||||
use api\tests\functional\_steps\OauthSteps;
|
||||
|
62
api/tests/functional/oauth/ValidateCest.php
Normal file
62
api/tests/functional/oauth/ValidateCest.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\tests\functional\oauth;
|
||||
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class ValidateCest {
|
||||
|
||||
// TODO: validate case, when scopes are passed with commas
|
||||
|
||||
public function completelyValidateValidRequest(FunctionalTester $I) {
|
||||
$I->wantTo('validate and obtain information about new oauth request');
|
||||
$I->sendGET('/api/oauth2/v1/validate', [
|
||||
'client_id' => 'ely',
|
||||
'redirect_uri' => 'http://ely.by',
|
||||
'response_type' => 'code',
|
||||
'scope' => 'minecraft_server_session account_info account_email',
|
||||
'state' => 'test-state',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
'oAuth' => [
|
||||
'client_id' => 'ely',
|
||||
'redirect_uri' => 'http://ely.by',
|
||||
'response_type' => 'code',
|
||||
'scope' => 'minecraft_server_session account_info account_email',
|
||||
'state' => 'test-state',
|
||||
],
|
||||
'client' => [
|
||||
'id' => 'ely',
|
||||
'name' => 'Ely.by',
|
||||
'description' => 'Всем знакомое елуби',
|
||||
],
|
||||
'session' => [
|
||||
'scopes' => [
|
||||
'minecraft_server_session',
|
||||
'account_info',
|
||||
'account_email',
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function completelyValidateValidRequestWithOverriddenDescription(FunctionalTester $I) {
|
||||
$I->wantTo('validate and get information with description replacement');
|
||||
$I->sendGET('/api/oauth2/v1/validate', [
|
||||
'client_id' => 'ely',
|
||||
'redirect_uri' => 'http://ely.by',
|
||||
'response_type' => 'code',
|
||||
'description' => 'all familiar eliby',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'client' => [
|
||||
'description' => 'all familiar eliby',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
@ -13,13 +13,13 @@ use yii\db\ActiveRecord;
|
||||
* @property string $type
|
||||
* @property string $name
|
||||
* @property string $description
|
||||
* @property string $redirect_uri
|
||||
* @property string|null $redirect_uri
|
||||
* @property string $website_url
|
||||
* @property string $minecraft_server_ip
|
||||
* @property integer $account_id
|
||||
* @property bool $is_trusted
|
||||
* @property bool $is_deleted
|
||||
* @property integer $created_at
|
||||
* @property int $created_at
|
||||
*
|
||||
* Behaviors:
|
||||
* @property Account|null $account
|
||||
|
@ -69,7 +69,8 @@ class OauthSession extends ActiveRecord {
|
||||
}
|
||||
|
||||
public function removeRefreshToken(): void {
|
||||
/** @var \api\components\OAuth2\Storage\RefreshTokenStorage $refreshTokensStorage */
|
||||
/** @var \api\components\OAuth2\Repositories\RefreshTokenStorage $refreshTokensStorage */
|
||||
// TODO: rework
|
||||
$refreshTokensStorage = Yii::$app->oauth->getRefreshTokenStorage();
|
||||
$refreshTokensSet = $refreshTokensStorage->sessionHash($this->id);
|
||||
foreach ($refreshTokensSet->members() as $refreshTokenId) {
|
||||
|
@ -5,7 +5,7 @@
|
||||
"type": "project",
|
||||
"minimum-stability": "stable",
|
||||
"require": {
|
||||
"php": "^7.2",
|
||||
"php": "^7.3",
|
||||
"ext-intl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-libxml": "*",
|
||||
@ -19,7 +19,7 @@
|
||||
"goaop/framework": "^2.2.0",
|
||||
"guzzlehttp/guzzle": "^6.0.0",
|
||||
"lcobucci/jwt": "^3.3",
|
||||
"league/oauth2-server": "^4.1",
|
||||
"league/oauth2-server": "dev-adaptation",
|
||||
"mito/yii2-sentry": "^1.0",
|
||||
"nesbot/carbon": "^2.22",
|
||||
"paragonie/constant_time_encoding": "^2.0",
|
||||
@ -54,6 +54,10 @@
|
||||
{
|
||||
"type": "composer",
|
||||
"url": "https://asset-packagist.org"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/elyby/oauth2-server.git"
|
||||
}
|
||||
],
|
||||
"config": {
|
||||
|
171
composer.lock
generated
171
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "35095ab389bcc73cacbafceffa74fb71",
|
||||
"content-hash": "35a16287a6dc45c16e0553022aa34f5b",
|
||||
"packages": [
|
||||
{
|
||||
"name": "bacon/bacon-qr-code",
|
||||
@ -305,6 +305,69 @@
|
||||
],
|
||||
"time": "2018-04-13T00:48:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "defuse/php-encryption",
|
||||
"version": "v2.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/defuse/php-encryption.git",
|
||||
"reference": "0f407c43b953d571421e0020ba92082ed5fb7620"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/defuse/php-encryption/zipball/0f407c43b953d571421e0020ba92082ed5fb7620",
|
||||
"reference": "0f407c43b953d571421e0020ba92082ed5fb7620",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-openssl": "*",
|
||||
"paragonie/random_compat": ">= 2",
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"nikic/php-parser": "^2.0|^3.0|^4.0",
|
||||
"phpunit/phpunit": "^4|^5"
|
||||
},
|
||||
"bin": [
|
||||
"bin/generate-defuse-key"
|
||||
],
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Defuse\\Crypto\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Hornby",
|
||||
"email": "taylor@defuse.ca",
|
||||
"homepage": "https://defuse.ca/"
|
||||
},
|
||||
{
|
||||
"name": "Scott Arciszewski",
|
||||
"email": "info@paragonie.com",
|
||||
"homepage": "https://paragonie.com"
|
||||
}
|
||||
],
|
||||
"description": "Secure PHP Encryption Library",
|
||||
"keywords": [
|
||||
"aes",
|
||||
"authenticated encryption",
|
||||
"cipher",
|
||||
"crypto",
|
||||
"cryptography",
|
||||
"encrypt",
|
||||
"encryption",
|
||||
"openssl",
|
||||
"security",
|
||||
"symmetric key cryptography"
|
||||
],
|
||||
"time": "2018-07-24T23:27:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/annotations",
|
||||
"version": "v1.6.0",
|
||||
@ -1215,30 +1278,37 @@
|
||||
},
|
||||
{
|
||||
"name": "league/oauth2-server",
|
||||
"version": "4.1.7",
|
||||
"version": "dev-adaptation",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/oauth2-server.git",
|
||||
"reference": "138524984ac472652c69399529a35b6595cf22d3"
|
||||
"url": "https://github.com/elyby/oauth2-server.git",
|
||||
"reference": "08e470e81a20896109704bac4b7c24781797dfc3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/oauth2-server/zipball/138524984ac472652c69399529a35b6595cf22d3",
|
||||
"reference": "138524984ac472652c69399529a35b6595cf22d3",
|
||||
"url": "https://api.github.com/repos/elyby/oauth2-server/zipball/08e470e81a20896109704bac4b7c24781797dfc3",
|
||||
"reference": "08e470e81a20896109704bac4b7c24781797dfc3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"league/event": "~2.1",
|
||||
"php": ">=5.4.0",
|
||||
"symfony/http-foundation": "~2.4|~3.0"
|
||||
"defuse/php-encryption": "^2.2.1",
|
||||
"ext-json": "*",
|
||||
"ext-openssl": "*",
|
||||
"lcobucci/jwt": "^3.3.1",
|
||||
"league/event": "^2.2",
|
||||
"php": ">=7.1.0",
|
||||
"psr/http-message": "^1.0.1"
|
||||
},
|
||||
"replace": {
|
||||
"league/oauth2server": "*",
|
||||
"lncd/oauth2": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "0.9.*",
|
||||
"phpunit/phpunit": "4.3.*"
|
||||
"phpstan/phpstan": "^0.11.8",
|
||||
"phpstan/phpstan-phpunit": "^0.11.2",
|
||||
"phpunit/phpunit": "^7.5.13 || ^8.2.3",
|
||||
"roave/security-advisories": "dev-master",
|
||||
"zendframework/zend-diactoros": "^2.1.2"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -1246,7 +1316,11 @@
|
||||
"League\\OAuth2\\Server\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"LeagueTests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
@ -1256,14 +1330,21 @@
|
||||
"email": "hello@alexbilbie.com",
|
||||
"homepage": "http://www.alexbilbie.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Andy Millington",
|
||||
"email": "andrew@noexceptions.io",
|
||||
"homepage": "https://www.noexceptions.io",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "A lightweight and powerful OAuth 2.0 authorization and resource server library with support for all the core specification grants. This library will allow you to secure your API with OAuth and allow your applications users to approve apps that want to access their data from your API.",
|
||||
"homepage": "http://oauth2.thephpleague.com/",
|
||||
"homepage": "https://oauth2.thephpleague.com/",
|
||||
"keywords": [
|
||||
"Authentication",
|
||||
"api",
|
||||
"auth",
|
||||
"auth",
|
||||
"authentication",
|
||||
"authorisation",
|
||||
"authorization",
|
||||
"oauth",
|
||||
@ -1275,7 +1356,10 @@
|
||||
"secure",
|
||||
"server"
|
||||
],
|
||||
"time": "2018-06-23T16:27:31+00:00"
|
||||
"support": {
|
||||
"source": "https://github.com/elyby/oauth2-server/tree/adaptation"
|
||||
},
|
||||
"time": "2019-08-22T21:17:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mito/yii2-sentry",
|
||||
@ -1916,60 +2000,6 @@
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-06-28T13:16:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-foundation",
|
||||
"version": "v3.4.22",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-foundation.git",
|
||||
"reference": "9a81d2330ea255ded06a69b4f7fb7804836e7a05"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/9a81d2330ea255ded06a69b4f7fb7804836e7a05",
|
||||
"reference": "9a81d2330ea255ded06a69b4f7fb7804836e7a05",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.5.9|>=7.0.8",
|
||||
"symfony/polyfill-mbstring": "~1.1",
|
||||
"symfony/polyfill-php70": "~1.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/expression-language": "~2.8|~3.0|~4.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.4-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\HttpFoundation\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony HttpFoundation Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-01-27T09:04:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v4.2.8",
|
||||
@ -6653,12 +6683,13 @@
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"league/oauth2-server": 20,
|
||||
"roave/security-advisories": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": "^7.2",
|
||||
"php": "^7.3",
|
||||
"ext-intl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-libxml": "*",
|
||||
|
Loading…
Reference in New Issue
Block a user