states, and pulls data from the authenticated /api/v1/tenant/packages endpoint.
(resources/js/admin/pages/EventFormPage.tsx, resources/js/admin/api.ts)
- Harden tenant-admin auth flow: prevent PKCE state loss, scope out StrictMode double-processing, add SPA
routes for /event-admin/login and /event-admin/logout, and tighten token/session clearing semantics (resources/js/admin/auth/{context,tokens}.tsx, resources/js/admin/pages/{AuthCallbackPage,LogoutPage}.tsx,
resources/js/admin/router.tsx, routes/web.php)
70 lines
2.4 KiB
PHP
70 lines
2.4 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\File;
|
|
use Illuminate\Support\Str;
|
|
|
|
class OAuthListKeysCommand extends Command
|
|
{
|
|
protected $signature = 'oauth:list-keys {--json : Output as JSON for scripting}';
|
|
|
|
protected $description = 'List available JWT signing key directories and their status.';
|
|
|
|
public function handle(): int
|
|
{
|
|
$storage = rtrim(config('oauth.keys.storage_path', storage_path('app/oauth-keys')), DIRECTORY_SEPARATOR);
|
|
$currentKid = config('oauth.keys.current_kid', 'fotospiel-jwt');
|
|
|
|
if (! File::exists($storage)) {
|
|
$this->error("Key store path does not exist: {$storage}");
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$directories = collect(File::directories($storage))
|
|
->filter(fn ($path) => Str::lower(basename($path)) !== 'archive')
|
|
->values()
|
|
->map(function (string $path) use ($currentKid) {
|
|
$kid = basename($path);
|
|
$publicKey = $path.DIRECTORY_SEPARATOR.'public.key';
|
|
$privateKey = $path.DIRECTORY_SEPARATOR.'private.key';
|
|
|
|
return [
|
|
'kid' => $kid,
|
|
'status' => $kid === $currentKid ? 'current' : 'legacy',
|
|
'public' => File::exists($publicKey),
|
|
'private' => File::exists($privateKey),
|
|
'updated_at' => File::exists($path) ? date('c', File::lastModified($path)) : null,
|
|
'path' => $path,
|
|
];
|
|
})
|
|
->sortBy(fn ($entry) => ($entry['status'] === 'current' ? '0-' : '1-').$entry['kid'])
|
|
->values();
|
|
|
|
if ($this->option('json')) {
|
|
$this->line($directories->toJson(JSON_PRETTY_PRINT));
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
if ($directories->isEmpty()) {
|
|
$this->warn('No signing key directories found.');
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
$this->table(
|
|
['KID', 'Status', 'Public.key', 'Private.key', 'Updated At', 'Path'],
|
|
$directories->map(fn ($entry) => [
|
|
$entry['kid'],
|
|
$entry['status'],
|
|
$entry['public'] ? 'yes' : 'no',
|
|
$entry['private'] ? 'yes' : 'no',
|
|
$entry['updated_at'] ?? 'n/a',
|
|
$entry['path'],
|
|
])
|
|
);
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
}
|