Первый тест - Success

This commit is contained in:
Artem Vasilev 2024-03-07 03:59:12 +03:00
parent 1efe232439
commit 2636bebd60
18 changed files with 699 additions and 29 deletions

View File

@ -0,0 +1,87 @@
<?php
namespace Webmasterskaya\Component\OauthServer\Administrator\Event\Scope;
use Joomla\CMS\Event\AbstractEvent;
use Webmasterskaya\Component\OauthServer\Site\Entity\Scope;
class ScopeResolveEvent extends AbstractEvent
{
private bool $is_constructed;
public function __construct(array $scopes, string $grant, object $client, ?int $user_id)
{
$arguments = [
'scopes' => $scopes,
'grant' => $grant,
'client' => $client,
'user_id' => $user_id
];
$this->is_constructed = false;
parent::__construct('onOauthServerScopeResolve', $arguments);
$this->is_constructed = true;
}
protected function onSetScopes(array $scopes): array
{
foreach ($scopes as &$scope) {
if (!($scope instanceof Scope)) {
throw new \InvalidArgumentException(sprintf('Argument "scopes" must be array of "%s" in class "%s". "%s" given.',
Scope::class,
get_class($this),
get_debug_type($scope)
));
}
}
return $scopes;
}
protected function onSetGrant(string $grant)
{
if ($this->is_constructed) {
$grant = $this->getArgument('grant');
}
return $grant;
}
protected function onSetClient(object $client)
{
if ($this->is_constructed) {
$client = $this->getArgument('client');
}
return $client;
}
protected function onSetUser_id(?int $user_id)
{
if ($this->is_constructed) {
$user_id = $this->getArgument('user_id');
}
return $user_id;
}
public function removeArgument($name)
{
throw new \BadMethodCallException(
sprintf(
'Cannot remove the argument %s of the event %s.',
$name,
$this->name
)
);
}
public function clearArguments()
{
throw new \BadMethodCallException(
sprintf(
'Cannot clear arguments of the event %s.',
$this->name
)
);
}
}

View File

@ -0,0 +1,64 @@
<?php
namespace Webmasterskaya\Component\OauthServer\Administrator\Model;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Form\FormFactoryInterface;
use Joomla\CMS\MVC\Factory\LegacyFactory;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\MVC\Model\AdminModel;
class AuthCodeModel extends AdminModel implements RevokedModelInterface
{
use GetItemByIdentifierTrait;
use RevokedModelTrait;
public function __construct($config = [], MVCFactoryInterface $factory = null, FormFactoryInterface $formFactory = null)
{
$this->name = 'AuthCode';
parent::__construct($config, $factory, $formFactory);
}
public function getTable($name = 'AuthCode', $prefix = 'Administrator', $options = [])
{
return parent::getTable($name, $prefix, $options);
}
public function getForm($data = [], $loadData = true): Form|bool
{
$form = $this->loadForm('com_oauthserver.auth_code', 'auth_code', ['control' => 'jform', 'load_data' => $loadData]);
if (empty($form)) {
return false;
}
return $form;
}
protected function loadFormData(): mixed
{
// Check the session for previously entered form data.
$data = Factory::getApplication()->getUserState('com_oauthserver.edit.auth_code.data', []);
if (empty($data)) {
$data = $this->getItem();
}
$this->preprocessData('com_oauthserver.auth_code', $data);
return $data;
}
/**
* @param \Webmasterskaya\Component\OauthServer\Administrator\Table\AuthCodeTable $table
* @return void
* @since version
*/
protected function prepareTable($table)
{
if ($table->expiry instanceof \DateTime || $table->expiry instanceof \DateTimeImmutable) {
$table->expiry = $table->expiry->format($table->getDbo()->getDateFormat());
}
}
}

View File

