From 5ed6f0ce86de42b56efa31e07097b7556c26b4d1 Mon Sep 17 00:00:00 2001
From: ErickSkrauch <erickskrauch@yandex.ru>
Date: Fri, 6 Dec 2024 01:34:09 +0100
Subject: [PATCH] Move OAuth module from API to common and solve PHPStan's
 errors

---
 .../OAuth2/Events/RequestedRefreshToken.php   |  10 -
 .../OAuth2/Grants/ClientCredentialsGrant.php  |  12 --
 .../ResponseTypes/BearerTokenResponse.php     |  12 --
 api/components/Tokens/TokenReader.php         |   3 +
 api/config/config.php                         |   3 -
 .../controllers/AuthorizationController.php   |  22 +-
 api/modules/oauth/models/OauthProcess.php     |  43 ++--
 api/tests/functional/_steps/OauthSteps.php    |   2 +-
 .../OAuth2/Entities/AccessTokenEntityTest.php |   2 +-
 autocompletion.php                            |   1 -
 .../OAuth2/AuthorizationServerFactory.php     |  16 +-
 .../components/OAuth2/CryptTrait.php          |   6 +-
 .../OAuth2/Entities/AccessTokenEntity.php     |   2 +-
 .../OAuth2/Entities/AuthCodeEntity.php        |   4 +-
 .../OAuth2/Entities/ClientEntity.php          |   8 +-
 .../OAuth2/Entities/ScopeEntity.php           |   7 +-
 .../components/OAuth2/Entities/UserEntity.php |   4 +-
 .../OAuth2/Events/RequestedRefreshToken.php   |  10 +
 .../OAuth2/Grants/AuthCodeGrant.php           |  22 +-
 .../OAuth2/Grants/ClientCredentialsGrant.php  |  12 ++
 .../OAuth2/Grants/RefreshTokenGrant.php       |  28 +--
 .../components/OAuth2/Keys/EmptyKey.php       |   4 +-
 .../Repositories/AccessTokenRepository.php    |  21 +-
 .../Repositories/AuthCodeRepository.php       |  10 +-
 .../OAuth2/Repositories/ClientRepository.php  |  17 +-
 .../Repositories/EmptyScopeRepository.php     |   8 +-
 .../Repositories/InternalScopeRepository.php  |  12 +-
 .../Repositories/PublicScopeRepository.php    |  10 +-
 .../Repositories/RefreshTokenRepository.php   |   8 +-
 .../ResponseTypes/BearerTokenResponse.php     |  12 ++
 common/config/config.php                      |   1 +
 phpstan-baseline.neon                         | 200 ------------------
 32 files changed, 155 insertions(+), 377 deletions(-)
 delete mode 100644 api/components/OAuth2/Events/RequestedRefreshToken.php
 delete mode 100644 api/components/OAuth2/Grants/ClientCredentialsGrant.php
 delete mode 100644 api/components/OAuth2/ResponseTypes/BearerTokenResponse.php
 rename api/components/OAuth2/Component.php => common/components/OAuth2/AuthorizationServerFactory.php (82%)
 rename {api => common}/components/OAuth2/CryptTrait.php (81%)
 rename {api => common}/components/OAuth2/Entities/AccessTokenEntity.php (93%)
 rename {api => common}/components/OAuth2/Entities/AuthCodeEntity.php (75%)
 rename {api => common}/components/OAuth2/Entities/ClientEntity.php (77%)
 rename {api => common}/components/OAuth2/Entities/ScopeEntity.php (66%)
 rename {api => common}/components/OAuth2/Entities/UserEntity.php (71%)
 create mode 100644 common/components/OAuth2/Events/RequestedRefreshToken.php
 rename {api => common}/components/OAuth2/Grants/AuthCodeGrant.php (69%)
 create mode 100644 common/components/OAuth2/Grants/ClientCredentialsGrant.php
 rename {api => common}/components/OAuth2/Grants/RefreshTokenGrant.php (85%)
 rename {api => common}/components/OAuth2/Keys/EmptyKey.php (76%)
 rename {api => common}/components/OAuth2/Repositories/AccessTokenRepository.php (63%)
 rename {api => common}/components/OAuth2/Repositories/AuthCodeRepository.php (57%)
 rename {api => common}/components/OAuth2/Repositories/ClientRepository.php (61%)
 rename {api => common}/components/OAuth2/Repositories/EmptyScopeRepository.php (80%)
 rename {api => common}/components/OAuth2/Repositories/InternalScopeRepository.php (85%)
 rename {api => common}/components/OAuth2/Repositories/PublicScopeRepository.php (86%)
 rename {api => common}/components/OAuth2/Repositories/RefreshTokenRepository.php (63%)
 create mode 100644 common/components/OAuth2/ResponseTypes/BearerTokenResponse.php

diff --git a/api/components/OAuth2/Events/RequestedRefreshToken.php b/api/components/OAuth2/Events/RequestedRefreshToken.php
deleted file mode 100644
index 337633d..0000000
--- a/api/components/OAuth2/Events/RequestedRefreshToken.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace api\components\OAuth2\Events;
-
-use League\OAuth2\Server\EventEmitting\AbstractEvent;
-
-class RequestedRefreshToken extends AbstractEvent {
-
-}
diff --git a/api/components/OAuth2/Grants/ClientCredentialsGrant.php b/api/components/OAuth2/Grants/ClientCredentialsGrant.php
deleted file mode 100644
index fa72668..0000000
--- a/api/components/OAuth2/Grants/ClientCredentialsGrant.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace api\components\OAuth2\Grants;
-
-use api\components\OAuth2\CryptTrait;
-use League\OAuth2\Server\Grant\ClientCredentialsGrant as BaseClientCredentialsGrant;
-
-class ClientCredentialsGrant extends BaseClientCredentialsGrant {
-    use CryptTrait;
-
-}
diff --git a/api/components/OAuth2/ResponseTypes/BearerTokenResponse.php b/api/components/OAuth2/ResponseTypes/BearerTokenResponse.php
deleted file mode 100644
index 63e2c6e..0000000
--- a/api/components/OAuth2/ResponseTypes/BearerTokenResponse.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace api\components\OAuth2\ResponseTypes;
-
-use api\components\OAuth2\CryptTrait;
-use League\OAuth2\Server\ResponseTypes\BearerTokenResponse as BaseBearerTokenResponse;
-
-class BearerTokenResponse extends BaseBearerTokenResponse {
-    use CryptTrait;
-
-}
diff --git a/api/components/Tokens/TokenReader.php b/api/components/Tokens/TokenReader.php
index 282da4d..70b5bf5 100644
--- a/api/components/Tokens/TokenReader.php
+++ b/api/components/Tokens/TokenReader.php
@@ -30,6 +30,9 @@ final readonly class TokenReader {
         return $this->token->claims()->get('client_id', false) ?: null;
     }
 
