Add DeleteClient command and enhance token settings management
Some checks failed
🧪✨ Tests Workflow / 🧪 ✨ Database Migrations (push) Failing after -33s
🧪✨ Tests Workflow / 🛡️ 🔒 License Check (push) Successful in -36s
🧪✨ Tests Workflow / 🐙 🔍 Code Sniffer (push) Has been cancelled
🧪✨ Tests Workflow / 📝 ✨ Code Lint (push) Has been cancelled
🧪✨ Tests Workflow / 🧪 ✅ Unit Tests (push) Has been cancelled
🧪✨ Tests Workflow / 🛡️ 🔒 Library Audit (push) Has been cancelled

This commit is contained in:
2026-01-29 23:00:01 -05:00
parent 96409973bf
commit 8f5f57f5f6
6 changed files with 86 additions and 7 deletions

View File

@@ -9,6 +9,7 @@ use Siteworxpro\App\Cli\Commands\OAuth\AddRedirectUri;
use Siteworxpro\App\Cli\Commands\OAuth\AddScope; use Siteworxpro\App\Cli\Commands\OAuth\AddScope;
use Siteworxpro\App\Cli\Commands\OAuth\ClientCapabilities; use Siteworxpro\App\Cli\Commands\OAuth\ClientCapabilities;
use Siteworxpro\App\Cli\Commands\OAuth\CreateClient; use Siteworxpro\App\Cli\Commands\OAuth\CreateClient;
use Siteworxpro\App\Cli\Commands\OAuth\DeleteClient;
use Siteworxpro\App\Cli\Commands\OAuth\ListClients; use Siteworxpro\App\Cli\Commands\OAuth\ListClients;
use Siteworxpro\App\Cli\Commands\Queue\Start; use Siteworxpro\App\Cli\Commands\Queue\Start;
use Siteworxpro\App\Cli\Commands\User\Add; use Siteworxpro\App\Cli\Commands\User\Add;
@@ -42,6 +43,7 @@ class App
$this->app->addCommand(new AddScope()); $this->app->addCommand(new AddScope());
$this->app->addCommand(new ResetPassword()); $this->app->addCommand(new ResetPassword());
$this->app->addCommand(new ClientCapabilities()); $this->app->addCommand(new ClientCapabilities());
$this->app->addCommand(new DeleteClient());
} }
public function run(): int public function run(): int

View File

@@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Cli\Commands\OAuth;
use Siteworxpro\App\Cli\ClimateOutput;
use Siteworxpro\App\Cli\Commands\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\ArgvInput;
#[AsCommand('oauth:client:delete', 'Delete an OAuth client')]
class DeleteClient extends Command
{
public function __invoke(ClimateOutput|ArgvInput $input, $output): int
{
$client = $this->askForClient($output, $input);
if ($client === null) {
$output->red('No client selected, aborting.');
return \Symfony\Component\Console\Command\Command::FAILURE;
}
$output->red()->bold('You are about to delete the following OAuth client:');
$output->red("ID: $client->id");
$output->red("Name: $client->name");
$output->red("Description: $client->description");
$output
->br()
->backgroundRed()
->yellow()
->bold('This action is irreversible and will remove all associated data.');
$question = $this->helper->ask(
$input,
$output,
new \Symfony\Component\Console\Question\ConfirmationQuestion(
'Are you sure you want to proceed? (y/N): ',
false,
'/^(y|yes)/i'
)
);
if (!$question) {
$output->info('Operation cancelled by user.');
return \Symfony\Component\Console\Command\Command::SUCCESS;
}
$client->delete();
$output->green('OAuth client deleted successfully.');
return \Symfony\Component\Console\Command\Command::SUCCESS;
}
}

View File

@@ -46,7 +46,8 @@ class ListClients extends Command
'Access Token Url' => Config::get('app.url') . '/client/access_token', 'Access Token Url' => Config::get('app.url') . '/client/access_token',
'OAuth Config Url' => Config::get('app.url') . 'OAuth Config Url' => Config::get('app.url') .
'/client/' . $client->id . '/.well-known/openid-configuration', '/client/' . $client->id . '/.well-known/openid-configuration',
'Scopes' => $client->scopes->toArray() 'Scopes' => $client->scopes->toArray(),
'Capabilities' => $client->capabilities->toArray(),
]); ]);
return self::SUCCESS; return self::SUCCESS;

View File

@@ -40,7 +40,10 @@ final class AccessTokenController extends Controller
/** @var Response $response */ /** @var Response $response */
$response = $client $response = $client
->getAuthorizationServer() ->getAuthorizationServer()
->respondToAccessTokenRequest($request, JsonResponseFactory::createJsonResponse([])); ->respondToAccessTokenRequest(
$request,
JsonResponseFactory::createJsonResponse([])
);
Dispatcher::push(new Issued($response)); Dispatcher::push(new Issued($response));

