yet another iteration
This commit is contained in:
parent
9dc6243822
commit
b999c1bd62
6
.env
6
.env
@ -41,9 +41,3 @@ MESSENGER_TRANSPORT_DSN_HIGH=doctrine://default?queue_name=high
|
|||||||
MESSENGER_TRANSPORT_DSN_LOW=doctrine://default?queue_name=low
|
MESSENGER_TRANSPORT_DSN_LOW=doctrine://default?queue_name=low
|
||||||
# MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages
|
# MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages
|
||||||
###< symfony/messenger ###
|
###< symfony/messenger ###
|
||||||
|
|
||||||
###> trikoder/oauth2-bundle ###
|
|
||||||
# Fallback OAuth2 encryption key
|
|
||||||
# Please override this with a secure value: https://oauth2.thephpleague.com/installation/#string-password
|
|
||||||
OAUTH2_ENCRYPTION_KEY=6cfc355e274dc909f82730c8741eb1e0
|
|
||||||
###< trikoder/oauth2-bundle ###
|
|
||||||
|
1071
composer.lock
generated
1071
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,4 @@ return [
|
|||||||
SymfonyCasts\Bundle\ResetPassword\SymfonyCastsResetPasswordBundle::class => ['all' => true],
|
SymfonyCasts\Bundle\ResetPassword\SymfonyCastsResetPasswordBundle::class => ['all' => true],
|
||||||
Knp\Bundle\TimeBundle\KnpTimeBundle::class => ['all' => true],
|
Knp\Bundle\TimeBundle\KnpTimeBundle::class => ['all' => true],
|
||||||
Fidry\PsyshBundle\PsyshBundle::class => ['all' => true],
|
Fidry\PsyshBundle\PsyshBundle::class => ['all' => true],
|
||||||
Trikoder\Bundle\OAuth2Bundle\TrikoderOAuth2Bundle::class => ['all' => true],
|
|
||||||
Ajgarlag\Bundle\PsrHttpMessageBundle\AjgarlagPsrHttpMessageBundle::class => ['all' => true],
|
|
||||||
SymfonyBundles\JsonRequestBundle\JsonRequestBundle::class => ['all' => true],
|
|
||||||
];
|
];
|
||||||
|
@ -31,9 +31,7 @@ security:
|
|||||||
pattern: ^/api/
|
pattern: ^/api/
|
||||||
security: true
|
security: true
|
||||||
stateless: true
|
stateless: true
|
||||||
guard:
|
|
||||||
authenticators:
|
|
||||||
- Trikoder\Bundle\OAuth2Bundle\Security\Guard\Authenticator\OAuth2Authenticator
|
|
||||||
main:
|
main:
|
||||||
entry_point: App\Security\Authenticator
|
entry_point: App\Security\Authenticator
|
||||||
guard:
|
guard:
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
trikoder_oauth2:
|
|
||||||
|
|
||||||
authorization_server:
|
|
||||||
private_key: '%kernel.project_dir%/var/oauth/private.key'
|
|
||||||
private_key_passphrase: null # Passphrase of the private key, if any
|
|
||||||
|
|
||||||
encryption_key: '%env(string:OAUTH2_ENCRYPTION_KEY)%' # (Optional) Change this
|
|
||||||
|
|
||||||
grant_types:
|
|
||||||
authorization_code:
|
|
||||||
enable: true
|
|
||||||
client_credentials:
|
|
||||||
enable: true
|
|
||||||
implicit:
|
|
||||||
enable: true
|
|
||||||
password:
|
|
||||||
enable: true
|
|
||||||
refresh_token:
|
|
||||||
enable: true
|
|
||||||
|
|
||||||
resource_server:
|
|
||||||
public_key: '%kernel.project_dir%/var/oauth/public.key'
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
doctrine: null
|
|
||||||
|
|
||||||
# Scopes that you wish to utilize in your application.
|
|
||||||
# This should be a simple array of strings.
|
|
||||||
scopes:
|
|
||||||
- read
|
|
||||||
- write
|
|
||||||
- follow
|
|
@ -1,7 +0,0 @@
|
|||||||
oauth2_authorization_code:
|
|
||||||
controller: Trikoder\Bundle\OAuth2Bundle\Controller\AuthorizationController::indexAction
|
|
||||||
path: '/oauth/authorize'
|
|
||||||
|
|
||||||
oauth2_token:
|
|
||||||
controller: Trikoder\Bundle\OAuth2Bundle\Controller\TokenController::indexAction
|
|
||||||
path: '/oauth/token'
|
|
85
plugins/IndieAuth/Controller/Apps.php
Normal file
85
plugins/IndieAuth/Controller/Apps.php
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types = 1);
|
||||||
|
|
||||||
|
// {{{ License
|
||||||
|
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||||
|
//
|
||||||
|
// GNU social is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// GNU social is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
// }}}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ActivityPub implementation for GNU social
|
||||||
|
*
|
||||||
|
* @package OAuth2
|
||||||
|
* @category API
|
||||||
|
*
|
||||||
|
* @author Diogo Peralta Cordeiro <@diogo.site>
|
||||||
|
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||||
|
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Plugin\IndieAuth\Controller;
|
||||||
|
|
||||||
|
use App\Core\Controller;
|
||||||
|
use App\Core\DB\DB;
|
||||||
|
use App\Core\Log;
|
||||||
|
use App\Util\Common;
|
||||||
|
use Plugin\IndieAuth\Entity\OAuth2Client;
|
||||||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* App Management Endpoint
|
||||||
|
*
|
||||||
|
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||||
|
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||||
|
*/
|
||||||
|
class Apps extends Controller
|
||||||
|
{
|
||||||
|
public function onPost(): JsonResponse
|
||||||
|
{
|
||||||
|
Log::debug('OAuth2 Apps: Received a POST request.');
|
||||||
|
Log::debug('OAuth2 Apps: Request content: ', [$body = $this->request->getContent()]);
|
||||||
|
$args = json_decode($body, true);
|
||||||
|
|
||||||
|
$identifier = hash('md5', random_bytes(16));
|
||||||
|
// Random string Length should be between 43 and 128
|
||||||
|
$secret = Common::base64url_encode(hash('sha256', random_bytes(57)));
|
||||||
|
|
||||||
|
DB::persist($app = OAuth2Client::create([
|
||||||
|
'identifier' => $identifier,
|
||||||
|
'secret' => $secret,
|
||||||
|
'redirect_uris' => $args['redirect_uris'],
|
||||||
|
'grants' => 'client_credentials authorization_code',
|
||||||
|
'scopes' => $args['scopes'],
|
||||||
|
'active' => true,
|
||||||
|
'allow_plain_text_pkce' => false,
|
||||||
|
'client_name' => $args['client_name'],
|
||||||
|
'website' => $args['website'],
|
||||||
|
]));
|
||||||
|
|
||||||
|
Log::debug('OAuth2 Apps: Created App: ', [$app]);
|
||||||
|
|
||||||
|
DB::flush();
|
||||||
|
|
||||||
|
// Success
|
||||||
|
return new JsonResponse([
|
||||||
|
'name' => $app->getClientName(),
|
||||||
|
'website' => $app->getWebsite(),
|
||||||
|
'redirect_uri' => $app->getRedirectUris()[0],
|
||||||
|
'client_id' => $app->getIdentifier(),
|
||||||
|
'client_secret' => $app->getSecret(),
|
||||||
|
], status: 200, headers: ['content_type' => 'application/json; charset=utf-8']);
|
||||||
|
}
|
||||||
|
}
|
70
plugins/IndieAuth/Controller/OAuth2.php
Normal file
70
plugins/IndieAuth/Controller/OAuth2.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types = 1);
|
||||||
|
|
||||||
|
// {{{ License
|
||||||
|
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||||
|
//
|
||||||
|
// GNU social is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// GNU social is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
// }}}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ActivityPub implementation for GNU social
|
||||||
|
*
|
||||||
|
* @package OAuth2
|
||||||
|
* @category API
|
||||||
|
*
|
||||||
|
* @author Diogo Peralta Cordeiro <@diogo.site>
|
||||||
|
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||||
|
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Plugin\IndieAuth\Controller;
|
||||||
|
|
||||||
|
use App\Core\Controller;
|
||||||
|
use Nyholm\Psr7\Factory\Psr17Factory;
|
||||||
|
use Plugin\IndieAuth\IndieAuth;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
|
||||||
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* App Management Endpoint
|
||||||
|
*
|
||||||
|
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||||
|
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||||
|
*/
|
||||||
|
class OAuth2 extends Controller
|
||||||
|
{
|
||||||
|
private ServerRequestInterface $psrRequest;
|
||||||
|
|
||||||
|
public function __construct(RequestStack $requestStack)
|
||||||
|
{
|
||||||
|
parent::__construct($requestStack);
|
||||||
|
$psr17Factory = new Psr17Factory();
|
||||||
|
$psrHttpFactory = new PsrHttpFactory($psr17Factory, $psr17Factory, $psr17Factory, $psr17Factory);
|
||||||
|
$this->psrRequest = $psrHttpFactory->createRequest($this->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleAuthorizationEndpointRequest(): ResponseInterface
|
||||||
|
{
|
||||||
|
return IndieAuth::$server->handleAuthorizationEndpointRequest($this->psrRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleTokenEndpointRequest(): ResponseInterface
|
||||||
|
{
|
||||||
|
return IndieAuth::$server->handleTokenEndpointRequest($this->psrRequest);
|
||||||
|
}
|
||||||
|
}
|
225
plugins/IndieAuth/Entity/OAuth2Client.php
Normal file
225
plugins/IndieAuth/Entity/OAuth2Client.php
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types = 1);
|
||||||
|
|
||||||
|
// {{{ License
|
||||||
|
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||||
|
//
|
||||||
|
// GNU social is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// GNU social is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
// }}}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ActivityPub implementation for GNU social
|
||||||
|
*
|
||||||
|
* @package GNUsocial
|
||||||
|
* @category OAuth2
|
||||||
|
*
|
||||||
|
* @author Diogo Peralta Cordeiro <@diogo.site>
|
||||||
|
* @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||||
|
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Plugin\IndieAuth\Entity;
|
||||||
|
|
||||||
|
use App\Core\Entity;
|
||||||
|
use DateTimeInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OAuth application registration record
|
||||||
|
*
|
||||||
|
* @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||||
|
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||||
|
*/
|
||||||
|
class OAuth2Client extends Entity
|
||||||
|
{
|
||||||
|
// {{{ Autocode
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
private string $identifier;
|
||||||
|
private ?string $secret;
|
||||||
|
private string $redirect_uris = '';
|
||||||
|
private string $grants = '';
|
||||||
|
private string $scopes = '';
|
||||||
|
private bool $active = true;
|
||||||
|
private bool $allow_plain_text_pkce = false;
|
||||||
|
private ?string $client_name = null;
|
||||||
|
private ?string $website = null;
|
||||||
|
private DateTimeInterface $created;
|
||||||
|
private DateTimeInterface $modified;
|
||||||
|
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return $this->getIdentifier();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIdentifier(): string
|
||||||
|
{
|
||||||
|
return $this->identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSecret(): ?string
|
||||||
|
{
|
||||||
|
return $this->secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSecret(?string $secret): self
|
||||||
|
{
|
||||||
|
$this->secret = $secret;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRedirectUris(): array
|
||||||
|
{
|
||||||
|
return explode(' ', $this->redirect_uris);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRedirectUris(string ...$redirect_uris): self
|
||||||
|
{
|
||||||
|
$this->redirect_uris = implode(' ', $redirect_uris);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGrants(): array
|
||||||
|
{
|
||||||
|
return explode(' ', $this->grants);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setGrants(string ...$grants): self
|
||||||
|
{
|
||||||
|
$this->grants = implode(' ', $grants);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getScopes(): array
|
||||||
|
{
|
||||||
|
return explode(' ', $this->scopes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setScopes(string ...$scopes): self
|
||||||
|
{
|
||||||
|
$this->scopes = implode(' ', $scopes);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isActive(): bool
|
||||||
|
{
|
||||||
|
return $this->active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setActive(bool $active): self
|
||||||
|
{
|
||||||
|
$this->active = $active;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isConfidential(): bool
|
||||||
|
{
|
||||||
|
return !empty($this->secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isPlainTextPkceAllowed(): bool
|
||||||
|
{
|
||||||
|
return $this->allow_plain_text_pkce;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAllowPlainTextPkce(bool $allow_plain_text_pkce): self
|
||||||
|
{
|
||||||
|
$this->allow_plain_text_pkce = $allow_plain_text_pkce;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setIdentifier(string $identifier): self
|
||||||
|
{
|
||||||
|
$this->identifier = $identifier;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClientName(): string
|
||||||
|
{
|
||||||
|
return $this->client_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setClientName(string $client_name): self
|
||||||
|
{
|
||||||
|
$this->client_name = $client_name;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getWebsite(): ?string
|
||||||
|
{
|
||||||
|
return $this->website;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setWebsite(?string $website): self
|
||||||
|
{
|
||||||
|
$this->website = $website;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCreated(DateTimeInterface $created): self
|
||||||
|
{
|
||||||
|
$this->created = $created;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCreated(): DateTimeInterface
|
||||||
|
{
|
||||||
|
return $this->created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setModified(DateTimeInterface $modified): self
|
||||||
|
{
|
||||||
|
$this->modified = $modified;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getModified(): DateTimeInterface
|
||||||
|
{
|
||||||
|
return $this->modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
// }}} Autocode
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return table definition for Schema setup and Entity usage.
|
||||||
|
*
|
||||||
|
* @return array array of column definitions
|
||||||
|
*/
|
||||||
|
public static function schemaDef(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'oauth2_client',
|
||||||
|
'fields' => [
|
||||||
|
'identifier' => ['type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'foreign key to oauth2_client->identifier'],
|
||||||
|
'secret' => ['type' => 'varchar', 'length' => 128, 'not null' => false, 'description' => 'foreign key to oauth2_client->identifier'],
|
||||||
|
'client_name' => ['type' => 'varchar', 'length' => 191, 'not null' => false, 'description' => 'name of the application'],
|
||||||
|
'redirect_uris' => ['type' => 'text', 'not null' => false, 'description' => 'application homepage - used for source link'],
|
||||||
|
'grants' => ['type' => 'text', 'not null' => true, 'default' => '', 'description' => 'application homepage - used for source link'],
|
||||||
|
'scopes' => ['type' => 'text', 'not null' => true, 'default' => '', 'description' => 'application homepage - used for source link'],
|
||||||
|
'active' => ['type' => 'bool', 'not null' => true, 'description' => 'was this note generated by a local actor'],
|
||||||
|
'allow_plain_text_pkce' => ['type' => 'bool', 'not null' => true, 'default' => false, 'description' => 'was this note generated by a local actor'],
|
||||||
|
'website' => ['type' => 'text', 'not null' => false, 'description' => 'application homepage - used for source link'],
|
||||||
|
'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
|
||||||
|
'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
|
||||||
|
],
|
||||||
|
'primary key' => ['identifier'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -30,21 +30,19 @@ declare(strict_types = 1);
|
|||||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Plugin\OAuth2;
|
namespace Plugin\IndieAuth;
|
||||||
|
|
||||||
use App\Core\Event;
|
use App\Core\Event;
|
||||||
|
use App\Core\Log;
|
||||||
use App\Core\Modules\Plugin;
|
use App\Core\Modules\Plugin;
|
||||||
use App\Core\Router\RouteLoader;
|
use App\Core\Router\RouteLoader;
|
||||||
use App\Core\Router\Router;
|
use App\Core\Router\Router;
|
||||||
use App\Util\Common;
|
use App\Util\Common;
|
||||||
use App\Util\Exception\NoLoggedInUser;
|
|
||||||
use Nyholm\Psr7\Response;
|
use Nyholm\Psr7\Response;
|
||||||
use Plugin\OAuth2\Controller\Apps;
|
use Plugin\IndieAuth\Controller\Apps;
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
use Plugin\IndieAuth\Controller\OAuth2;
|
||||||
use Trikoder\Bundle\OAuth2Bundle\Event\AuthorizationRequestResolveEvent;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
use Trikoder\Bundle\OAuth2Bundle\Event\UserResolveEvent;
|
use Taproot\IndieAuth\Server;
|
||||||
use Trikoder\Bundle\OAuth2Bundle\Model\Grant;
|
|
||||||
use Trikoder\Bundle\OAuth2Bundle\OAuth2Events;
|
|
||||||
use XML_XRD_Element_Link;
|
use XML_XRD_Element_Link;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,12 +51,39 @@ use XML_XRD_Element_Link;
|
|||||||
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
|
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||||
*/
|
*/
|
||||||
class OAuth2 extends Plugin implements EventSubscriberInterface
|
class IndieAuth extends Plugin
|
||||||
{
|
{
|
||||||
public const OAUTH_ACCESS_TOKEN_REL = 'http://apinamespace.org/oauth/access_token';
|
public const OAUTH_ACCESS_TOKEN_REL = 'http://apinamespace.org/oauth/access_token';
|
||||||
public const OAUTH_REQUEST_TOKEN_REL = 'http://apinamespace.org/oauth/request_token';
|
public const OAUTH_REQUEST_TOKEN_REL = 'http://apinamespace.org/oauth/request_token';
|
||||||
public const OAUTH_AUTHORIZE_REL = 'http://apinamespace.org/oauth/authorize';
|
public const OAUTH_AUTHORIZE_REL = 'http://apinamespace.org/oauth/authorize';
|
||||||
|
|
||||||
|
public static Server $server;
|
||||||
|
|
||||||
|
public function onInitializePlugin()
|
||||||
|
{
|
||||||
|
self::$server = new Server([
|
||||||
|
'secret' => 'YOUR_APP_INDIEAUTH_SECRET$config["secret"] must be a string with a minimum length of 64 characters.yeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
|
||||||
|
'logger' => Log::getLogger(),
|
||||||
|
'requirePKCE' => false,
|
||||||
|
// A path to store token data, or an object implementing TokenStorageInterface.
|
||||||
|
'tokenStorage' => '/../data/auth_tokens/',
|
||||||
|
|
||||||
|
// An authentication callback function, which either returns data about the current user,
|
||||||
|
// or redirects to/implements an authentication flow.
|
||||||
|
'authenticationHandler' => function (ServerRequestInterface $request, string $authenticationRedirect, ?string $normalizedMeUrl) {
|
||||||
|
// If the request is authenticated, return an array with a `me` key containing the
|
||||||
|
// canonical URL of the currently logged-in user.
|
||||||
|
if ($actor = Common::actor()) {
|
||||||
|
return ['me' => $actor->getUri(Router::ABSOLUTE_URL)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, redirect the user to a login page, ensuring that they will be redirected
|
||||||
|
// back to the IndieAuth flow with query parameters intact once logged in.
|
||||||
|
return new Response(302, ['Location' => Router::url('security_login') . '?returnUrl=' . urlencode($authenticationRedirect)]);
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public function version(): string
|
public function version(): string
|
||||||
{
|
{
|
||||||
return '3.0.0';
|
return '3.0.0';
|
||||||
@ -78,6 +103,19 @@ class OAuth2 extends Plugin implements EventSubscriberInterface
|
|||||||
Apps::class,
|
Apps::class,
|
||||||
['http-methods' => ['POST']],
|
['http-methods' => ['POST']],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$r->connect(
|
||||||
|
'oauth2_authorization_code',
|
||||||
|
'/oauth/authorize',
|
||||||
|
[OAuth2::class, 'handleAuthorizationEndpointRequest'],
|
||||||
|
);
|
||||||
|
|
||||||
|
$r->connect(
|
||||||
|
'oauth2_token',
|
||||||
|
'/oauth/token',
|
||||||
|
[OAuth2::class, 'handleTokenEndpointRequest'],
|
||||||
|
);
|
||||||
|
|
||||||
return Event::next;
|
return Event::next;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,40 +126,4 @@ class OAuth2 extends Plugin implements EventSubscriberInterface
|
|||||||
$links[] = new XML_XRD_Element_link(self::OAUTH_ACCESS_TOKEN_REL, Router::url('oauth2_token', type: Router::ABSOLUTE_URL));
|
$links[] = new XML_XRD_Element_link(self::OAUTH_ACCESS_TOKEN_REL, Router::url('oauth2_token', type: Router::ABSOLUTE_URL));
|
||||||
return Event::next;
|
return Event::next;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function userResolve(UserResolveEvent $event): void
|
|
||||||
{
|
|
||||||
$user = Common::user();
|
|
||||||
|
|
||||||
if (\is_null($user)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$event->setUser($user);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function authorizeRequestResolve(AuthorizationRequestResolveEvent $event): void
|
|
||||||
{
|
|
||||||
$request = Common::getRequest();
|
|
||||||
try {
|
|
||||||
$user = Common::ensureLoggedIn();
|
|
||||||
$event->setUser($user);
|
|
||||||
$event->resolveAuthorization(AuthorizationRequestResolveEvent::AUTHORIZATION_APPROVED);
|
|
||||||
$event->getClient()->setGrants(new Grant('client_credentials'), new Grant('authorization_code'));
|
|
||||||
} catch (NoLoggedInUser) {
|
|
||||||
$event->setResponse(new Response(302, [
|
|
||||||
'Location' => Router::url('security_login', [
|
|
||||||
'returnUrl' => $request->getUri(),
|
|
||||||
]),
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getSubscribedEvents(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
OAuth2Events::USER_RESOLVE => 'userResolve',
|
|
||||||
OAuth2Events::AUTHORIZATION_REQUEST_RESOLVE => 'authorizeRequestResolve',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
7
plugins/IndieAuth/composer.json
Normal file
7
plugins/IndieAuth/composer.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"require": {
|
||||||
|
"nyholm/psr7": "^1.4",
|
||||||
|
"symfony/psr-http-message-bridge": "^2.1",
|
||||||
|
"taproot/indieauth": "^0.1.0"
|
||||||
|
}
|
||||||
|
}
|
@ -1,106 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types = 1);
|
|
||||||
|
|
||||||
// {{{ License
|
|
||||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
|
||||||
//
|
|
||||||
// GNU social is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// GNU social is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
// }}}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ActivityPub implementation for GNU social
|
|
||||||
*
|
|
||||||
* @package OAuth2
|
|
||||||
* @category API
|
|
||||||
*
|
|
||||||
* @author Diogo Peralta Cordeiro <@diogo.site>
|
|
||||||
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
|
|
||||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Plugin\OAuth2\Controller;
|
|
||||||
|
|
||||||
use App\Core\Controller;
|
|
||||||
use App\Core\DB\DB;
|
|
||||||
use App\Core\Log;
|
|
||||||
use App\Util\Common;
|
|
||||||
use Plugin\OAuth2\Entity\OAuth2ClientMeta;
|
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
|
||||||
use Trikoder\Bundle\OAuth2Bundle\Model\Client;
|
|
||||||
use Trikoder\Bundle\OAuth2Bundle\Model\Grant;
|
|
||||||
use Trikoder\Bundle\OAuth2Bundle\Model\RedirectUri;
|
|
||||||
use Trikoder\Bundle\OAuth2Bundle\Model\Scope;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* App Management Endpoint
|
|
||||||
*
|
|
||||||
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
|
|
||||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
|
||||||
*/
|
|
||||||
class Apps extends Controller
|
|
||||||
{
|
|
||||||
public function onPost(): JsonResponse
|
|
||||||
{
|
|
||||||
Log::debug('OAuth2 Apps: Received a POST request.');
|
|
||||||
Log::debug('OAuth2 Apps: Request content: ', [$body = $this->request->getContent()]);
|
|
||||||
$args = json_decode($body, true);
|
|
||||||
|
|
||||||
$identifier = hash('md5', random_bytes(16)); // Random string Length should be between 43 and 128
|
|
||||||
$secret = Common::base64url_encode(hash('sha256', random_bytes(57)));
|
|
||||||
|
|
||||||
$client = new Client($identifier, $secret);
|
|
||||||
$client->setActive(true);
|
|
||||||
$client->setAllowPlainTextPkce(false);
|
|
||||||
|
|
||||||
$redirectUris = array_map(
|
|
||||||
static fn (string $redirectUri): RedirectUri => new RedirectUri($redirectUri),
|
|
||||||
explode(' ', $args['redirect_uris']),
|
|
||||||
);
|
|
||||||
$client->setRedirectUris(...$redirectUris);
|
|
||||||
|
|
||||||
$client->setGrants(new Grant('client_credentials'));
|
|
||||||
|
|
||||||
$scopes = array_map(
|
|
||||||
static fn (string $scope): Scope => new Scope($scope),
|
|
||||||
explode(' ', $args['scopes']),
|
|
||||||
);
|
|
||||||
$client->setScopes(...$scopes);
|
|
||||||
|
|
||||||
DB::persist($client);
|
|
||||||
|
|
||||||
DB::persist($additional_meta = OAuth2ClientMeta::create([
|
|
||||||
'identifier' => $client->getIdentifier(),
|
|
||||||
'client_name' => $args['client_name'],
|
|
||||||
'website' => $args['website'],
|
|
||||||
]));
|
|
||||||
|
|
||||||
Log::debug('OAuth2 Apps: Created App: ', [$client, $additional_meta]);
|
|
||||||
$app_meta = [
|
|
||||||
'id' => (string) $additional_meta->getId(),
|
|
||||||
'name' => $additional_meta->getClientName(),
|
|
||||||
'website' => $additional_meta->getWebsite(),
|
|
||||||
'redirect_uri' => (string) $client->getRedirectUris()[0],
|
|
||||||
'client_id' => $client->getIdentifier(),
|
|
||||||
'client_secret' => $client->getSecret(),
|
|
||||||
];
|
|
||||||
|
|
||||||
Log::debug('OAuth2 Apps: Create App Meta: ', [$app_meta]);
|
|
||||||
|
|
||||||
DB::flush();
|
|
||||||
|
|
||||||
// Success
|
|
||||||
return new JsonResponse($app_meta, status: 200, headers: ['content_type' => 'application/json; charset=utf-8']);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,144 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types = 1);
|
|
||||||
|
|
||||||
// {{{ License
|
|
||||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
|
||||||
//
|
|
||||||
// GNU social is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// GNU social is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
// }}}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ActivityPub implementation for GNU social
|
|
||||||
*
|
|
||||||
* @package GNUsocial
|
|
||||||
* @category OAuth2
|
|
||||||
*
|
|
||||||
* @author Diogo Peralta Cordeiro <@diogo.site>
|
|
||||||
* @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org
|
|
||||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Plugin\OAuth2\Entity;
|
|
||||||
|
|
||||||
use App\Core\Entity;
|
|
||||||
use DateTimeInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OAuth application registration record
|
|
||||||
*
|
|
||||||
* @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org
|
|
||||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
|
||||||
*/
|
|
||||||
class OAuth2ClientMeta extends Entity
|
|
||||||
{
|
|
||||||
// {{{ Autocode
|
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
private int $id;
|
|
||||||
private string $identifier;
|
|
||||||
private string $client_name;
|
|
||||||
private ?string $website = null;
|
|
||||||
private DateTimeInterface $created;
|
|
||||||
private DateTimeInterface $modified;
|
|
||||||
|
|
||||||
public function getId(): int
|
|
||||||
{
|
|
||||||
return $this->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setId(int $id): self
|
|
||||||
{
|
|
||||||
$this->id = $id;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getIdentifier(): string
|
|
||||||
{
|
|
||||||
return $this->identifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setIdentifier(string $identifier): self
|
|
||||||
{
|
|
||||||
$this->identifier = $identifier;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getClientName(): string
|
|
||||||
{
|
|
||||||
return $this->client_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setClientName(string $client_name): self
|
|
||||||
{
|
|
||||||
$this->client_name = $client_name;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getWebsite(): ?string
|
|
||||||
{
|
|
||||||
return $this->website;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setWebsite(?string $website): self
|
|
||||||
{
|
|
||||||
$this->website = $website;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setCreated(DateTimeInterface $created): self
|
|
||||||
{
|
|
||||||
$this->created = $created;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getCreated(): DateTimeInterface
|
|
||||||
{
|
|
||||||
return $this->created;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setModified(DateTimeInterface $modified): self
|
|
||||||
{
|
|
||||||
$this->modified = $modified;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getModified(): DateTimeInterface
|
|
||||||
{
|
|
||||||
return $this->modified;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
// }}} Autocode
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return table definition for Schema setup and Entity usage.
|
|
||||||
*
|
|
||||||
* @return array array of column definitions
|
|
||||||
*/
|
|
||||||
public static function schemaDef(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'name' => 'oauth2_client_meta',
|
|
||||||
'fields' => [
|
|
||||||
'id' => ['type' => 'serial', 'not null' => true, 'description' => 'unique identifier'],
|
|
||||||
'identifier' => ['type' => 'varchar', 'length' => 32, 'description' => 'foreign key to oauth2_client->identifier'],
|
|
||||||
'client_name' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'name of the application'],
|
|
||||||
'website' => ['type' => 'text', 'not null' => false, 'description' => 'application homepage - used for source link'],
|
|
||||||
'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
|
|
||||||
'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
|
|
||||||
],
|
|
||||||
'primary key' => ['id'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"require": {
|
|
||||||
"nyholm/psr7": "*",
|
|
||||||
"symfony-bundles/json-request-bundle": "^4.1",
|
|
||||||
"trikoder/oauth2-bundle": "v3.x-dev"
|
|
||||||
}
|
|
||||||
}
|
|
@ -58,6 +58,11 @@ abstract class Log
|
|||||||
self::$logger = $l;
|
self::$logger = $l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getLogger(): LoggerInterface
|
||||||
|
{
|
||||||
|
return self::$logger;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log a critical error when a really unexpected exception occured. This indicates a bug in the software
|
* Log a critical error when a really unexpected exception occured. This indicates a bug in the software
|
||||||
*
|
*
|
||||||
|
62
symfony.lock
62
symfony.lock
@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"ajgarlag/psr-http-message-bundle": {
|
|
||||||
"version": "1.2.1"
|
|
||||||
},
|
|
||||||
"alchemy/binary-driver": {
|
"alchemy/binary-driver": {
|
||||||
"version": "v5.2.0"
|
"version": "v5.2.0"
|
||||||
},
|
},
|
||||||
|
"barnabywalters/mf-cleaner": {
|
||||||
|
"version": "v0.1.4"
|
||||||
|
},
|
||||||
"behat/gherkin": {
|
"behat/gherkin": {
|
||||||
"version": "v4.9.0"
|
"version": "v4.9.0"
|
||||||
},
|
},
|
||||||
@ -68,8 +68,8 @@
|
|||||||
"composer/xdebug-handler": {
|
"composer/xdebug-handler": {
|
||||||
"version": "1.4.6"
|
"version": "1.4.6"
|
||||||
},
|
},
|
||||||
"defuse/php-encryption": {
|
"dflydev/fig-cookies": {
|
||||||
"version": "v2.3.1"
|
"version": "v3.0.0"
|
||||||
},
|
},
|
||||||
"doctrine/annotations": {
|
"doctrine/annotations": {
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
@ -208,6 +208,15 @@
|
|||||||
"guzzlehttp/psr7": {
|
"guzzlehttp/psr7": {
|
||||||
"version": "2.1.0"
|
"version": "2.1.0"
|
||||||
},
|
},
|
||||||
|
"indieauth/client": {
|
||||||
|
"version": "1.1.5"
|
||||||
|
},
|
||||||
|
"indieweb/link-rel-parser": {
|
||||||
|
"version": "0.1.3"
|
||||||
|
},
|
||||||
|
"indieweb/representative-h-card": {
|
||||||
|
"version": "0.1.2"
|
||||||
|
},
|
||||||
"jchook/phpunit-assert-throws": {
|
"jchook/phpunit-assert-throws": {
|
||||||
"version": "v1.0.3"
|
"version": "v1.0.3"
|
||||||
},
|
},
|
||||||
@ -229,18 +238,6 @@
|
|||||||
"landrok/activitypub": {
|
"landrok/activitypub": {
|
||||||
"version": "0.5.6"
|
"version": "0.5.6"
|
||||||
},
|
},
|
||||||
"lcobucci/clock": {
|
|
||||||
"version": "2.1.0"
|
|
||||||
},
|
|
||||||
"lcobucci/jwt": {
|
|
||||||
"version": "4.1.5"
|
|
||||||
},
|
|
||||||
"league/event": {
|
|
||||||
"version": "2.2.0"
|
|
||||||
},
|
|
||||||
"league/oauth2-server": {
|
|
||||||
"version": "8.3.3"
|
|
||||||
},
|
|
||||||
"league/uri-parser": {
|
"league/uri-parser": {
|
||||||
"version": "1.4.1"
|
"version": "1.4.1"
|
||||||
},
|
},
|
||||||
@ -301,6 +298,9 @@
|
|||||||
"oscarotero/html-parser": {
|
"oscarotero/html-parser": {
|
||||||
"version": "v0.1.6"
|
"version": "v0.1.6"
|
||||||
},
|
},
|
||||||
|
"p3k/http": {
|
||||||
|
"version": "0.1.12"
|
||||||
|
},
|
||||||
"paragonie/constant_time_encoding": {
|
"paragonie/constant_time_encoding": {
|
||||||
"version": "v2.4.0"
|
"version": "v2.4.0"
|
||||||
},
|
},
|
||||||
@ -390,6 +390,12 @@
|
|||||||
"psr/http-message": {
|
"psr/http-message": {
|
||||||
"version": "1.0.1"
|
"version": "1.0.1"
|
||||||
},
|
},
|
||||||
|
"psr/http-server-handler": {
|
||||||
|
"version": "1.0.1"
|
||||||
|
},
|
||||||
|
"psr/http-server-middleware": {
|
||||||
|
"version": "1.0.1"
|
||||||
|
},
|
||||||
"psr/link": {
|
"psr/link": {
|
||||||
"version": "1.1.1"
|
"version": "1.1.1"
|
||||||
},
|
},
|
||||||
@ -468,9 +474,6 @@
|
|||||||
"someonewithpc/redis-polyfill": {
|
"someonewithpc/redis-polyfill": {
|
||||||
"version": "dev-master"
|
"version": "dev-master"
|
||||||
},
|
},
|
||||||
"symfony-bundles/json-request-bundle": {
|
|
||||||
"version": "4.1.1"
|
|
||||||
},
|
|
||||||
"symfony/amqp-messenger": {
|
"symfony/amqp-messenger": {
|
||||||
"version": "v5.4.0"
|
"version": "v5.4.0"
|
||||||
},
|
},
|
||||||
@ -868,6 +871,9 @@
|
|||||||
"symfonycasts/verify-email-bundle": {
|
"symfonycasts/verify-email-bundle": {
|
||||||
"version": "v1.3.0"
|
"version": "v1.3.0"
|
||||||
},
|
},
|
||||||
|
"taproot/indieauth": {
|
||||||
|
"version": "v0.1.0"
|
||||||
|
},
|
||||||
"tgalopin/html-sanitizer": {
|
"tgalopin/html-sanitizer": {
|
||||||
"version": "1.4.0"
|
"version": "1.4.0"
|
||||||
},
|
},
|
||||||
@ -886,19 +892,6 @@
|
|||||||
"theseer/tokenizer": {
|
"theseer/tokenizer": {
|
||||||
"version": "1.2.0"
|
"version": "1.2.0"
|
||||||
},
|
},
|
||||||
"trikoder/oauth2-bundle": {
|
|
||||||
"version": "3.2",
|
|
||||||
"recipe": {
|
|
||||||
"repo": "github.com/symfony/recipes-contrib",
|
|
||||||
"branch": "master",
|
|
||||||
"version": "3.0",
|
|
||||||
"ref": "43774de114904e86d18de0cea92f368d8e3c7499"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"config/packages/trikoder_oauth2.yaml",
|
|
||||||
"config/routes/trikoder_oauth2.yaml"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"twig/extra-bundle": {
|
"twig/extra-bundle": {
|
||||||
"version": "v3.3.0"
|
"version": "v3.3.0"
|
||||||
},
|
},
|
||||||
@ -923,6 +916,9 @@
|
|||||||
"webmozart/assert": {
|
"webmozart/assert": {
|
||||||
"version": "1.10.0"
|
"version": "1.10.0"
|
||||||
},
|
},
|
||||||
|
"webmozart/path-util": {
|
||||||
|
"version": "2.3.0"
|
||||||
|
},
|
||||||
"wikimedia/composer-merge-plugin": {
|
"wikimedia/composer-merge-plugin": {
|
||||||
"version": "v2.0.1"
|
"version": "v2.0.1"
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user