+    /**
+     * @return list<string>|null
+     */
     public function getScopes(): ?array {
         $scopes = $this->token->claims()->get('scope', false);
         if ($scopes !== false) {
diff --git a/api/config/config.php b/api/config/config.php
index ef44aff..e1dbe1c 100644
--- a/api/config/config.php
+++ b/api/config/config.php
@@ -26,9 +26,6 @@ return [
         'user' => [
             'class' => api\components\User\Component::class,
         ],
-        'oauth' => [
-            'class' => api\components\OAuth2\Component::class,
-        ],
         'tokens' => [
             'class' => api\components\Tokens\Component::class,
             'privateKeyPath' => getenv('JWT_PRIVATE_KEY_PATH') ?: __DIR__ . '/../../data/certs/private.pem',
diff --git a/api/modules/oauth/controllers/AuthorizationController.php b/api/modules/oauth/controllers/AuthorizationController.php
index 351dcf4..03a51e6 100644
--- a/api/modules/oauth/controllers/AuthorizationController.php
+++ b/api/modules/oauth/controllers/AuthorizationController.php
@@ -9,10 +9,20 @@ use api\rbac\Permissions as P;
 use GuzzleHttp\Psr7\ServerRequest;
 use Psr\Http\Message\ServerRequestInterface;
 use Yii;
+use yii\base\Module;
 use yii\filters\AccessControl;
 use yii\helpers\ArrayHelper;
 
-class AuthorizationController extends Controller {
+final class AuthorizationController extends Controller {
+
+    public function __construct(
+        string $id,
+        Module $module,
+        private readonly OauthProcess $oauthProcess,
+        array $config = [],
+    ) {
+        parent::__construct($id, $module, $config);
+    }
 
     public function behaviors(): array {
         return ArrayHelper::merge(Controller::behaviors(), [
@@ -45,19 +55,15 @@ class AuthorizationController extends Controller {
     }
 
     public function actionValidate(): array {
-        return $this->createOauthProcess()->validate($this->getServerRequest());
+        return $this->oauthProcess->validate($this->getServerRequest());
     }
 
     public function actionComplete(): array {
-        return $this->createOauthProcess()->complete($this->getServerRequest());
+        return $this->oauthProcess->complete($this->getServerRequest());
     }
 
     public function actionToken(): array {
-        return $this->createOauthProcess()->getToken($this->getServerRequest());
-    }
-
-    private function createOauthProcess(): OauthProcess {
-        return new OauthProcess(Yii::$app->oauth->getAuthServer());
+        return $this->oauthProcess->getToken($this->getServerRequest());
     }
 
     private function getServerRequest(): ServerRequestInterface {
diff --git a/api/modules/oauth/models/OauthProcess.php b/api/modules/oauth/models/OauthProcess.php
index a5e320c..85ce25f 100644
--- a/api/modules/oauth/models/OauthProcess.php
+++ b/api/modules/oauth/models/OauthProcess.php
@@ -3,9 +3,9 @@ declare(strict_types=1);
 
 namespace api\modules\oauth\models;
 
-use api\components\OAuth2\Entities\UserEntity;
-use api\components\OAuth2\Events\RequestedRefreshToken;
 use api\rbac\Permissions as P;
+use common\components\OAuth2\Entities\UserEntity;
+use common\components\OAuth2\Events\RequestedRefreshToken;
 use common\models\Account;
 use common\models\OauthClient;
 use common\models\OauthSession;
@@ -18,14 +18,16 @@ use Psr\Http\Message\ServerRequestInterface;
 use Webmozart\Assert\Assert;
 use Yii;
 
-class OauthProcess {
+final readonly class OauthProcess {
 
     private const array INTERNAL_PERMISSIONS_TO_PUBLIC_SCOPES = [
         P::OBTAIN_OWN_ACCOUNT_INFO => 'account_info',
         P::OBTAIN_ACCOUNT_EMAIL => 'account_email',
     ];
 
-    public function __construct(private readonly AuthorizationServer $server) {
+    public function __construct(
+        private AuthorizationServer $server,
+    ) {
     }
 
     /**
@@ -43,8 +45,7 @@ class OauthProcess {
      *
      * In addition, you can pass the description value to override the application's description.
      *
-     * @param ServerRequestInterface $request
-     * @return array
+     * @return array<mixed>
      */
     public function validate(ServerRequestInterface $request): array {
         try {
@@ -77,8 +78,7 @@ class OauthProcess {
      * If the field is present, it will be interpreted as any value resulting in false positives.
      * Otherwise, the value will be interpreted as "true".
      *
-     * @param ServerRequestInterface $request
-     * @return array
+     * @return array<mixed>
      */
     public function complete(ServerRequestInterface $request): array {
         try {
@@ -144,8 +144,7 @@ class OauthProcess {
      *     grant_type,
      * ]
      *
-     * @param ServerRequestInterface $request
-     * @return array
+     * @return array<mixed>
      */
     public function getToken(ServerRequestInterface $request): array {
         $params = (array)$request->getParsedBody();
@@ -232,11 +231,9 @@ class OauthProcess {
     }
 
     /**
-     * @param ServerRequestInterface $request
-     * @param OauthClient $client
      * @param ScopeEntityInterface[] $scopes
      *
-     * @return array
+     * @return array<mixed>
      */
     private function buildSuccessResponse(ServerRequestInterface $request, OauthClient $client, array $scopes): array {
         return [
@@ -262,7 +259,7 @@ class OauthProcess {
 
     /**
      * @param ScopeEntityInterface[] $scopes
-     * @return array
+     * @return string[]
      */
     private function buildScopesArray(array $scopes): array {
         $result = [];
@@ -273,6 +270,15 @@ class OauthProcess {
         return $result;
     }
 
+    /**
+     * @return array{
+     *     success: false,
+     *     error: string,
+     *     parameter: string|null,
+     *     statusCode: int,
+     *     redirectUri?: string,
+     * }
+     */
     private function buildCompleteErrorResponse(OAuthServerException $e): array {
         $hint = $e->getPayload()['hint'] ?? '';
         if (preg_match('/the `(\w+)` scope/', $hint, $matches)) {
@@ -304,8 +310,10 @@ class OauthProcess {
      *
      * Part of the existing texts are the legacy from the previous implementation.
      *
-     * @param OAuthServerException $e
-     * @return array
+     * @return array{
+     *     error: string,
+     *     message: string,
+     * }
      */
     private function buildIssueErrorResponse(OAuthServerException $e): array {
         $errorType = $e->getErrorType();
@@ -331,6 +339,9 @@ class OauthProcess {
         return new OAuthServerException('Client must accept authentication request.', 0, 'accept_required', 401);
     }
 
+    /**
+     * @return list<string>
+     */
     private function getScopesList(AuthorizationRequestInterface $request): array {
         return array_values(array_map(fn(ScopeEntityInterface $scope): string => $scope->getIdentifier(), $request->getScopes()));
     }
diff --git a/api/tests/functional/_steps/OauthSteps.php b/api/tests/functional/_steps/OauthSteps.php
index e35ab03..4a22a89 100644
--- a/api/tests/functional/_steps/OauthSteps.php
+++ b/api/tests/functional/_steps/OauthSteps.php
@@ -3,8 +3,8 @@ declare(strict_types=1);
 
 namespace api\tests\functional\_steps;
 
-use api\components\OAuth2\Repositories\PublicScopeRepository;
 use api\tests\FunctionalTester;
+use common\components\OAuth2\Repositories\PublicScopeRepository;
 
 class OauthSteps extends FunctionalTester {
 
diff --git a/api/tests/unit/components/OAuth2/Entities/AccessTokenEntityTest.php b/api/tests/unit/components/OAuth2/Entities/AccessTokenEntityTest.php
index b22115d..8bfcf2b 100644
--- a/api/tests/unit/components/OAuth2/Entities/AccessTokenEntityTest.php
+++ b/api/tests/unit/components/OAuth2/Entities/AccessTokenEntityTest.php
@@ -3,8 +3,8 @@ declare(strict_types=1);
 
 namespace api\tests\unit\components\OAuth2\Entities;
 
-use api\components\OAuth2\Entities\AccessTokenEntity;
 use api\tests\unit\TestCase;
+use common\components\OAuth2\Entities\AccessTokenEntity;
 use DateTimeImmutable;
 use League\OAuth2\Server\Entities\ClientEntityInterface;
 use League\OAuth2\Server\Entities\ScopeEntityInterface;
diff --git a/autocompletion.php b/autocompletion.php
index f930039..257612f 100644
--- a/autocompletion.php
+++ b/autocompletion.php
@@ -34,7 +34,6 @@ abstract class BaseApplication extends yii\base\Application {
  *
  * @property \api\components\User\Component       $user
  * @property \api\components\ReCaptcha\Component  $reCaptcha
- * @property \api\components\OAuth2\Component     $oauth
  * @property \api\components\Tokens\Component     $tokens
  * @property \api\components\Tokens\TokensFactory $tokensFactory
  *
diff --git a/api/components/OAuth2/Component.php b/common/components/OAuth2/AuthorizationServerFactory.php
similarity index 82%
rename from api/components/OAuth2/Component.php
rename to common/components/OAuth2/AuthorizationServerFactory.php
index cc0675a..fd2630e 100644
--- a/api/components/OAuth2/Component.php
+++ b/common/components/OAuth2/AuthorizationServerFactory.php
@@ -1,26 +1,16 @@
 <?php
 declare(strict_types=1);
 
-namespace api\components\OAuth2;
+namespace common\components\OAuth2;
 
 use Carbon\CarbonInterval;
 use DateInterval;
 use League\OAuth2\Server\AuthorizationServer;
 use yii\base\Component as BaseComponent;
 
-final class Component extends BaseComponent {
+final class AuthorizationServerFactory extends BaseComponent {
 
-    private ?AuthorizationServer $_authServer = null;
-
-    public function getAuthServer(): AuthorizationServer {
-        if ($this->_authServer === null) {
-            $this->_authServer = $this->createAuthServer();
-        }
-
-        return $this->_authServer;
-    }
-
-    private function createAuthServer(): AuthorizationServer {
+    public static function build(): AuthorizationServer {
         $clientsRepo = new Repositories\ClientRepository();
         $accessTokensRepo = new Repositories\AccessTokenRepository();
         $publicScopesRepo = new Repositories\PublicScopeRepository();
diff --git a/api/components/OAuth2/CryptTrait.php b/common/components/OAuth2/CryptTrait.php
similarity index 81%
rename from api/components/OAuth2/CryptTrait.php
rename to common/components/OAuth2/CryptTrait.php
index 643578e..bcfa475 100644
--- a/api/components/OAuth2/CryptTrait.php
+++ b/common/components/OAuth2/CryptTrait.php
@@ -1,7 +1,7 @@
 <?php
 declare(strict_types=1);
 
-namespace api\components\OAuth2;
+namespace common\components\OAuth2;
 
 use LogicException;
 use RangeException;
@@ -18,11 +18,11 @@ use Yii;
  */
 trait CryptTrait {
 
-    protected function encrypt($unencryptedData): string {
+    protected function encrypt(string $unencryptedData): string {
         return Yii::$app->tokens->encryptValue($unencryptedData);
     }
 
-    protected function decrypt($encryptedData): string {
+    protected function decrypt(string $encryptedData): string {
         try {
             return Yii::$app->tokens->decryptValue($encryptedData);
         } catch (SodiumException|RangeException $e) {
diff --git a/api/components/OAuth2/Entities/AccessTokenEntity.php b/common/components/OAuth2/Entities/AccessTokenEntity.php
similarity index 93%
rename from api/components/OAuth2/Entities/AccessTokenEntity.php
rename to common/components/OAuth2/Entities/AccessTokenEntity.php
index 15243a2..ecd3854 100644
--- a/api/components/OAuth2/Entities/AccessTokenEntity.php
+++ b/common/components/OAuth2/Entities/AccessTokenEntity.php
@@ -1,7 +1,7 @@
 <?php
 declare(strict_types=1);
 
-namespace api\components\OAuth2\Entities;
+namespace common\components\OAuth2\Entities;
 
 use League\OAuth2\Server\CryptKeyInterface;
 use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
diff --git a/api/components/OAuth2/Entities/AuthCodeEntity.php b/common/components/OAuth2/Entities/AuthCodeEntity.php
similarity index 75%
rename from api/components/OAuth2/Entities/AuthCodeEntity.php
rename to common/components/OAuth2/Entities/AuthCodeEntity.php
index 1db3362..bd68d1c 100644
--- a/api/components/OAuth2/Entities/AuthCodeEntity.php
+++ b/common/components/OAuth2/Entities/AuthCodeEntity.php
@@ -1,14 +1,14 @@
 <?php
 declare(strict_types=1);
 
-namespace api\components\OAuth2\Entities;
+namespace common\components\OAuth2\Entities;
 
 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 implements AuthCodeEntityInterface {
+final class AuthCodeEntity implements AuthCodeEntityInterface {
     use EntityTrait;
     use AuthCodeTrait;
     use TokenEntityTrait;
diff --git a/api/components/OAuth2/Entities/ClientEntity.php b/common/components/OAuth2/Entities/ClientEntity.php
similarity index 77%
rename from api/components/OAuth2/Entities/ClientEntity.php
rename to common/components/OAuth2/Entities/ClientEntity.php
index 4d375c3..5ea23ec 100644
--- a/api/components/OAuth2/Entities/ClientEntity.php
+++ b/common/components/OAuth2/Entities/ClientEntity.php
@@ -1,19 +1,19 @@
 <?php
 declare(strict_types=1);
 
-namespace api\components\OAuth2\Entities;
+namespace common\components\OAuth2\Entities;
 
 use League\OAuth2\Server\Entities\ClientEntityInterface;
 use League\OAuth2\Server\Entities\Traits\ClientTrait;
 use League\OAuth2\Server\Entities\Traits\EntityTrait;
 
-class ClientEntity implements ClientEntityInterface {
+final class ClientEntity implements ClientEntityInterface {
     use EntityTrait;
     use ClientTrait;
 
     /**
-     * @param non-empty-string $id
-     * @param string|string[] $redirectUri
+     * @phpstan-param non-empty-string $id
+     * @phpstan-param string|list<string> $redirectUri
      */
     public function __construct(
         string $id,
diff --git a/api/components/OAuth2/Entities/ScopeEntity.php b/common/components/OAuth2/Entities/ScopeEntity.php
similarity index 66%
rename from api/components/OAuth2/Entities/ScopeEntity.php
rename to common/components/OAuth2/Entities/ScopeEntity.php
index 24895c2..6c3de48 100644
--- a/api/components/OAuth2/Entities/ScopeEntity.php
+++ b/common/components/OAuth2/Entities/ScopeEntity.php
@@ -1,16 +1,19 @@
 <?php
 declare(strict_types=1);
 
-namespace api\components\OAuth2\Entities;
+namespace common\components\OAuth2\Entities;
 
 use League\OAuth2\Server\Entities\ScopeEntityInterface;
 use League\OAuth2\Server\Entities\Traits\EntityTrait;
 use League\OAuth2\Server\Entities\Traits\ScopeTrait;
 
-class ScopeEntity implements ScopeEntityInterface {
+final class ScopeEntity implements ScopeEntityInterface {
     use EntityTrait;
     use ScopeTrait;
 
+    /**
+     * @phpstan-param non-empty-string $id
+     */
     public function __construct(string $id) {
         $this->identifier = $id;
     }
diff --git a/api/components/OAuth2/Entities/UserEntity.php b/common/components/OAuth2/Entities/UserEntity.php
similarity index 71%
rename from api/components/OAuth2/Entities/UserEntity.php
rename to common/components/OAuth2/Entities/UserEntity.php
index 0328763..4c01aa5 100644
--- a/api/components/OAuth2/Entities/UserEntity.php
+++ b/common/components/OAuth2/Entities/UserEntity.php
@@ -1,12 +1,12 @@
 <?php
 declare(strict_types=1);
 
-namespace api\components\OAuth2\Entities;
+namespace common\components\OAuth2\Entities;
 
 use League\OAuth2\Server\Entities\Traits\EntityTrait;
 use League\OAuth2\Server\Entities\UserEntityInterface;
 
-class UserEntity implements UserEntityInterface {
+final class UserEntity implements UserEntityInterface {
     use EntityTrait;
 
     public function __construct(int $id) {
diff --git a/common/components/OAuth2/Events/RequestedRefreshToken.php b/common/components/OAuth2/Events/RequestedRefreshToken.php
new file mode 100644
index 0000000..f312416
--- /dev/null
+++ b/common/components/OAuth2/Events/RequestedRefreshToken.php
@@ -0,0 +1,10 @@
+<?php
+declare(strict_types=1);
+
+namespace common\components\OAuth2\Events;
+
+use League\OAuth2\Server\EventEmitting\AbstractEvent;
+
+final class RequestedRefreshToken extends AbstractEvent {
+
+}
diff --git a/api/components/OAuth2/Grants/AuthCodeGrant.php b/common/components/OAuth2/Grants/AuthCodeGrant.php
similarity index 69%
rename from api/components/OAuth2/Grants/AuthCodeGrant.php
rename to common/components/OAuth2/Grants/AuthCodeGrant.php
index fa16d27..9dd7031 100644
--- a/api/components/OAuth2/Grants/AuthCodeGrant.php
+++ b/common/components/OAuth2/Grants/AuthCodeGrant.php
@@ -1,35 +1,23 @@
 <?php
 declare(strict_types=1);
 
-namespace api\components\OAuth2\Grants;
+namespace common\components\OAuth2\Grants;
 
-use api\components\OAuth2\CryptTrait;
-use api\components\OAuth2\Events\RequestedRefreshToken;
-use api\components\OAuth2\Repositories\PublicScopeRepository;
+use common\components\OAuth2\CryptTrait;
+use common\components\OAuth2\Events\RequestedRefreshToken;
+use common\components\OAuth2\Repositories\PublicScopeRepository;
 use DateInterval;
 use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
 use League\OAuth2\Server\Entities\ClientEntityInterface;
-use League\OAuth2\Server\Entities\ScopeEntityInterface;
 use League\OAuth2\Server\Exception\OAuthServerException;
-use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
 use League\OAuth2\Server\Grant\AuthCodeGrant as BaseAuthCodeGrant;
 use League\OAuth2\Server\RequestEvent;
 use Psr\Http\Message\ServerRequestInterface;
 use yii\helpers\StringHelper;
 
-class AuthCodeGrant extends BaseAuthCodeGrant {
+final class AuthCodeGrant extends BaseAuthCodeGrant {
     use CryptTrait;
 
-    /**
-     * @param DateInterval $accessTokenTTL
-     * @param ClientEntityInterface $client
-     * @param string|null $userIdentifier
-     * @param ScopeEntityInterface[] $scopes
-     *
-     * @return AccessTokenEntityInterface
-     * @throws OAuthServerException
-     * @throws UniqueTokenIdentifierConstraintViolationException
-     */
     protected function issueAccessToken(
         DateInterval $accessTokenTTL,
         ClientEntityInterface $client,
diff --git a/common/components/OAuth2/Grants/ClientCredentialsGrant.php b/common/components/OAuth2/Grants/ClientCredentialsGrant.php
new file mode 100644
index 0000000..4df9356
--- /dev/null
+++ b/common/components/OAuth2/Grants/ClientCredentialsGrant.php
@@ -0,0 +1,12 @@
+<?php
+declare(strict_types=1);
+
+namespace common\components\OAuth2\Grants;
+
+use common\components\OAuth2\CryptTrait;
+use League\OAuth2\Server\Grant\ClientCredentialsGrant as BaseClientCredentialsGrant;
+
+final class ClientCredentialsGrant extends BaseClientCredentialsGrant {
+    use CryptTrait;
+
+}
diff --git a/api/components/OAuth2/Grants/RefreshTokenGrant.php b/common/components/OAuth2/Grants/RefreshTokenGrant.php
similarity index 85%
rename from api/components/OAuth2/Grants/RefreshTokenGrant.php
rename to common/components/OAuth2/Grants/RefreshTokenGrant.php
index 1ea1a9c..6b5d3d4 100644
--- a/api/components/OAuth2/Grants/RefreshTokenGrant.php
+++ b/common/components/OAuth2/Grants/RefreshTokenGrant.php
@@ -1,11 +1,11 @@
 <?php
 declare(strict_types=1);
 
-namespace api\components\OAuth2\Grants;
+namespace common\components\OAuth2\Grants;
 
-use api\components\OAuth2\CryptTrait;
 use api\components\Tokens\TokenReader;
 use Carbon\FactoryImmutable;
+use common\components\OAuth2\CryptTrait;
 use common\models\OauthSession;
 use InvalidArgumentException;
 use Lcobucci\JWT\Validation\Constraint\LooseValidAt;
@@ -18,7 +18,7 @@ use Psr\Http\Message\ServerRequestInterface;
 use Throwable;
 use Yii;
 
-class RefreshTokenGrant extends BaseRefreshTokenGrant {
+final class RefreshTokenGrant extends BaseRefreshTokenGrant {
     use CryptTrait;
 
     /**
@@ -26,11 +26,7 @@ class RefreshTokenGrant extends BaseRefreshTokenGrant {
      * 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
+     * @inheritDoc
      */
     protected function validateOldRefreshToken(ServerRequestInterface $request, string $clientId): array {
         $refreshToken = $this->getRequestParameter('refresh_token', $request);
@@ -45,18 +41,13 @@ class RefreshTokenGrant extends BaseRefreshTokenGrant {
      * Currently we're not rotating refresh tokens.
      * So we're 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
+     * @return array<string, mixed>
      * @throws OAuthServerException
      */
     private function validateLegacyRefreshToken(string $refreshToken): array {
@@ -91,14 +82,7 @@ class RefreshTokenGrant extends BaseRefreshTokenGrant {
     }
 
     /**
-     * @return array{
-     *     client_id: string,
-     *     refresh_token_id?: string,
-     *     access_token_id?: string,
-     *     scopes: list<string>|null,
-     *     user_id: string|null,
-     *     expire_time: int|null,
-     * }
+     * @return array<string, mixed>
      * @throws OAuthServerException
      */
     private function validateAccessToken(string $jwt): array {
diff --git a/api/components/OAuth2/Keys/EmptyKey.php b/common/components/OAuth2/Keys/EmptyKey.php
similarity index 76%
rename from api/components/OAuth2/Keys/EmptyKey.php
rename to common/components/OAuth2/Keys/EmptyKey.php
index 216cc4c..82f241b 100644
--- a/api/components/OAuth2/Keys/EmptyKey.php
+++ b/common/components/OAuth2/Keys/EmptyKey.php
@@ -1,11 +1,11 @@
 <?php
 declare(strict_types=1);
 
-namespace api\components\OAuth2\Keys;
+namespace common\components\OAuth2\Keys;
 
 use League\OAuth2\Server\CryptKeyInterface;
 
-class EmptyKey implements CryptKeyInterface {
+final class EmptyKey implements CryptKeyInterface {
 
     public function getKeyPath(): string {
         return '';
diff --git a/api/components/OAuth2/Repositories/AccessTokenRepository.php b/common/components/OAuth2/Repositories/AccessTokenRepository.php
similarity index 63%
rename from api/components/OAuth2/Repositories/AccessTokenRepository.php
rename to common/components/OAuth2/Repositories/AccessTokenRepository.php
index affaf2f..6bad3db 100644
--- a/api/components/OAuth2/Repositories/AccessTokenRepository.php
+++ b/common/components/OAuth2/Repositories/AccessTokenRepository.php
@@ -1,28 +1,23 @@
 <?php
 declare(strict_types=1);
 
-namespace api\components\OAuth2\Repositories;
+namespace common\components\OAuth2\Repositories;
 
-use api\components\OAuth2\Entities\AccessTokenEntity;
+use common\components\OAuth2\Entities\AccessTokenEntity;
 use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
 use League\OAuth2\Server\Entities\ClientEntityInterface;
 use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
 
-class AccessTokenRepository implements AccessTokenRepositoryInterface {
+final class AccessTokenRepository implements AccessTokenRepositoryInterface {
 
     /**
-     * Create a new access token
-     *
-     * @param ClientEntityInterface $clientEntity
-     * @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes
-     * @param mixed $userIdentifier
-     *
-     * @return AccessTokenEntityInterface
+     * @inheritDoc
+     * @phpstan-param non-empty-string|null $userIdentifier
      */
     public function getNewToken(
         ClientEntityInterface $clientEntity,
         array $scopes,
-        $userIdentifier = null,
+        ?string $userIdentifier = null,
     ): AccessTokenEntityInterface {
         $accessToken = new AccessTokenEntity();
         $accessToken->setClient($clientEntity);
@@ -38,11 +33,11 @@ class AccessTokenRepository implements AccessTokenRepositoryInterface {
         // We don't store access tokens, so there's no need to do anything here
     }
 
-    public function revokeAccessToken($tokenId): void {
+    public function revokeAccessToken(string $tokenId): void {
         // We don't store access tokens, so there's no need to do anything here
     }
 
-    public function isAccessTokenRevoked($tokenId): bool {
+    public function isAccessTokenRevoked(string $tokenId): bool {
         return false;
     }
 
diff --git a/api/components/OAuth2/Repositories/AuthCodeRepository.php b/common/components/OAuth2/Repositories/AuthCodeRepository.php
similarity index 57%
rename from api/components/OAuth2/Repositories/AuthCodeRepository.php
rename to common/components/OAuth2/Repositories/AuthCodeRepository.php
index 4a71f9e..324c5d6 100644
--- a/api/components/OAuth2/Repositories/AuthCodeRepository.php
+++ b/common/components/OAuth2/Repositories/AuthCodeRepository.php
@@ -1,13 +1,13 @@
 <?php
 declare(strict_types=1);
 
-namespace api\components\OAuth2\Repositories;
+namespace common\components\OAuth2\Repositories;
 
-use api\components\OAuth2\Entities\AuthCodeEntity;
+use common\components\OAuth2\Entities\AuthCodeEntity;
 use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
 use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
 
-class AuthCodeRepository implements AuthCodeRepositoryInterface {
+final class AuthCodeRepository implements AuthCodeRepositoryInterface {
 
     public function getNewAuthCode(): AuthCodeEntityInterface {
         return new AuthCodeEntity();
@@ -16,10 +16,10 @@ class AuthCodeRepository implements AuthCodeRepositoryInterface {
     public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity): void {
     }
 
-    public function revokeAuthCode($codeId): void {
+    public function revokeAuthCode(string $codeId): void {
     }
 
-    public function isAuthCodeRevoked($codeId): bool {
+    public function isAuthCodeRevoked(string $codeId): bool {
         return false;
     }
 
diff --git a/api/components/OAuth2/Repositories/ClientRepository.php b/common/components/OAuth2/Repositories/ClientRepository.php
similarity index 61%
rename from api/components/OAuth2/Repositories/ClientRepository.php
rename to common/components/OAuth2/Repositories/ClientRepository.php
index 0503aa1..859fcfa 100644
--- a/api/components/OAuth2/Repositories/ClientRepository.php
+++ b/common/components/OAuth2/Repositories/ClientRepository.php
@@ -1,26 +1,27 @@
 <?php
 declare(strict_types=1);
 
-namespace api\components\OAuth2\Repositories;
+namespace common\components\OAuth2\Repositories;
 
-use api\components\OAuth2\Entities\ClientEntity;
+use common\components\OAuth2\Entities\ClientEntity;
 use common\models\OauthClient;
 use League\OAuth2\Server\Entities\ClientEntityInterface;
 use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
 
-class ClientRepository implements ClientRepositoryInterface {
+final class ClientRepository implements ClientRepositoryInterface {
 
-    public function getClientEntity($clientId): ?ClientEntityInterface {
-        $client = $this->findModel($clientId);
+    public function getClientEntity(string $clientIdentifier): ?ClientEntityInterface {
+        $client = $this->findModel($clientIdentifier);
         if ($client === null) {
             return null;
         }
 
-        return new ClientEntity($client->id, $client->name, $client->redirect_uri ?? '', (bool)$client->is_trusted);
+        // @phpstan-ignore argument.type
+        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);
+    public function validateClient(string $clientIdentifier, ?string $clientSecret, ?string $grantType): bool {
+        $client = $this->findModel($clientIdentifier);
         if ($client === null) {
             return false;
         }
diff --git a/api/components/OAuth2/Repositories/EmptyScopeRepository.php b/common/components/OAuth2/Repositories/EmptyScopeRepository.php
similarity index 80%
rename from api/components/OAuth2/Repositories/EmptyScopeRepository.php
rename to common/components/OAuth2/Repositories/EmptyScopeRepository.php
index 33dc7f5..85e148b 100644
--- a/api/components/OAuth2/Repositories/EmptyScopeRepository.php
+++ b/common/components/OAuth2/Repositories/EmptyScopeRepository.php
@@ -1,7 +1,7 @@
 <?php
 declare(strict_types=1);
 
-namespace api\components\OAuth2\Repositories;
+namespace common\components\OAuth2\Repositories;
 
 use League\OAuth2\Server\Entities\ClientEntityInterface;
 use League\OAuth2\Server\Entities\ScopeEntityInterface;
@@ -12,7 +12,7 @@ use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
  * To create an instance of the authorization server, you need to pass the scopes
  * repository. This class acts as a dummy to meet this requirement.
  */
-class EmptyScopeRepository implements ScopeRepositoryInterface {
+final class EmptyScopeRepository implements ScopeRepositoryInterface {
 
     public function getScopeEntityByIdentifier($identifier): ?ScopeEntityInterface {
         return null;
@@ -20,9 +20,9 @@ class EmptyScopeRepository implements ScopeRepositoryInterface {
 
     public function finalizeScopes(
         array $scopes,
-        $grantType,
+        string $grantType,
         ClientEntityInterface $clientEntity,
-        $userIdentifier = null,
+        ?string $userIdentifier = null,
         ?string $authCodeId = null,
     ): array {
         return $scopes;
diff --git a/api/components/OAuth2/Repositories/InternalScopeRepository.php b/common/components/OAuth2/Repositories/InternalScopeRepository.php
similarity index 85%
rename from api/components/OAuth2/Repositories/InternalScopeRepository.php
rename to common/components/OAuth2/Repositories/InternalScopeRepository.php
index dcd2027..cd36ee0 100644
--- a/api/components/OAuth2/Repositories/InternalScopeRepository.php
+++ b/common/components/OAuth2/Repositories/InternalScopeRepository.php
@@ -1,17 +1,17 @@
 <?php
 declare(strict_types=1);
 
-namespace api\components\OAuth2\Repositories;
+namespace common\components\OAuth2\Repositories;
 
-use api\components\OAuth2\Entities\ClientEntity;
-use api\components\OAuth2\Entities\ScopeEntity;
 use api\rbac\Permissions as P;
+use common\components\OAuth2\Entities\ClientEntity;
+use common\components\OAuth2\Entities\ScopeEntity;
 use League\OAuth2\Server\Entities\ClientEntityInterface;
 use League\OAuth2\Server\Entities\ScopeEntityInterface;
 use League\OAuth2\Server\Exception\OAuthServerException;
 use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
 
-class InternalScopeRepository implements ScopeRepositoryInterface {
+final class InternalScopeRepository implements ScopeRepositoryInterface {
 
     private const array ALLOWED_SCOPES = [
         P::CHANGE_ACCOUNT_USERNAME,
@@ -39,9 +39,9 @@ class InternalScopeRepository implements ScopeRepositoryInterface {
      */
     public function finalizeScopes(
         array $scopes,
-        $grantType,
+        string $grantType,
         ClientEntityInterface $clientEntity,
-        $userIdentifier = null,
+        ?string $userIdentifier = null,
         ?string $authCodeId = null,
     ): array {
         if (empty($scopes)) {
diff --git a/api/components/OAuth2/Repositories/PublicScopeRepository.php b/common/components/OAuth2/Repositories/PublicScopeRepository.php
similarity index 86%
rename from api/components/OAuth2/Repositories/PublicScopeRepository.php
rename to common/components/OAuth2/Repositories/PublicScopeRepository.php
index 3ebd19f..961e738 100644
--- a/api/components/OAuth2/Repositories/PublicScopeRepository.php
+++ b/common/components/OAuth2/Repositories/PublicScopeRepository.php
@@ -1,15 +1,15 @@
 <?php
 declare(strict_types=1);
 
-namespace api\components\OAuth2\Repositories;
+namespace common\components\OAuth2\Repositories;
 
-use api\components\OAuth2\Entities\ScopeEntity;
 use api\rbac\Permissions as P;
+use common\components\OAuth2\Entities\ScopeEntity;
 use League\OAuth2\Server\Entities\ClientEntityInterface;
 use League\OAuth2\Server\Entities\ScopeEntityInterface;
 use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
 
-class PublicScopeRepository implements ScopeRepositoryInterface {
+final class PublicScopeRepository implements ScopeRepositoryInterface {
 
     public const string OFFLINE_ACCESS = 'offline_access';
     public const string CHANGE_SKIN = 'change_skin';
@@ -41,9 +41,9 @@ class PublicScopeRepository implements ScopeRepositoryInterface {
 
     public function finalizeScopes(
         array $scopes,
-        $grantType,
+        string $grantType,
         ClientEntityInterface $clientEntity,
-        $userIdentifier = null,
+        ?string $userIdentifier = null,
         ?string $authCodeId = null,
     ): array {
         return $scopes;
diff --git a/api/components/OAuth2/Repositories/RefreshTokenRepository.php b/common/components/OAuth2/Repositories/RefreshTokenRepository.php
similarity index 63%
rename from api/components/OAuth2/Repositories/RefreshTokenRepository.php
rename to common/components/OAuth2/Repositories/RefreshTokenRepository.php
index 199e342..4c5febe 100644
--- a/api/components/OAuth2/Repositories/RefreshTokenRepository.php
+++ b/common/components/OAuth2/Repositories/RefreshTokenRepository.php
@@ -1,12 +1,12 @@
 <?php
 declare(strict_types=1);
 
-namespace api\components\OAuth2\Repositories;
+namespace common\components\OAuth2\Repositories;
 
 use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
 use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
 
-class RefreshTokenRepository implements RefreshTokenRepositoryInterface {
+final class RefreshTokenRepository implements RefreshTokenRepositoryInterface {
 
     public function getNewRefreshToken(): ?RefreshTokenEntityInterface {
         return null;
@@ -16,11 +16,11 @@ class RefreshTokenRepository implements RefreshTokenRepositoryInterface {
         // Do nothing
     }
 
-    public function revokeRefreshToken($tokenId): void {
+    public function revokeRefreshToken(string $tokenId): void {
         // Do nothing
     }
 
-    public function isRefreshTokenRevoked($tokenId): bool {
+    public function isRefreshTokenRevoked(string $tokenId): bool {
         return false;
     }
 
diff --git a/common/components/OAuth2/ResponseTypes/BearerTokenResponse.php b/common/components/OAuth2/ResponseTypes/BearerTokenResponse.php
new file mode 100644
index 0000000..ad6508b
--- /dev/null
+++ b/common/components/OAuth2/ResponseTypes/BearerTokenResponse.php
@@ -0,0 +1,12 @@
+<?php
+declare(strict_types=1);
+
+namespace common\components\OAuth2\ResponseTypes;
+
+use common\components\OAuth2\CryptTrait;
+use League\OAuth2\Server\ResponseTypes\BearerTokenResponse as BaseBearerTokenResponse;
+
+final class BearerTokenResponse extends BaseBearerTokenResponse {
+    use CryptTrait;
+
+}
diff --git a/common/config/config.php b/common/config/config.php
index e7b8e2c..f1653d6 100644
--- a/common/config/config.php
+++ b/common/config/config.php
@@ -26,6 +26,7 @@ return [
                     'http://' . (getenv('CHRLY_HOST') ?: 'skinsystem.ely.by'),
                 ],
             ],
+            League\OAuth2\Server\AuthorizationServer::class => common\components\OAuth2\AuthorizationServerFactory::build(...),
         ],
     ],
     'components' => [
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index 4876fb8..fa9d25c 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -5,151 +5,6 @@ parameters:
 			count: 1
 			path: api/components/ErrorHandler.php
 
-		-
-			message: "#^Property api\\\\components\\\\OAuth2\\\\Entities\\\\ScopeEntity\\:\\:\\$identifier \\(non\\-empty\\-string\\) does not accept string\\.$#"
-			count: 1
-			path: api/components/OAuth2/Entities/ScopeEntity.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Grants\\\\AuthCodeGrant\\:\\:decrypt\\(\\) has parameter \\$encryptedData with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Grants/AuthCodeGrant.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Grants\\\\AuthCodeGrant\\:\\:encrypt\\(\\) has parameter \\$unencryptedData with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Grants/AuthCodeGrant.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Grants\\\\ClientCredentialsGrant\\:\\:decrypt\\(\\) has parameter \\$encryptedData with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Grants/ClientCredentialsGrant.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Grants\\\\ClientCredentialsGrant\\:\\:encrypt\\(\\) has parameter \\$unencryptedData with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Grants/ClientCredentialsGrant.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Grants\\\\RefreshTokenGrant\\:\\:decrypt\\(\\) has parameter \\$encryptedData with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Grants/RefreshTokenGrant.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Grants\\\\RefreshTokenGrant\\:\\:encrypt\\(\\) has parameter \\$unencryptedData with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Grants/RefreshTokenGrant.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Grants\\\\RefreshTokenGrant\\:\\:validateAccessToken\\(\\) should return array\\{client_id\\: string, refresh_token_id\\?\\: string, access_token_id\\?\\: string, scopes\\: array\\<int, string\\>\\|null, user_id\\: string\\|null, expire_time\\: int\\|null\\} but returns array\\{client_id\\: string\\|null, refresh_token_id\\: '', access_token_id\\: '', scopes\\: array\\|null, user_id\\: int\\|null, expire_time\\: null\\}\\.$#"
-			count: 1
-			path: api/components/OAuth2/Grants/RefreshTokenGrant.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Grants\\\\RefreshTokenGrant\\:\\:validateLegacyRefreshToken\\(\\) return type has no value type specified in iterable type array\\.$#"
-			count: 1
-			path: api/components/OAuth2/Grants/RefreshTokenGrant.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Grants\\\\RefreshTokenGrant\\:\\:validateOldRefreshToken\\(\\) return type has no value type specified in iterable type array\\.$#"
-			count: 1
-			path: api/components/OAuth2/Grants/RefreshTokenGrant.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Repositories\\\\AccessTokenRepository\\:\\:isAccessTokenRevoked\\(\\) has parameter \\$tokenId with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Repositories/AccessTokenRepository.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Repositories\\\\AccessTokenRepository\\:\\:revokeAccessToken\\(\\) has parameter \\$tokenId with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Repositories/AccessTokenRepository.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Repositories\\\\AuthCodeRepository\\:\\:isAuthCodeRevoked\\(\\) has parameter \\$codeId with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Repositories/AuthCodeRepository.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Repositories\\\\AuthCodeRepository\\:\\:revokeAuthCode\\(\\) has parameter \\$codeId with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Repositories/AuthCodeRepository.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Repositories\\\\ClientRepository\\:\\:getClientEntity\\(\\) has parameter \\$clientId with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Repositories/ClientRepository.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Repositories\\\\ClientRepository\\:\\:validateClient\\(\\) has parameter \\$clientId with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Repositories/ClientRepository.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Repositories\\\\ClientRepository\\:\\:validateClient\\(\\) has parameter \\$clientSecret with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Repositories/ClientRepository.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Repositories\\\\ClientRepository\\:\\:validateClient\\(\\) has parameter \\$grantType with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Repositories/ClientRepository.php
-
-		-
-			message: "#^Parameter \\#1 \\$id of class api\\\\components\\\\OAuth2\\\\Entities\\\\ClientEntity constructor expects non\\-empty\\-string, string given\\.$#"
-			count: 1
-			path: api/components/OAuth2/Repositories/ClientRepository.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Repositories\\\\EmptyScopeRepository\\:\\:finalizeScopes\\(\\) has parameter \\$grantType with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Repositories/EmptyScopeRepository.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Repositories\\\\EmptyScopeRepository\\:\\:finalizeScopes\\(\\) has parameter \\$userIdentifier with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Repositories/EmptyScopeRepository.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Repositories\\\\InternalScopeRepository\\:\\:finalizeScopes\\(\\) has parameter \\$grantType with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Repositories/InternalScopeRepository.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Repositories\\\\InternalScopeRepository\\:\\:finalizeScopes\\(\\) has parameter \\$userIdentifier with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Repositories/InternalScopeRepository.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Repositories\\\\PublicScopeRepository\\:\\:finalizeScopes\\(\\) has parameter \\$grantType with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Repositories/PublicScopeRepository.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Repositories\\\\PublicScopeRepository\\:\\:finalizeScopes\\(\\) has parameter \\$userIdentifier with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Repositories/PublicScopeRepository.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Repositories\\\\RefreshTokenRepository\\:\\:isRefreshTokenRevoked\\(\\) has parameter \\$tokenId with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Repositories/RefreshTokenRepository.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\Repositories\\\\RefreshTokenRepository\\:\\:revokeRefreshToken\\(\\) has parameter \\$tokenId with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/Repositories/RefreshTokenRepository.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\ResponseTypes\\\\BearerTokenResponse\\:\\:decrypt\\(\\) has parameter \\$encryptedData with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/ResponseTypes/BearerTokenResponse.php
-
-		-
-			message: "#^Method api\\\\components\\\\OAuth2\\\\ResponseTypes\\\\BearerTokenResponse\\:\\:encrypt\\(\\) has parameter \\$unencryptedData with no type specified\\.$#"
-			count: 1
-			path: api/components/OAuth2/ResponseTypes/BearerTokenResponse.php
-
 		-
 			message: "#^Property api\\\\components\\\\ReCaptcha\\\\Component\\:\\:\\$public has no type specified\\.$#"
 			count: 1
@@ -220,11 +75,6 @@ parameters:
 			count: 1
 			path: api/components/Tokens/Component.php
 
-		-
-			message: "#^Method api\\\\components\\\\Tokens\\\\TokenReader\\:\\:getScopes\\(\\) return type has no value type specified in iterable type array\\.$#"
-			count: 1
-			path: api/components/Tokens/TokenReader.php
-
 		-
 			message: "#^Property api\\\\components\\\\User\\\\Component\\:\\:\\$loginUrl type has no value type specified in iterable type array\\.$#"
 			count: 1
@@ -305,16 +155,6 @@ parameters:
 			count: 1
 			path: api/models/authentication/ConfirmEmailForm.php
 
-		-
-			message: "#^Property api\\\\models\\\\authentication\\\\ForgotPasswordForm\\:\\:\\$captcha has no type specified\\.$#"
-			count: 1
-			path: api/models/authentication/ForgotPasswordForm.php
-
-		-
-			message: "#^Property api\\\\models\\\\authentication\\\\ForgotPasswordForm\\:\\:\\$login has no type specified\\.$#"
-			count: 1
-			path: api/models/authentication/ForgotPasswordForm.php
-
 		-
 			message: "#^Property api\\\\models\\\\authentication\\\\RecoverPasswordForm\\:\\:\\$key has no type specified\\.$#"
 			count: 1
@@ -695,46 +535,6 @@ parameters:
 			count: 1
 			path: api/modules/oauth/models/OauthClientTypeForm.php
 
-		-
-			message: "#^Method api\\\\modules\\\\oauth\\\\models\\\\OauthProcess\\:\\:buildCompleteErrorResponse\\(\\) return type has no value type specified in iterable type array\\.$#"
-			count: 1
-			path: api/modules/oauth/models/OauthProcess.php
-
-		-
-			message: "#^Method api\\\\modules\\\\oauth\\\\models\\\\OauthProcess\\:\\:buildIssueErrorResponse\\(\\) return type has no value type specified in iterable type array\\.$#"
-			count: 1
-			path: api/modules/oauth/models/OauthProcess.php
-
-		-
-			message: "#^Method api\\\\modules\\\\oauth\\\\models\\\\OauthProcess\\:\\:buildScopesArray\\(\\) return type has no value type specified in iterable type array\\.$#"
-			count: 1
-			path: api/modules/oauth/models/OauthProcess.php
-
-		-
-			message: "#^Method api\\\\modules\\\\oauth\\\\models\\\\OauthProcess\\:\\:buildSuccessResponse\\(\\) return type has no value type specified in iterable type array\\.$#"
-			count: 1
-			path: api/modules/oauth/models/OauthProcess.php
-
-		-
-			message: "#^Method api\\\\modules\\\\oauth\\\\models\\\\OauthProcess\\:\\:complete\\(\\) return type has no value type specified in iterable type array\\.$#"
-			count: 1
-			path: api/modules/oauth/models/OauthProcess.php
-
-		-
-			message: "#^Method api\\\\modules\\\\oauth\\\\models\\\\OauthProcess\\:\\:getScopesList\\(\\) return type has no value type specified in iterable type array\\.$#"
-			count: 1
-			path: api/modules/oauth/models/OauthProcess.php
-
-		-
-			message: "#^Method api\\\\modules\\\\oauth\\\\models\\\\OauthProcess\\:\\:getToken\\(\\) return type has no value type specified in iterable type array\\.$#"
-			count: 1
-			path: api/modules/oauth/models/OauthProcess.php
-
-		-
-			message: "#^Method api\\\\modules\\\\oauth\\\\models\\\\OauthProcess\\:\\:validate\\(\\) return type has no value type specified in iterable type array\\.$#"
-			count: 1
-			path: api/modules/oauth/models/OauthProcess.php
-
 		-
 			message: "#^Method api\\\\modules\\\\session\\\\Module\\:\\:error\\(\\) has parameter \\$message with no type specified\\.$#"
 			count: 1