@ -4,7 +4,6 @@ namespace Webmasterskaya\Component\OauthServer\Administrator\Model;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Object\CMSObject;
use Joomla\Registry\Registry;
use Joomla\Utilities\ArrayHelper;
trait GetItemByIdentifierTrait
@ -37,7 +36,7 @@ trait GetItemByIdentifierTrait
$all_properties = $table->getProperties(false);
if (!empty($all_properties['_jsonEncode'])) {
foreach ($all_properties['$_jsonEncode'] as $prop) {
foreach ($all_properties['_jsonEncode'] as $prop) {
if (array_key_exists($prop, $properties) && is_string($properties[$prop])) {
$properties[$prop] = json_decode($properties[$prop]);
}

View File

@ -0,0 +1,38 @@
<?php
namespace Webmasterskaya\Component\OauthServer\Administrator\Model;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Form;
use Joomla\CMS\MVC\Model\AdminModel;
class RefreshTokenModel extends AdminModel implements RevokedModelInterface
{
use GetItemByIdentifierTrait;
use RevokedModelTrait;
public function getForm($data = [], $loadData = true): Form|bool
{
$form = $this->loadForm('com_oauthserver.refresh_token', 'refresh_token', ['control' => 'jform', 'load_data' => $loadData]);
if (empty($form)) {
return false;
}
return $form;
}
protected function loadFormData(): mixed
{
// Check the session for previously entered form data.
$data = Factory::getApplication()->getUserState('com_oauthserver.edit.refresh_token.data', []);
if (empty($data)) {
$data = $this->getItem();
}
$this->preprocessData('com_oauthserver.refresh_token', $data);
return $data;
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace Webmasterskaya\Component\OauthServer\Administrator\Model;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\MVC\Model\BaseModel;
use Joomla\CMS\MVC\Model\ItemModelInterface;
class ScopeModel extends BaseModel implements ItemModelInterface
{
private static array $_storage;
private const PREDEFINED_SCOPES = ['userinfo', 'email'];
public function getItem($pk = null)
{
// TODO: Implement getItem() method.
}
private function fillStorage(): void
{
$config = ComponentHelper::getParams('com_oauthserver');
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace Webmasterskaya\Component\OauthServer\Administrator\Table;
use Joomla\CMS\Table\Table;
use Joomla\Database\DatabaseDriver;
/**
* @property int $id
* @property string $identifier
* @property \DateTimeImmutable|\DateTime|string $expiry
* @property int|null $user_id
* @property string|array|null $scopes
* @property int $revoked
* @property int $client_id
*
* @since version
*/
class AuthCodeTable extends Table implements RevokedTableInterface
{
use RevokedTableTrait;
/**
* Indicates that columns fully support the NULL value in the database
*
* @var boolean
* @since version
*/
protected $_supportNullValue = true;
/**
* An array of key names to be json encoded in the bind function
*
* @var array
* @since version
*/
protected $_jsonEncode = ['scopes'];
public function __construct(DatabaseDriver $db)
{
parent::__construct('#__webmasterskaya_oauthserver_authorization_codes', 'id', $db);
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace Webmasterskaya\Component\OauthServer\Administrator\Table;
use DateTimeImmutable;
use Joomla\CMS\Table\Table;
use Joomla\Database\DatabaseDriver;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
/**
* @property int $id
* @property string $identifier
* @property \DateTimeImmutable|\DateTime|string $expiry
* @property int $revoked
* @property int|null $access_token_id
*
* @since version
*/
class RefreshTokenTable extends Table implements RevokedTableInterface
{
use RevokedTableTrait;
/**
* Indicates that columns fully support the NULL value in the database
*
* @var boolean
* @since version
*/
protected $_supportNullValue = true;
public function __construct(DatabaseDriver $db)
{
parent::__construct('#__webmasterskaya_oauthserver_refresh_tokens', 'id', $db);
}
}

View File

@ -4,5 +4,5 @@ namespace Webmasterskaya\Component\OauthServer\Administrator\Table;
interface RevokedTableInterface
{
public function revoke($pks = null, $userId = 0): bool;
public function revoke($pks = null): bool;
}

View File

@ -45,11 +45,8 @@ trait RevokedTableTrait
abstract public function appendPrimaryKeys($query, $pk = null);
public function revoke($pks = null, $userId = 0): bool
public function revoke($pks = null): bool
{
// Sanitize input
$userId = (int)$userId;
// Pre-processing by observers
$event = AbstractEvent::create(
'onTableBeforeRevoke',

View File

@ -3,12 +3,21 @@
namespace Webmasterskaya\Component\OauthServer\Site\Controller;
use Joomla\CMS\Application\CMSApplication;
use Webmasterskaya\Component\OauthServer\Site\Entity\User as UserEntity;
use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\Router\Route;
use Joomla\Input\Input;
use Joomla\CMS\Uri\Uri;
use Laminas\Diactoros\ResponseFactory;
use Laminas\Diactoros\ServerRequestFactory;
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Grant\AuthCodeGrant;
use Webmasterskaya\Component\OauthServer\Site\Repository\AccessTokenRepository;
use Webmasterskaya\Component\OauthServer\Site\Repository\AuthCodeRepository;
use Webmasterskaya\Component\OauthServer\Site\Repository\ClientRepository;
use Webmasterskaya\Component\OauthServer\Site\Repository\RefreshTokenRepository;
use Webmasterskaya\Component\OauthServer\Site\Repository\ScopeRepository;
class LoginController extends BaseController
{
@ -17,20 +26,104 @@ class LoginController extends BaseController
parent::__construct($config, $factory, $app, $input);
}
public function authorize()
/**
* @return void
* @throws \Exception
* @since version
*/
public function authorize(): void
{
$app = $this->app;
$user = $app->getIdentity();
$uri = Uri::getInstance();
if (!$user->id) {
$return = http_build_query(['return' => base64_encode(Uri::getInstance()->toString(['scheme', 'user', 'pass', 'host', 'port', 'path']))]);
$this->app->setUserState('oauthserver.login.authorize.request', Uri::getInstance()->getQuery(true));
$return = http_build_query(['return' => base64_encode($uri->toString(['scheme', 'user', 'pass', 'host', 'port', 'path']))]);
$this->app->setUserState('oauthserver.login.authorize.request', $uri->getQuery(true));
$this->app->enqueueMessage('Необходимо авторизоваться!');
$this->app->redirect(Route::_('index.php?option=com_users&view=login&' . $return));
}
$clientRepository = new ClientRepository($this->factory);
var_dump($this->app->getUserState('oauthserver.login.authorize.request'));
$state_request = $this->app->getUserState('oauthserver.login.authorize.request');
if (!empty($state_request) && empty($uri->getQuery(true))) {
foreach ($state_request as $k => $v) {
$uri->setVar($k, $v);
}
}
$this->app->setUserState('oauthserver.login.authorize.request', []);
/** @var \Webmasterskaya\Component\OauthServer\Administrator\Model\ClientModel $clientModel */
$clientModel = $this->factory->createModel('Client', 'Administrator', ['request_ignore' => true]);
$clientRepository = new ClientRepository($clientModel);
/** @var \Webmasterskaya\Component\OauthServer\Administrator\Model\AccessTokenModel $accessTokenModel */
$accessTokenModel = $this->factory->createModel('AccessToken', 'Administrator', ['request_ignore' => true]);
$accessTokenRepository = new AccessTokenRepository($accessTokenModel, $clientModel);
$scopeRepository = new ScopeRepository($clientModel);
$scopeRepository->setDispatcher($this->getDispatcher());
/** @var \Webmasterskaya\Component\OauthServer\Administrator\Model\AuthCodeModel $authCodeModel */
$authCodeModel = $this->factory->createModel('AuthCode', 'Administrator', ['request_ignore' => true]);
$authCodeRepository = new AuthCodeRepository($authCodeModel, $clientModel);
/** @var \Webmasterskaya\Component\OauthServer\Administrator\Model\RefreshTokenModel $refreshTokenModel */
$refreshTokenModel = $this->factory->createModel('RefreshToken', 'Administrator', ['request_ignore' => true]);
$refreshTokenRepository = new RefreshTokenRepository($refreshTokenModel, $accessTokenModel);
$key = openssl_pkey_new([
"digest_alg" => "sha512",
"private_key_bits" => 4096,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
]);
$ppk = '';
openssl_pkey_export($key, $ppk);
// Extract the public key from $res to $pubKey
// $pub = openssl_pkey_get_details($key);
// $pub = $pub["key"];
// var_dump($this->app->getUserState('oauthserver.login.authorize.request'));
$server = new AuthorizationServer(
$clientRepository,
$accessTokenRepository,
$scopeRepository,
$ppk,
$this->app->get('secret')
);
$grant = new AuthCodeGrant(
$authCodeRepository,
$refreshTokenRepository,
new \DateInterval('PT10M') // authorization codes will expire after 10 minutes
);
$grant->setRefreshTokenTTL(new \DateInterval('P1M')); // refresh tokens will expire after 1 month
$server->enableGrantType(
$grant,
new \DateInterval('PT1H') // access tokens will expire after 1 hour
);
$serverRequest = ServerRequestFactory::fromGlobals();
$serverResponse = $this->app->getResponse();
// var_dump($serverRequest->getQueryParams()); die();
$authRequest = $server->validateAuthorizationRequest($serverRequest);
$authRequest->setUser(new UserEntity($user));
$authRequest->setAuthorizationApproved(true);
$this->app->setResponse($server->completeAuthorizationRequest($authRequest, $serverResponse));
return;
echo "<pre>";
var_dump();
die();
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace Webmasterskaya\Component\OauthServer\Site\Entity;
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 AuthCode implements AuthCodeEntityInterface
{
use AuthCodeTrait;
use EntityTrait;
use TokenEntityTrait;
public function getData(): array
{
return [
'identifier' => $this->getIdentifier(),
'expiry' => $this->getExpiryDateTime(),
'user_id' => $this->getUserIdentifier(),
'scopes' => $this->getScopes(),
'client_identifier' => $this->getClient()->getIdentifier()
];
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace Webmasterskaya\Component\OauthServer\Site\Entity;
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
use League\OAuth2\Server\Entities\Traits\EntityTrait;
use League\OAuth2\Server\Entities\Traits\RefreshTokenTrait;
class RefreshToken implements RefreshTokenEntityInterface
{
use EntityTrait;
use RefreshTokenTrait;
public function getData(): array
{
return [
'identifier' => $this->getIdentifier(),
'expiry' => $this->getExpiryDateTime(),
'access_token_identifier' => $this->getAccessToken()->getIdentifier()
];
}
}

View File

@ -0,0 +1,48 @@
<?php
namespace Webmasterskaya\Component\OauthServer\Site\Entity;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
use League\OAuth2\Server\Entities\Traits\EntityTrait;
final class Scope implements ScopeEntityInterface
{
use EntityTrait;
protected ?string $description;
/**
* @return mixed
* @since version
*/
public function jsonSerialize(): mixed
{
return $this->getIdentifier();
}
/**
* @return string|null
* @since version
*/
public function getDescription(): ?string
{
return $this->description ?? null;
}
/**
* @param string $description
* @since version
*/
public function setDescription(string $description): void
{
$this->description = $description;
}
public function __toString(): string
{
return $this->identifier;
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Webmasterskaya\Component\OauthServer\Site\Entity;
use League\OAuth2\Server\Entities\Traits\EntityTrait;
use League\OAuth2\Server\Entities\UserEntityInterface;
class User implements UserEntityInterface
{
use EntityTrait;
public function __construct(\Joomla\CMS\User\User $user)
{
$this->setIdentifier($user->id);
}
}

View File

@ -3,28 +3,75 @@
namespace Webmasterskaya\Component\OauthServer\Site\Repository;
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
use Wamania\Snowball\NotFoundException;
use Webmasterskaya\Component\OauthServer\Administrator\Model\AuthCodeModel;
use Webmasterskaya\Component\OauthServer\Administrator\Model\ClientModel;
use Webmasterskaya\Component\OauthServer\Site\Entity\AuthCode;
class AuthCodeRepository implements AuthCodeRepositoryInterface
{
private AuthCodeModel $authCodeModel;
public function getNewAuthCode()
private ClientModel $clientModel;
/**
* @param \Webmasterskaya\Component\OauthServer\Administrator\Model\AuthCodeModel $authCodeModel
* @param \Webmasterskaya\Component\OauthServer\Administrator\Model\ClientModel $clientModel
* @since version
*/
public function __construct(AuthCodeModel $authCodeModel, ClientModel $clientModel)
{
// TODO: Implement getNewAuthCode() method.
$this->authCodeModel = $authCodeModel;
$this->clientModel = $clientModel;
}
public function getNewAuthCode(): AuthCode
{
return new AuthCode();
}
public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity)
{
// TODO: Implement persistNewAuthCode() method.
$found = false;
try {
$authCode = $this->authCodeModel->getItemByIdentifier($authCodeEntity->getIdentifier());
if ($authCode->id > 0) {
$found = true;
}
} catch (\Throwable $e) {
}
if ($found) {
throw UniqueTokenIdentifierConstraintViolationException::create();
}
$data = $authCodeEntity->getData();
$client = $this->clientModel->getItemByIdentifier($authCodeEntity->getClient()->getIdentifier());
$data['client_id'] = $client->id;
unset($data['client_identifier']);
$this->authCodeModel->save($data);
}
public function revokeAuthCode($codeId)
{
// TODO: Implement revokeAuthCode() method.
$this->authCodeModel->revoke($codeId);
}
public function isAuthCodeRevoked($codeId)
{
// TODO: Implement isAuthCodeRevoked() method.
$authCode = $this->authCodeModel->getItemByIdentifier($codeId);
if (empty($authCode->id)) {
return true;
}
return !!$authCode->revoked;
}
}

View File

@ -3,28 +3,67 @@
namespace Webmasterskaya\Component\OauthServer\Site\Repository;
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
use Webmasterskaya\Component\OauthServer\Administrator\Model\AccessTokenModel;
use Webmasterskaya\Component\OauthServer\Administrator\Model\RefreshTokenModel;
use Webmasterskaya\Component\OauthServer\Site\Entity\RefreshToken;
class RefreshTokenRepository implements RefreshTokenRepositoryInterface
{
public function getNewRefreshToken()
private RefreshTokenModel $refreshTokenModel;
private AccessTokenModel $accessTokenModel;
/**
* @param \Webmasterskaya\Component\OauthServer\Administrator\Model\RefreshTokenModel $refreshTokenModel
* @param \Webmasterskaya\Component\OauthServer\Administrator\Model\AccessTokenModel $accessTokenModel
* @since version
*/
public function __construct(RefreshTokenModel $refreshTokenModel, AccessTokenModel $accessTokenModel)
{
// TODO: Implement getNewRefreshToken() method.
$this->refreshTokenModel = $refreshTokenModel;
$this->accessTokenModel = $accessTokenModel;
}
public function getNewRefreshToken(): RefreshToken
{
return new RefreshToken();
}
public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity)
{
// TODO: Implement persistNewRefreshToken() method.
$refreshToken = $this->refreshTokenModel->getItemByIdentifier($refreshTokenEntity->getIdentifier());
if ($refreshToken->id > 0) {
throw UniqueTokenIdentifierConstraintViolationException::create();
}
$data = $refreshTokenEntity->getData();
$accessToken = $this->accessTokenModel->getItemByIdentifier($refreshTokenEntity->getAccessToken());
unset($data['access_token_identifier']);
$data['access_token_id'] = $accessToken->id;
$this->refreshTokenModel->save($data);
}
public function revokeRefreshToken($tokenId)
public function revokeRefreshToken($tokenId): void
{
// TODO: Implement revokeRefreshToken() method.
$this->refreshTokenModel->revoke($tokenId);
}
public function isRefreshTokenRevoked($tokenId)
public function isRefreshTokenRevoked($tokenId): bool
{
// TODO: Implement isRefreshTokenRevoked() method.
$refreshToken = $this->refreshTokenModel->getItemByIdentifier($tokenId);
if (empty($refreshToken->id)) {
return true;
}
return !!$refreshToken->revoked;
}
}

View File

@ -2,19 +2,107 @@
namespace Webmasterskaya\Component\OauthServer\Site\Repository;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Event\DispatcherAwareInterface;
use Joomla\Event\DispatcherAwareTrait;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
use Webmasterskaya\Component\OauthServer\Administrator\Event\Scope\ScopeResolveEvent;
use Webmasterskaya\Component\OauthServer\Administrator\Model\ClientModel;
use Webmasterskaya\Component\OauthServer\Site\Entity\Scope;
class ScopeRepository implements ScopeRepositoryInterface
class ScopeRepository implements ScopeRepositoryInterface, DispatcherAwareInterface
{
use DispatcherAwareTrait;
public function getScopeEntityByIdentifier($identifier)
private ClientModel $clientModel;
public function __construct(ClientModel $clientModel)
{
// TODO: Implement getScopeEntityByIdentifier() method.
$this->clientModel = $clientModel;
}
public function getScopeEntityByIdentifier($identifier): ?Scope
{
$defined = ['userinfo', 'email'];
if (!in_array($identifier, $defined)) {
return null;
}
$scope = new Scope();
$scope->setIdentifier($identifier);
return $scope;
}
/**
* @param Scope[] $scopes
* @param $grantType
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $clientEntity
* @param null $userIdentifier
* @return mixed
* @throws \League\OAuth2\Server\Exception\OAuthServerException
* @since version
*/
public function finalizeScopes(array $scopes, $grantType, ClientEntityInterface $clientEntity, $userIdentifier = null)
{
// TODO: Implement finalizeScopes() method.
$client = $this->clientModel->getItemByIdentifier($clientEntity->getIdentifier());
$scopes = $this->setupScopes($client, array_values($scopes));
PluginHelper::importPlugin('oauthserver');
$event = new ScopeResolveEvent(
$scopes,
$grantType,
$client,
$userIdentifier
);
return $this->getDispatcher()
->dispatch($event->getName(), $event)
->getArgument('scopes', []);
}
/**
* @param object $client
* @param array $requestedScopes
* @return array
* @throws \League\OAuth2\Server\Exception\OAuthServerException
* @since version
*/
private function setupScopes(object $client, array $requestedScopes): array
{
$clientScopes = $client->scopes;
if (empty($clientScopes)) {
return $requestedScopes;
}
$clientScopes = array_map(function ($item) {
$scope = new Scope();
$scope->setIdentifier((string)$item);
return $scope;
}, $clientScopes);
if (empty($requestedScopes)) {
return $clientScopes;
}
$finalizedScopes = [];
$clientScopesAsStrings = array_map('strval', $clientScopes);
foreach ($requestedScopes as $requestedScope) {
$requestedScopeAsString = (string)$requestedScope;
if (!\in_array($requestedScopeAsString, $clientScopesAsStrings, true)) {
throw OAuthServerException::invalidScope($requestedScopeAsString);
}
$finalizedScopes[] = $requestedScope;
}
return $finalizedScopes;
}
}

View File

@ -16,6 +16,7 @@
"minimum-stability": "stable",
"require": {
"php": "^8.0",
"league/oauth2-server": "^8.5"
"league/oauth2-server": "^8.5",
"ext-openssl": "*"
}
}