View File

@@ -13,6 +13,7 @@ use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use League\OAuth2\Server\AuthorizationServer; use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\Traits\EntityTrait; use League\OAuth2\Server\Entities\Traits\EntityTrait;
use League\OAuth2\Server\Grant\ClientCredentialsGrant as ClientCredentialsGrant;
use Random\RandomException; use Random\RandomException;
use Siteworxpro\App\Helpers\Rand; use Siteworxpro\App\Helpers\Rand;
use Siteworxpro\App\Models\ClientRedirectUri; use Siteworxpro\App\Models\ClientRedirectUri;
@@ -195,6 +196,9 @@ class Client extends Model implements ClientEntityInterface
Key::loadFromAsciiSafeString($this->encryption_key) Key::loadFromAsciiSafeString($this->encryption_key)
); );
$accessTokenTtl = $this->capabilities->toArray()['tokenSettings']['accessTokenTTL'] ?? 'PT1H';
$refreshTokenTtl = $this->capabilities->toArray()['tokenSettings']['refreshTokenTTL'] ?? 'P1M';
if (!empty($this->grant_types)) { if (!empty($this->grant_types)) {
foreach ($this->grant_types as $grantType) { foreach ($this->grant_types as $grantType) {
switch ($grantType) { switch ($grantType) {
@@ -204,22 +208,22 @@ class Client extends Model implements ClientEntityInterface
new \Siteworxpro\App\OAuth\RefreshTokenRepository(), new \Siteworxpro\App\OAuth\RefreshTokenRepository(),
new \DateInterval('PT10M') new \DateInterval('PT10M')
); );
$grant->setRefreshTokenTTL(new \DateInterval('P1M')); $grant->setRefreshTokenTTL(new \DateInterval($refreshTokenTtl));
break; break;
case 'client_credentials': case 'client_credentials':
$grant = new \League\OAuth2\Server\Grant\ClientCredentialsGrant(); $grant = new ClientCredentialsGrant();
break; break;
case 'refresh_token': case 'refresh_token':
$grant = new \League\OAuth2\Server\Grant\RefreshTokenGrant( $grant = new \League\OAuth2\Server\Grant\RefreshTokenGrant(
new \Siteworxpro\App\OAuth\RefreshTokenRepository() new \Siteworxpro\App\OAuth\RefreshTokenRepository()
); );
$grant->setRefreshTokenTTL(new \DateInterval('P1M')); $grant->setRefreshTokenTTL(new \DateInterval($refreshTokenTtl));
break; break;
default: default:
continue 2; continue 2;
} }
$authorizationServer->enableGrantType($grant); $authorizationServer->enableGrantType($grant, new \DateInterval($accessTokenTtl));
} }
} }

View File

@@ -22,6 +22,11 @@ class ClientCapabilities implements Arrayable
'logoUrl' => null, 'logoUrl' => null,
]; ];
private array $tokenSettings = [
'accessTokenTTL' => 'PT1H',
'refreshTokenTTL' => 'P1M',
];
public function __construct(array $capabilities = []) public function __construct(array $capabilities = [])
{ {
if (isset($capabilities['userPass'])) { if (isset($capabilities['userPass'])) {
@@ -47,6 +52,10 @@ class ClientCapabilities implements Arrayable
if (isset($capabilities['support_email'])) { if (isset($capabilities['support_email'])) {
$this->support_email = (string)$capabilities['support_email']; $this->support_email = (string)$capabilities['support_email'];
} }
if (isset($capabilities['tokenSettings']) && is_array($capabilities['tokenSettings'])) {
$this->tokenSettings = array_merge($this->tokenSettings, $capabilities['tokenSettings']);
}
} }
public static function fromJson(string $data): self public static function fromJson(string $data): self
@@ -66,7 +75,8 @@ class ClientCapabilities implements Arrayable
'passkey' => "bool", 'passkey' => "bool",
'socials' => "array", 'socials' => "array",
'branding' => "array", 'branding' => "array",
'support_email' => "string" 'support_email' => "string",
'tokenSettings' => "array",
])] ])]
public function toArray(): array public function toArray(): array
{ {
@@ -77,6 +87,7 @@ class ClientCapabilities implements Arrayable
'socials' => $this->socials, 'socials' => $this->socials,
'branding' => $this->branding, 'branding' => $this->branding,
'support_email' => $this->support_email, 'support_email' => $this->support_email,
'tokenSettings' => $this->tokenSettings,
]; ];
} }