diff --git a/app/Console/Commands/Correction/RestoresOAuthKeys.php b/app/Console/Commands/Correction/RestoresOAuthKeys.php index 3d2c856555..ad430a73c3 100644 --- a/app/Console/Commands/Correction/RestoresOAuthKeys.php +++ b/app/Console/Commands/Correction/RestoresOAuthKeys.php @@ -28,6 +28,7 @@ use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Support\System\OAuthKeys; use Illuminate\Console\Command; use Illuminate\Support\Facades\Log; +use Laravel\Passport\Passport; class RestoresOAuthKeys extends Command { @@ -43,6 +44,12 @@ class RestoresOAuthKeys extends Command { Log::debug('Restore OAuth Keys command.'); $this->restoreOAuthKeys(); + + if (! windows_os()) { + chmod(Passport::keyPath('oauth-public.key'), 0660); + chmod(Passport::keyPath('oauth-private.key'), 0600); + } + Log::debug('Done with OAuth Keys command.'); return 0; diff --git a/app/Http/Controllers/Profile/OAuthController.php b/app/Http/Controllers/Profile/OAuthController.php new file mode 100644 index 0000000000..ac2bb7a65f --- /dev/null +++ b/app/Http/Controllers/Profile/OAuthController.php @@ -0,0 +1,51 @@ +. + */ + +namespace FireflyIII\Http\Controllers\Profile; + +use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Http\Middleware\IsDemoUser; +use Illuminate\Support\Facades\Log; + +class OAuthController extends Controller +{ + protected bool $internalAuth; + public function __construct() + { + parent::__construct(); + + $this->middleware(static function ($request, $next) { + app('view')->share('title', (string) trans('firefly.oauth_tokens')); + app('view')->share('mainTitleIcon', 'fa-user'); + + return $next($request); + }); + $authGuard = config('firefly.authentication_guard'); + $this->internalAuth = 'web' === $authGuard; + Log::debug(sprintf('ProfileController::__construct(). Authentication guard is "%s"', $authGuard)); + + $this->middleware(IsDemoUser::class)->except(['index']); + } + + public function index() { + return view('profile.oauth.index'); + } +} diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 30366a21b0..c764095bc3 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -187,7 +187,8 @@ final class ProfileController extends Controller /** @var User $user */ $user = auth()->user(); $isInternalAuth = $this->internalAuth; - $count = DB::table('oauth_clients')->where('personal_access_client', true)->whereNull('user_id')->count(); + // $count = DB::table('oauth_clients')->where('personal_access_client', true)->whereNull('user_id')->count(); + $count = 0; $subTitle = $user->email; $userId = $user->id; $enabled2FA = null !== $user->mfa_secret; @@ -198,12 +199,12 @@ final class ProfileController extends Controller $mfaBackupCount = count($recoveryData); $this->createOAuthKeys(); - if (0 === $count) { - /** @var ClientRepository $repository */ - $repository = app(ClientRepository::class); - $name = sprintf('%s Personal Access Grant Client', config('app.name')); - $repository->createPersonalAccessClient(null, $name, 'http://localhost'); - } +// if (0 === $count) { +// /** @var ClientRepository $repository */ +// $repository = app(ClientRepository::class); +// $name = sprintf('%s Personal Access Grant Client', config('app.name')); +// $repository->createPersonalAccessClient(null, $name, 'http://localhost'); +// } $accessToken = Preferences::get('access_token'); if (null === $accessToken) { diff --git a/app/Http/Middleware/SecureHeaders.php b/app/Http/Middleware/SecureHeaders.php index 1d84d21871..cf14cf5e20 100644 --- a/app/Http/Middleware/SecureHeaders.php +++ b/app/Http/Middleware/SecureHeaders.php @@ -117,17 +117,39 @@ class SecureHeaders $disableFrameHeader = config('firefly.disable_frame_header'); $disableCSP = config('firefly.disable_csp_header'); if (false === $disableFrameHeader) { - $response->header('X-Frame-Options', 'deny'); + if(method_exists($response, 'header')) { + $response->header('X-Frame-Options', 'deny'); + } + if(!method_exists($response, 'header')) { + $response->headers->set('X-Frame-Options', 'deny'); + } + } if (false === $disableCSP && !$response->headers->has('Content-Security-Policy')) { - $response->header('Content-Security-Policy', implode('; ', $csp)); + if(method_exists($response, 'header')) { + $response->header('Content-Security-Policy', implode('; ', $csp)); + } + if(!method_exists($response, 'header')) { + $response->header('Content-Security-Policy', implode('; ', $csp)); + } } - $response->header('X-XSS-Protection', '1; mode=block'); - $response->header('X-Content-Type-Options', 'nosniff'); - $response->header('Referrer-Policy', 'no-referrer'); - $response->header('X-Permitted-Cross-Domain-Policies', 'none'); - $response->header('X-Robots-Tag', 'none'); - $response->header('Feature-Policy', implode('; ', $featurePolicies)); + if(method_exists($response, 'header')) { + $response->header('X-XSS-Protection', '1; mode=block'); + $response->header('X-Content-Type-Options', 'nosniff'); + $response->header('Referrer-Policy', 'no-referrer'); + $response->header('X-Permitted-Cross-Domain-Policies', 'none'); + $response->header('X-Robots-Tag', 'none'); + $response->header('Feature-Policy', implode('; ', $featurePolicies)); + } + if(!method_exists($response, 'header')) { + $response->headers->set('X-XSS-Protection', '1; mode=block'); + $response->headers->set('X-Content-Type-Options', 'nosniff'); + $response->headers->set('Referrer-Policy', 'no-referrer'); + $response->headers->set('X-Permitted-Cross-Domain-Policies', 'none'); + $response->headers->set('X-Robots-Tag', 'none'); + $response->headers->set('Feature-Policy', implode('; ', $featurePolicies)); + } + return $response; } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index c94ae94283..9d9773e416 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -43,8 +43,11 @@ class AppServiceProvider extends ServiceProvider */ public function boot(): void { + // do not check permissions for key files. + Passport::$validateKeyPermissions = false; + + Schema::defaultStringLength(191); - // Passport::$clientUuids = false; Response::macro('api', function (array $value) { $headers = ['Cache-Control' => 'no-store']; $uuid = (string) request()->header('X-Trace-Id'); @@ -85,8 +88,7 @@ class AppServiceProvider extends ServiceProvider #[Override] public function register(): void { - Passport::ignoreRoutes(); - + // Passport::ignoreRoutes(); // Passport::ignoreMigrations(); // Sanctum::ignoreMigrations(); } diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index dab98b10e5..5d543f54ee 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -41,6 +41,9 @@ class AuthServiceProvider extends ServiceProvider */ public function boot(): void { + // new code for authorization. + Passport::authorizationView('auth.oauth.authorize'); + Auth::provider('remote_user_provider', static fn ($app, array $config): RemoteUserProvider => new RemoteUserProvider()); Auth::extend( @@ -48,6 +51,6 @@ class AuthServiceProvider extends ServiceProvider static fn ($app, string $name, array $config): RemoteUserGuard => new RemoteUserGuard(Auth::createUserProvider($config['provider']), $app) ); - Passport::tokensExpireIn(now()->addDays(14)); + // Passport::tokensExpireIn(now()->addDays(14)); } } diff --git a/app/Support/System/OAuthKeys.php b/app/Support/System/OAuthKeys.php index c45ed177e1..a6b525fa2a 100644 --- a/app/Support/System/OAuthKeys.php +++ b/app/Support/System/OAuthKeys.php @@ -31,6 +31,7 @@ use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Facades\Log; use Laravel\Passport\Console\KeysCommand; +use Laravel\Passport\Passport; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Safe\Exceptions\FilesystemException; @@ -57,8 +58,8 @@ class OAuthKeys public static function hasKeyFiles(): bool { Log::debug('hasKeyFiles()'); - $private = storage_path('oauth-private.key'); - $public = storage_path('oauth-public.key'); + $private = Passport::keyPath('oauth-private.key'); + $public = Passport::keyPath('oauth-public.key'); $privateExists = file_exists($private); $publicExists = file_exists($public); @@ -141,11 +142,17 @@ class OAuthKeys return false; } - $private = storage_path('oauth-private.key'); - $public = storage_path('oauth-public.key'); + $private = Passport::keyPath('oauth-private.key'); + $public = Passport::keyPath('oauth-public.key'); file_put_contents($private, $privateContent); file_put_contents($public, $publicContent); + if (! windows_os()) { + Log::debug('Set the correct permissions.'); + chmod(Passport::keyPath('oauth-public.key'), 0660); + chmod(Passport::keyPath('oauth-private.key'), 0600); + } + Log::debug(sprintf('Will store private key with hash "%s" in file "%s"', hash('sha256', $privateContent), $private)); Log::debug(sprintf('Will store public key with hash "%s" in file "%s"', hash('sha256', $publicContent), $public)); Log::debug('Done with generateKeysFromDB()'); @@ -155,8 +162,8 @@ class OAuthKeys public static function storeKeysInDB(): void { - $private = storage_path('oauth-private.key'); - $public = storage_path('oauth-public.key'); + $private = Passport::keyPath('oauth-private.key'); + $public = Passport::keyPath('oauth-public.key'); $privateContent = file_get_contents($private); $publicContent = file_get_contents($public); FireflyConfig::set(self::PRIVATE_KEY, Crypt::encrypt($privateContent)); diff --git a/app/User.php b/app/User.php index 119ae3bdf2..496c910485 100644 --- a/app/User.php +++ b/app/User.php @@ -72,12 +72,13 @@ use Laravel\Passport\HasApiTokens; use NotificationChannels\Pushover\PushoverReceiver; use SensitiveParameter; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Laravel\Passport\Contracts\OAuthenticatable; /** * @property null|UserGroup $userGroup * @property bool $blocked */ -class User extends Authenticatable +class User extends Authenticatable implements OAuthenticatable { use HasApiTokens; use Notifiable; diff --git a/composer.json b/composer.json index 59e677ca1a..33e48e62ce 100644 --- a/composer.json +++ b/composer.json @@ -77,7 +77,6 @@ "ext-pdo": "*", "ext-session": "*", "ext-simplexml": "*", - "ext-sodium": "*", "ext-tokenizer": "*", "ext-xml": "*", "ext-xmlwriter": "*", @@ -88,8 +87,8 @@ "jc5/google2fa-laravel": "^2.0", "jc5/recovery": "^2", "laravel-notification-channels/pushover": "^5.0", - "laravel/framework": "^12", - "laravel/passport": "^13.7", + "laravel/framework": "^13", + "laravel/passport": "^13.0", "laravel/slack-notification-channel": "^3.3", "laravel/ui": "^4.2", "league/commonmark": "^2", @@ -97,7 +96,7 @@ "league/fractal": "0.*", "mailersend/laravel-driver": "^3.0", "nunomaduro/collision": "^8", - "pragmarx/google2fa": "^8.0", + "pragmarx/google2fa": "^9", "predis/predis": "^3", "psr/log": "<4", "ramsey/uuid": "^4.7", diff --git a/composer.lock b/composer.lock index d0c3f764fb..22777f6539 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5305416d36c7d3ed283075f245312bb3", + "content-hash": "4f1f0dcde32d2f02333bd7af2cbc90eb", "packages": [ { "name": "bacon/bacon-qr-code", @@ -1657,27 +1657,26 @@ }, { "name": "jc5/google2fa-laravel", - "version": "2.0.10", + "version": "2.0.9", "source": { "type": "git", "url": "https://github.com/JC5/google2fa-laravel.git", - "reference": "7b521b07c60786719d74c12c1d6306f5add0d998" + "reference": "af390d117e92963c75fa36868b754e6321b3710f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/JC5/google2fa-laravel/zipball/7b521b07c60786719d74c12c1d6306f5add0d998", - "reference": "7b521b07c60786719d74c12c1d6306f5add0d998", + "url": "https://api.github.com/repos/JC5/google2fa-laravel/zipball/af390d117e92963c75fa36868b754e6321b3710f", + "reference": "af390d117e92963c75fa36868b754e6321b3710f", "shasum": "" }, "require": { - "laravel/framework": "^11.0|^12.0", + "laravel/framework": ">=5.4.36", "php": ">=8", - "pragmarx/google2fa-qrcode": "^3" + "pragmarx/google2fa-qrcode": ">1.0" }, "require-dev": { - "bacon/bacon-qr-code": "^3.0", - "orchestra/testbench": "3.4.*|3.5.*|3.6.*|3.7.*|4.*|5.*|6.*|7.*|8.*|9.*|10.*", - "phpunit/phpunit": "~5|~6|~7|~8|~9|~10|~11", + "orchestra/testbench": "3.4.*|3.5.*|3.6.*|3.7.*|4.*|5.*|6.*", + "phpunit/phpunit": "~9", "roave/security-advisories": "dev-master" }, "suggest": { @@ -1733,9 +1732,9 @@ ], "support": { "issues": "https://github.com/JC5/google2fa-laravel/issues", - "source": "https://github.com/JC5/google2fa-laravel/tree/2.0.10" + "source": "https://github.com/JC5/google2fa-laravel/tree/2.0.9" }, - "time": "2025-05-05T04:27:12+00:00" + "time": "2023-09-16T02:59:44+00:00" }, { "name": "jc5/recovery", @@ -1880,24 +1879,24 @@ }, { "name": "laravel/framework", - "version": "v12.56.0", + "version": "v13.5.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "dac16d424b59debb2273910dde88eb7050a2a709" + "reference": "ffa1850049a691b93129808f27ecd10e65c9d1a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/dac16d424b59debb2273910dde88eb7050a2a709", - "reference": "dac16d424b59debb2273910dde88eb7050a2a709", + "url": "https://api.github.com/repos/laravel/framework/zipball/ffa1850049a691b93129808f27ecd10e65c9d1a5", + "reference": "ffa1850049a691b93129808f27ecd10e65c9d1a5", "shasum": "" }, "require": { - "brick/math": "^0.11|^0.12|^0.13|^0.14", + "brick/math": "^0.14.2 || ^0.15 || ^0.16 || ^0.17", "composer-runtime-api": "^2.2", "doctrine/inflector": "^2.0.5", "dragonmantank/cron-expression": "^3.4", - "egulias/email-validator": "^3.2.1|^4.0", + "egulias/email-validator": "^4.0", "ext-ctype": "*", "ext-filter": "*", "ext-hash": "*", @@ -1907,9 +1906,10 @@ "ext-tokenizer": "*", "fruitcake/php-cors": "^1.3", "guzzlehttp/guzzle": "^7.8.2", + "guzzlehttp/promises": "^2.0.3", "guzzlehttp/uri-template": "^1.0", "laravel/prompts": "^0.3.0", - "laravel/serializable-closure": "^1.3|^2.0", + "laravel/serializable-closure": "^2.0.10", "league/commonmark": "^2.8.1", "league/flysystem": "^3.25.1", "league/flysystem-local": "^3.25.1", @@ -1917,25 +1917,24 @@ "monolog/monolog": "^3.0", "nesbot/carbon": "^3.8.4", "nunomaduro/termwind": "^2.0", - "php": "^8.2", - "psr/container": "^1.1.1|^2.0.1", - "psr/log": "^1.0|^2.0|^3.0", - "psr/simple-cache": "^1.0|^2.0|^3.0", + "php": "^8.3", + "psr/container": "^1.1.1 || ^2.0.1", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0", "ramsey/uuid": "^4.7", - "symfony/console": "^7.2.0", - "symfony/error-handler": "^7.2.0", - "symfony/finder": "^7.2.0", - "symfony/http-foundation": "^7.2.0", - "symfony/http-kernel": "^7.2.0", - "symfony/mailer": "^7.2.0", - "symfony/mime": "^7.2.0", - "symfony/polyfill-php83": "^1.33", + "symfony/console": "^7.4.0 || ^8.0.0", + "symfony/error-handler": "^7.4.0 || ^8.0.0", + "symfony/finder": "^7.4.0 || ^8.0.0", + "symfony/http-foundation": "^7.4.0 || ^8.0.0", + "symfony/http-kernel": "^7.4.0 || ^8.0.0", + "symfony/mailer": "^7.4.0 || ^8.0.0", + "symfony/mime": "^7.4.0 || ^8.0.0", "symfony/polyfill-php84": "^1.33", "symfony/polyfill-php85": "^1.33", - "symfony/process": "^7.2.0", - "symfony/routing": "^7.2.0", - "symfony/uid": "^7.2.0", - "symfony/var-dumper": "^7.2.0", + "symfony/process": "^7.4.5 || ^8.0.5", + "symfony/routing": "^7.4.0 || ^8.0.0", + "symfony/uid": "^7.4.0 || ^8.0.0", + "symfony/var-dumper": "^7.4.0 || ^8.0.0", "tijsverkoyen/css-to-inline-styles": "^2.2.5", "vlucas/phpdotenv": "^5.6.1", "voku/portable-ascii": "^2.0.2" @@ -1944,9 +1943,9 @@ "tightenco/collect": "<5.5.33" }, "provide": { - "psr/container-implementation": "1.1|2.0", - "psr/log-implementation": "1.0|2.0|3.0", - "psr/simple-cache-implementation": "1.0|2.0|3.0" + "psr/container-implementation": "1.1 || 2.0", + "psr/log-implementation": "1.0 || 2.0 || 3.0", + "psr/simple-cache-implementation": "1.0 || 2.0 || 3.0" }, "replace": { "illuminate/auth": "self.version", @@ -1992,7 +1991,6 @@ "aws/aws-sdk-php": "^3.322.9", "ext-gmp": "*", "fakerphp/faker": "^1.24", - "guzzlehttp/promises": "^2.0.3", "guzzlehttp/psr7": "^2.4", "laravel/pint": "^1.18", "league/flysystem-aws-s3-v3": "^3.25.1", @@ -2002,22 +2000,23 @@ "league/flysystem-sftp-v3": "^3.25.1", "mockery/mockery": "^1.6.10", "opis/json-schema": "^2.4.1", - "orchestra/testbench-core": "^10.9.0", - "pda/pheanstalk": "^5.0.6|^7.0.0", + "orchestra/testbench-core": "^11.0.0", + "pda/pheanstalk": "^7.0.0 || ^8.0.0", "php-http/discovery": "^1.15", - "phpstan/phpstan": "^2.1.41", - "phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1", - "predis/predis": "^2.3|^3.0", - "resend/resend-php": "^0.10.0|^1.0", - "symfony/cache": "^7.2.0", - "symfony/http-client": "^7.2.0", - "symfony/psr-http-message-bridge": "^7.2.0", - "symfony/translation": "^7.2.0" + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^11.5.50 || ^12.5.8 || ^13.0.3", + "predis/predis": "^2.3 || ^3.0", + "rector/rector": "^2.3", + "resend/resend-php": "^1.0", + "symfony/cache": "^7.4.0 || ^8.0.0", + "symfony/http-client": "^7.4.0 || ^8.0.0", + "symfony/psr-http-message-bridge": "^7.4.0 || ^8.0.0", + "symfony/translation": "^7.4.0 || ^8.0.0" }, "suggest": { "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.322.9).", - "brianium/paratest": "Required to run tests in parallel (^7.0|^8.0).", + "brianium/paratest": "Required to run tests in parallel (^7.0 || ^8.0).", "ext-apcu": "Required to use the APC cache driver.", "ext-fileinfo": "Required to use the Filesystem class.", "ext-ftp": "Required to use the Flysystem FTP driver.", @@ -2026,7 +2025,7 @@ "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.", "ext-pdo": "Required to use all database features.", "ext-posix": "Required to use all features of the queue worker.", - "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0|^6.0).", + "ext-redis": "Required to use the Redis cache and queue drivers (^4.0 || ^5.0 || ^6.0).", "fakerphp/faker": "Required to generate fake data using the fake() helper (^1.23).", "filp/whoops": "Required for friendly error pages in development (^2.14.3).", "laravel/tinker": "Required to use the tinker console command (^2.0).", @@ -2036,24 +2035,25 @@ "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)", "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).", "mockery/mockery": "Required to use mocking (^1.6).", - "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).", + "pda/pheanstalk": "Required to use the beanstalk queue driver (^7.0 || ^8.0).", "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).", - "phpunit/phpunit": "Required to use assertions and run tests (^10.5.35|^11.5.3|^12.0.1).", - "predis/predis": "Required to use the predis connector (^2.3|^3.0).", + "phpunit/phpunit": "Required to use assertions and run tests (^11.5.50 || ^12.5.8 || ^13.0.3).", + "predis/predis": "Required to use the predis connector (^2.3 || ^3.0).", "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", - "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", - "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0|^1.0).", - "symfony/cache": "Required to PSR-6 cache bridge (^7.2).", - "symfony/filesystem": "Required to enable support for relative symbolic links (^7.2).", - "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.2).", - "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.2).", - "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.2).", - "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.2)." + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0 || ^7.0).", + "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0 || ^1.0).", + "spatie/fork": "Required to use the 'fork' concurrency driver (^1.2).", + "symfony/cache": "Required to PSR-6 cache bridge (^7.4 || ^8.0).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^7.4 || ^8.0).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.4 || ^8.0).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.4 || ^8.0).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.4 || ^8.0).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.4 || ^8.0)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "12.x-dev" + "dev-master": "13.0.x-dev" } }, "autoload": { @@ -2098,20 +2098,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2026-03-26T14:51:54+00:00" + "time": "2026-04-14T13:55:03+00:00" }, { "name": "laravel/passport", - "version": "v13.7.3", + "version": "v13.7.4", "source": { "type": "git", "url": "https://github.com/laravel/passport.git", - "reference": "f4f85e3122c7c6ef375f8bd5395bc7c88801fcc5" + "reference": "16c45794c6a6176792fdf555f986aa1b944d9081" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/passport/zipball/f4f85e3122c7c6ef375f8bd5395bc7c88801fcc5", - "reference": "f4f85e3122c7c6ef375f8bd5395bc7c88801fcc5", + "url": "https://api.github.com/repos/laravel/passport/zipball/16c45794c6a6176792fdf555f986aa1b944d9081", + "reference": "16c45794c6a6176792fdf555f986aa1b944d9081", "shasum": "" }, "require": { @@ -2173,7 +2173,7 @@ "issues": "https://github.com/laravel/passport/issues", "source": "https://github.com/laravel/passport" }, - "time": "2026-04-07T13:08:13+00:00" + "time": "2026-04-09T13:39:45+00:00" }, { "name": "laravel/prompts", @@ -2236,16 +2236,16 @@ }, { "name": "laravel/serializable-closure", - "version": "v2.0.11", + "version": "v2.0.12", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "d1af40ac4a6ccc12bd062a7184f63c9995a63bdd" + "reference": "a6abb4e54f6fcd3138120b9ad497f0bd146f9919" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/d1af40ac4a6ccc12bd062a7184f63c9995a63bdd", - "reference": "d1af40ac4a6ccc12bd062a7184f63c9995a63bdd", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/a6abb4e54f6fcd3138120b9ad497f0bd146f9919", + "reference": "a6abb4e54f6fcd3138120b9ad497f0bd146f9919", "shasum": "" }, "require": { @@ -2293,7 +2293,7 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2026-04-07T13:32:18+00:00" + "time": "2026-04-14T13:33:34+00:00" }, { "name": "laravel/slack-notification-channel", @@ -2425,34 +2425,34 @@ }, { "name": "lcobucci/clock", - "version": "3.5.0", + "version": "3.6.0", "source": { "type": "git", "url": "https://github.com/lcobucci/clock.git", - "reference": "a3139d9e97d47826f27e6a17bb63f13621f86058" + "reference": "4cdd88f761e9be9095ccbedf3e08d61ae216c643" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/clock/zipball/a3139d9e97d47826f27e6a17bb63f13621f86058", - "reference": "a3139d9e97d47826f27e6a17bb63f13621f86058", + "url": "https://api.github.com/repos/lcobucci/clock/zipball/4cdd88f761e9be9095ccbedf3e08d61ae216c643", + "reference": "4cdd88f761e9be9095ccbedf3e08d61ae216c643", "shasum": "" }, "require": { - "php": "~8.3.0 || ~8.4.0 || ~8.5.0", + "php": "~8.4.0 || ~8.5.0", "psr/clock": "^1.0" }, "provide": { "psr/clock-implementation": "1.0" }, "require-dev": { - "infection/infection": "^0.31", - "lcobucci/coding-standard": "^11.2.0", + "infection/infection": "^0.32", + "lcobucci/coding-standard": "^12.0", "phpstan/extension-installer": "^1.3.1", - "phpstan/phpstan": "^2.0.0", - "phpstan/phpstan-deprecation-rules": "^2.0.0", - "phpstan/phpstan-phpunit": "^2.0.0", - "phpstan/phpstan-strict-rules": "^2.0.0", - "phpunit/phpunit": "^12.0.0" + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^13.0" }, "type": "library", "autoload": { @@ -2473,7 +2473,7 @@ "description": "Yet another clock abstraction", "support": { "issues": "https://github.com/lcobucci/clock/issues", - "source": "https://github.com/lcobucci/clock/tree/3.5.0" + "source": "https://github.com/lcobucci/clock/tree/3.6.0" }, "funding": [ { @@ -2485,7 +2485,7 @@ "type": "patreon" } ], - "time": "2025-10-27T09:03:17+00:00" + "time": "2026-04-13T21:30:16+00:00" }, { "name": "lcobucci/jwt", @@ -4886,16 +4886,16 @@ }, { "name": "pragmarx/google2fa", - "version": "v8.0.3", + "version": "v9.0.0", "source": { "type": "git", "url": "https://github.com/antonioribeiro/google2fa.git", - "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad" + "reference": "e6bc62dd6ae83acc475f57912e27466019a1f2cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", - "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", + "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/e6bc62dd6ae83acc475f57912e27466019a1f2cf", + "reference": "e6bc62dd6ae83acc475f57912e27466019a1f2cf", "shasum": "" }, "require": { @@ -4932,27 +4932,27 @@ ], "support": { "issues": "https://github.com/antonioribeiro/google2fa/issues", - "source": "https://github.com/antonioribeiro/google2fa/tree/v8.0.3" + "source": "https://github.com/antonioribeiro/google2fa/tree/v9.0.0" }, - "time": "2024-09-05T11:56:40+00:00" + "time": "2025-09-19T22:51:08+00:00" }, { "name": "pragmarx/google2fa-qrcode", - "version": "v3.0.1", + "version": "v3.0.0", "source": { "type": "git", "url": "https://github.com/antonioribeiro/google2fa-qrcode.git", - "reference": "c23ebcc3a50de0d1566016a6dd1486e183bb78e1" + "reference": "ce4d8a729b6c93741c607cfb2217acfffb5bf76b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/google2fa-qrcode/zipball/c23ebcc3a50de0d1566016a6dd1486e183bb78e1", - "reference": "c23ebcc3a50de0d1566016a6dd1486e183bb78e1", + "url": "https://api.github.com/repos/antonioribeiro/google2fa-qrcode/zipball/ce4d8a729b6c93741c607cfb2217acfffb5bf76b", + "reference": "ce4d8a729b6c93741c607cfb2217acfffb5bf76b", "shasum": "" }, "require": { "php": ">=7.1", - "pragmarx/google2fa": "^4.0|^5.0|^6.0|^7.0|^8.0" + "pragmarx/google2fa": ">=4.0" }, "require-dev": { "bacon/bacon-qr-code": "^2.0", @@ -4999,9 +4999,9 @@ ], "support": { "issues": "https://github.com/antonioribeiro/google2fa-qrcode/issues", - "source": "https://github.com/antonioribeiro/google2fa-qrcode/tree/v3.0.1" + "source": "https://github.com/antonioribeiro/google2fa-qrcode/tree/v3.0.0" }, - "time": "2025-09-19T23:02:26+00:00" + "time": "2021-08-15T12:53:48+00:00" }, { "name": "pragmarx/random", @@ -6749,47 +6749,39 @@ }, { "name": "symfony/console", - "version": "v7.4.8", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707" + "reference": "5b66d385dc58f69652e56f78a4184615e3f2b7f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707", - "reference": "1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707", + "url": "https://api.github.com/repos/symfony/console/zipball/5b66d385dc58f69652e56f78a4184615e3f2b7f7", + "reference": "5b66d385dc58f69652e56f78a4184615e3f2b7f7", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.2|^8.0" - }, - "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/dotenv": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/lock": "<6.4", - "symfony/process": "<6.4" + "symfony/string": "^7.4|^8.0" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/event-dispatcher": "^6.4|^7.0|^8.0", - "symfony/http-foundation": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/lock": "^6.4|^7.0|^8.0", - "symfony/messenger": "^6.4|^7.0|^8.0", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/stopwatch": "^6.4|^7.0|^8.0", - "symfony/var-dumper": "^6.4|^7.0|^8.0" + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -6823,7 +6815,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.8" + "source": "https://github.com/symfony/console/tree/v8.0.8" }, "funding": [ { @@ -6843,7 +6835,7 @@ "type": "tidelift" } ], - "time": "2026-03-30T13:54:39+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/css-selector", @@ -6983,33 +6975,32 @@ }, { "name": "symfony/error-handler", - "version": "v7.4.8", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "8dd79d8af777ee6cba2fd4d98da6ffb839f3c0fa" + "reference": "c1119fe8dcfc3825ec74ec061b96ef0c8f281517" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/8dd79d8af777ee6cba2fd4d98da6ffb839f3c0fa", - "reference": "8dd79d8af777ee6cba2fd4d98da6ffb839f3c0fa", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/c1119fe8dcfc3825ec74ec061b96ef0c8f281517", + "reference": "c1119fe8dcfc3825ec74ec061b96ef0c8f281517", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/log": "^1|^2|^3", "symfony/polyfill-php85": "^1.32", - "symfony/var-dumper": "^6.4|^7.0|^8.0" + "symfony/var-dumper": "^7.4|^8.0" }, "conflict": { - "symfony/deprecation-contracts": "<2.5", - "symfony/http-kernel": "<6.4" + "symfony/deprecation-contracts": "<2.5" }, "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0", + "symfony/console": "^7.4|^8.0", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/serializer": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", "symfony/webpack-encore-bundle": "^1.0|^2.0" }, "bin": [ @@ -7041,7 +7032,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.4.8" + "source": "https://github.com/symfony/error-handler/tree/v8.0.8" }, "funding": [ { @@ -7061,7 +7052,7 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/event-dispatcher", @@ -7293,23 +7284,23 @@ }, { "name": "symfony/finder", - "version": "v7.4.8", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "e0be088d22278583a82da281886e8c3592fbf149" + "reference": "8da41214757b87d97f181e3d14a4179286151007" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/e0be088d22278583a82da281886e8c3592fbf149", - "reference": "e0be088d22278583a82da281886e8c3592fbf149", + "url": "https://api.github.com/repos/symfony/finder/zipball/8da41214757b87d97f181e3d14a4179286151007", + "reference": "8da41214757b87d97f181e3d14a4179286151007", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4" }, "require-dev": { - "symfony/filesystem": "^6.4|^7.0|^8.0" + "symfony/filesystem": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -7337,7 +7328,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.4.8" + "source": "https://github.com/symfony/finder/tree/v8.0.8" }, "funding": [ { @@ -7357,7 +7348,7 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/http-client", @@ -7535,37 +7526,35 @@ }, { "name": "symfony/http-foundation", - "version": "v7.4.8", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "9381209597ec66c25be154cbf2289076e64d1eab" + "reference": "02656f7ebeae5c155d659e946f6b3a33df24051b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9381209597ec66c25be154cbf2289076e64d1eab", - "reference": "9381209597ec66c25be154cbf2289076e64d1eab", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/02656f7ebeae5c155d659e946f6b3a33df24051b", + "reference": "02656f7ebeae5c155d659e946f6b3a33df24051b", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=8.4", "symfony/polyfill-mbstring": "^1.1" }, "conflict": { - "doctrine/dbal": "<3.6", - "symfony/cache": "<6.4.12|>=7.0,<7.1.5" + "doctrine/dbal": "<4.3" }, "require-dev": { - "doctrine/dbal": "^3.6|^4", + "doctrine/dbal": "^4.3", "predis/predis": "^1.1|^2.0", - "symfony/cache": "^6.4.12|^7.1.5|^8.0", - "symfony/clock": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/mime": "^6.4|^7.0|^8.0", - "symfony/rate-limiter": "^6.4|^7.0|^8.0" + "symfony/cache": "^7.4|^8.0", + "symfony/clock": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/rate-limiter": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -7593,7 +7582,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.4.8" + "source": "https://github.com/symfony/http-foundation/tree/v8.0.8" }, "funding": [ { @@ -7613,78 +7602,63 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.4.8", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "017e76ad089bac281553389269e259e155935e1a" + "reference": "1770f6818d83b2fddc12185025b93f39a90cb628" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/017e76ad089bac281553389269e259e155935e1a", - "reference": "017e76ad089bac281553389269e259e155935e1a", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/1770f6818d83b2fddc12185025b93f39a90cb628", + "reference": "1770f6818d83b2fddc12185025b93f39a90cb628", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/error-handler": "^6.4|^7.0|^8.0", - "symfony/event-dispatcher": "^7.3|^8.0", + "symfony/error-handler": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", "symfony/http-foundation": "^7.4|^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/browser-kit": "<6.4", - "symfony/cache": "<6.4", - "symfony/config": "<6.4", - "symfony/console": "<6.4", - "symfony/dependency-injection": "<6.4", - "symfony/doctrine-bridge": "<6.4", "symfony/flex": "<2.10", - "symfony/form": "<6.4", - "symfony/http-client": "<6.4", "symfony/http-client-contracts": "<2.5", - "symfony/mailer": "<6.4", - "symfony/messenger": "<6.4", - "symfony/translation": "<6.4", "symfony/translation-contracts": "<2.5", - "symfony/twig-bridge": "<6.4", - "symfony/validator": "<6.4", - "symfony/var-dumper": "<6.4", - "twig/twig": "<3.12" + "twig/twig": "<3.21" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^6.4|^7.0|^8.0", - "symfony/clock": "^6.4|^7.0|^8.0", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/css-selector": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4.1|^7.0.1|^8.0", - "symfony/dom-crawler": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/browser-kit": "^7.4|^8.0", + "symfony/clock": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/css-selector": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/dom-crawler": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", "symfony/http-client-contracts": "^2.5|^3", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/property-access": "^7.1|^8.0", - "symfony/routing": "^6.4|^7.0|^8.0", - "symfony/serializer": "^7.1|^8.0", - "symfony/stopwatch": "^6.4|^7.0|^8.0", - "symfony/translation": "^6.4|^7.0|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/property-access": "^7.4|^8.0", + "symfony/routing": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", "symfony/translation-contracts": "^2.5|^3", - "symfony/uid": "^6.4|^7.0|^8.0", - "symfony/validator": "^6.4|^7.0|^8.0", - "symfony/var-dumper": "^6.4|^7.0|^8.0", - "symfony/var-exporter": "^6.4|^7.0|^8.0", - "twig/twig": "^3.12" + "symfony/uid": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0", + "symfony/var-exporter": "^7.4|^8.0", + "twig/twig": "^3.21" }, "type": "library", "autoload": { @@ -7712,7 +7686,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.4.8" + "source": "https://github.com/symfony/http-kernel/tree/v8.0.8" }, "funding": [ { @@ -7732,43 +7706,39 @@ "type": "tidelift" } ], - "time": "2026-03-31T20:57:01+00:00" + "time": "2026-03-31T21:14:05+00:00" }, { "name": "symfony/mailer", - "version": "v7.4.8", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "f6ea532250b476bfc1b56699b388a1bdbf168f62" + "reference": "ca5f6edaf8780ece814404b58a4482b22b509c56" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/f6ea532250b476bfc1b56699b388a1bdbf168f62", - "reference": "f6ea532250b476bfc1b56699b388a1bdbf168f62", + "url": "https://api.github.com/repos/symfony/mailer/zipball/ca5f6edaf8780ece814404b58a4482b22b509c56", + "reference": "ca5f6edaf8780ece814404b58a4482b22b509c56", "shasum": "" }, "require": { "egulias/email-validator": "^2.1.10|^3|^4", - "php": ">=8.2", + "php": ">=8.4", "psr/event-dispatcher": "^1", "psr/log": "^1|^2|^3", - "symfony/event-dispatcher": "^6.4|^7.0|^8.0", - "symfony/mime": "^7.2|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3" }, "conflict": { - "symfony/http-client-contracts": "<2.5", - "symfony/http-kernel": "<6.4", - "symfony/messenger": "<6.4", - "symfony/mime": "<6.4", - "symfony/twig-bridge": "<6.4" + "symfony/http-client-contracts": "<2.5" }, "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/http-client": "^6.4|^7.0|^8.0", - "symfony/messenger": "^6.4|^7.0|^8.0", - "symfony/twig-bridge": "^6.4|^7.0|^8.0" + "symfony/console": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/twig-bridge": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -7796,7 +7766,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.4.8" + "source": "https://github.com/symfony/mailer/tree/v8.0.8" }, "funding": [ { @@ -7816,7 +7786,7 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/mailgun-mailer", @@ -7890,40 +7860,37 @@ }, { "name": "symfony/mime", - "version": "v7.4.8", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "6df02f99998081032da3407a8d6c4e1dcb5d4379" + "reference": "ddff21f14c7ce04b98101b399a9463dce8b0ce66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/6df02f99998081032da3407a8d6c4e1dcb5d4379", - "reference": "6df02f99998081032da3407a8d6c4e1dcb5d4379", + "url": "https://api.github.com/repos/symfony/mime/zipball/ddff21f14c7ce04b98101b399a9463dce8b0ce66", + "reference": "ddff21f14c7ce04b98101b399a9463dce8b0ce66", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=8.4", "symfony/polyfill-intl-idn": "^1.10", "symfony/polyfill-mbstring": "^1.0" }, "conflict": { "egulias/email-validator": "~3.0.0", "phpdocumentor/reflection-docblock": "<5.2|>=7", - "phpdocumentor/type-resolver": "<1.5.1", - "symfony/mailer": "<6.4", - "symfony/serializer": "<6.4.3|>7.0,<7.0.3" + "phpdocumentor/type-resolver": "<1.5.1" }, "require-dev": { "egulias/email-validator": "^2.1.10|^3.1|^4", "league/html-to-markdown": "^5.0", "phpdocumentor/reflection-docblock": "^5.2|^6.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/property-access": "^6.4|^7.0|^8.0", - "symfony/property-info": "^6.4|^7.0|^8.0", - "symfony/serializer": "^6.4.3|^7.0.3|^8.0" + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/property-access": "^7.4|^8.0", + "symfony/property-info": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -7955,7 +7922,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.4.8" + "source": "https://github.com/symfony/mime/tree/v8.0.8" }, "funding": [ { @@ -7975,7 +7942,7 @@ "type": "tidelift" } ], - "time": "2026-03-30T14:11:46+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/options-resolver", @@ -8050,7 +8017,7 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.34.0", + "version": "v1.35.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -8109,7 +8076,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.34.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.35.0" }, "funding": [ { @@ -8133,7 +8100,7 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.34.0", + "version": "v1.35.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", @@ -8191,7 +8158,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.34.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.35.0" }, "funding": [ { @@ -8215,7 +8182,7 @@ }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.34.0", + "version": "v1.35.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", @@ -8278,7 +8245,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.34.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.35.0" }, "funding": [ { @@ -8302,7 +8269,7 @@ }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.34.0", + "version": "v1.35.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -8363,7 +8330,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.34.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.35.0" }, "funding": [ { @@ -8387,7 +8354,7 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.34.0", + "version": "v1.35.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -8447,7 +8414,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.34.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.35.0" }, "funding": [ { @@ -8469,89 +8436,9 @@ ], "time": "2026-04-10T16:19:22+00:00" }, - { - "name": "symfony/polyfill-php83", - "version": "v1.34.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "3600c2cb22399e25bb226e4a135ce91eeb2a6149" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/3600c2cb22399e25bb226e4a135ce91eeb2a6149", - "reference": "3600c2cb22399e25bb226e4a135ce91eeb2a6149", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php83\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.34.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-04-10T17:25:58+00:00" - }, { "name": "symfony/polyfill-php84", - "version": "v1.34.0", + "version": "v1.35.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php84.git", @@ -8607,7 +8494,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php84/tree/v1.34.0" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.35.0" }, "funding": [ { @@ -8631,7 +8518,7 @@ }, { "name": "symfony/polyfill-php85", - "version": "v1.34.0", + "version": "v1.35.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php85.git", @@ -8687,7 +8574,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php85/tree/v1.34.0" + "source": "https://github.com/symfony/polyfill-php85/tree/v1.35.0" }, "funding": [ { @@ -8711,7 +8598,7 @@ }, { "name": "symfony/polyfill-uuid", - "version": "v1.34.0", + "version": "v1.35.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", @@ -8770,7 +8657,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.34.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.35.0" }, "funding": [ { @@ -8794,20 +8681,20 @@ }, { "name": "symfony/process", - "version": "v7.4.8", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "60f19cd3badc8de688421e21e4305eba50f8089a" + "reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/60f19cd3badc8de688421e21e4305eba50f8089a", - "reference": "60f19cd3badc8de688421e21e4305eba50f8089a", + "url": "https://api.github.com/repos/symfony/process/zipball/cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc", + "reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4" }, "type": "library", "autoload": { @@ -8835,7 +8722,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.4.8" + "source": "https://github.com/symfony/process/tree/v8.0.8" }, "funding": [ { @@ -8855,7 +8742,7 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/psr-http-message-bridge", @@ -8946,34 +8833,29 @@ }, { "name": "symfony/routing", - "version": "v7.4.8", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "9608de9873ec86e754fb6c0a0fa7e5f1a960eb6b" + "reference": "0de330ec2ea922a7b08ec45615bd51179de7fda4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/9608de9873ec86e754fb6c0a0fa7e5f1a960eb6b", - "reference": "9608de9873ec86e754fb6c0a0fa7e5f1a960eb6b", + "url": "https://api.github.com/repos/symfony/routing/zipball/0de330ec2ea922a7b08ec45615bd51179de7fda4", + "reference": "0de330ec2ea922a7b08ec45615bd51179de7fda4", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/deprecation-contracts": "^2.5|^3" }, - "conflict": { - "symfony/config": "<6.4", - "symfony/dependency-injection": "<6.4", - "symfony/yaml": "<6.4" - }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/http-foundation": "^6.4|^7.0|^8.0", - "symfony/yaml": "^6.4|^7.0|^8.0" + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -9007,7 +8889,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.4.8" + "source": "https://github.com/symfony/routing/tree/v8.0.8" }, "funding": [ { @@ -9027,7 +8909,7 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/service-contracts", @@ -9383,24 +9265,24 @@ }, { "name": "symfony/uid", - "version": "v7.4.8", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "6883ebdf7bf6a12b37519dbc0df62b0222401b56" + "reference": "f63fa6096a24147283bce4d29327d285326438e0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/6883ebdf7bf6a12b37519dbc0df62b0222401b56", - "reference": "6883ebdf7bf6a12b37519dbc0df62b0222401b56", + "url": "https://api.github.com/repos/symfony/uid/zipball/f63fa6096a24147283bce4d29327d285326438e0", + "reference": "f63fa6096a24147283bce4d29327d285326438e0", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/polyfill-uuid": "^1.15" }, "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0" + "symfony/console": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -9437,7 +9319,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.4.8" + "source": "https://github.com/symfony/uid/tree/v8.0.8" }, "funding": [ { @@ -9457,35 +9339,35 @@ "type": "tidelift" } ], - "time": "2026-03-24T13:12:05+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.4.8", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "9510c3966f749a1d1ff0059e1eabef6cc621e7fd" + "reference": "cfb7badd53bf4177f6e9416cfbbccc13c0e773a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/9510c3966f749a1d1ff0059e1eabef6cc621e7fd", - "reference": "9510c3966f749a1d1ff0059e1eabef6cc621e7fd", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/cfb7badd53bf4177f6e9416cfbbccc13c0e773a1", + "reference": "cfb7badd53bf4177f6e9416cfbbccc13c0e773a1", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0" }, "conflict": { - "symfony/console": "<6.4" + "symfony/console": "<7.4", + "symfony/error-handler": "<7.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/uid": "^6.4|^7.0|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/uid": "^7.4|^8.0", "twig/twig": "^3.12" }, "bin": [ @@ -9524,7 +9406,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.4.8" + "source": "https://github.com/symfony/var-dumper/tree/v8.0.8" }, "funding": [ { @@ -9544,7 +9426,7 @@ "type": "tidelift" } ], - "time": "2026-03-30T13:44:50+00:00" + "time": "2026-03-31T07:15:36+00:00" }, { "name": "symfony/var-exporter", @@ -12231,16 +12113,16 @@ }, { "name": "sebastian/comparator", - "version": "8.1.1", + "version": "8.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "44b063d0a64da0e8ea74fb6464d8de2b1429ab7e" + "reference": "b3d09f4360ad97dcad8f82d1c047ad16ff38b7e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/44b063d0a64da0e8ea74fb6464d8de2b1429ab7e", - "reference": "44b063d0a64da0e8ea74fb6464d8de2b1429ab7e", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/b3d09f4360ad97dcad8f82d1c047ad16ff38b7e1", + "reference": "b3d09f4360ad97dcad8f82d1c047ad16ff38b7e1", "shasum": "" }, "require": { @@ -12299,7 +12181,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/8.1.1" + "source": "https://github.com/sebastianbergmann/comparator/tree/8.1.2" }, "funding": [ { @@ -12319,7 +12201,7 @@ "type": "tidelift" } ], - "time": "2026-04-08T04:47:31+00:00" + "time": "2026-04-14T08:24:42+00:00" }, { "name": "sebastian/complexity", @@ -13439,7 +13321,6 @@ "ext-pdo": "*", "ext-session": "*", "ext-simplexml": "*", - "ext-sodium": "*", "ext-tokenizer": "*", "ext-xml": "*", "ext-xmlwriter": "*" diff --git a/database/migrations/2026_04_13_185808_migrations_04_2026.php b/database/migrations/2026_04_13_185808_migrations_04_2026.php new file mode 100644 index 0000000000..4ec5be717c --- /dev/null +++ b/database/migrations/2026_04_13_185808_migrations_04_2026.php @@ -0,0 +1,88 @@ +char('id', 80)->primary(); + $table->foreignId('user_id')->index(); + $table->foreignUuid('client_id'); + $table->text('scopes')->nullable(); + $table->boolean('revoked'); + $table->dateTime('expires_at')->nullable(); + }); + + Schema::create('oauth_access_tokens', function (Blueprint $table) { + $table->char('id', 80)->primary(); + $table->foreignId('user_id')->nullable()->index(); + $table->foreignUuid('client_id'); + $table->string('name')->nullable(); + $table->text('scopes')->nullable(); + $table->boolean('revoked'); + $table->timestamps(); + $table->dateTime('expires_at')->nullable(); + }); + + Schema::create('oauth_refresh_tokens', function (Blueprint $table) { + $table->char('id', 80)->primary(); + $table->char('access_token_id', 80)->index(); + $table->boolean('revoked'); + $table->dateTime('expires_at')->nullable(); + }); + + Schema::create('oauth_clients', function (Blueprint $table) { + $table->uuid('id')->primary(); + $table->nullableMorphs('owner'); + $table->string('name'); + $table->string('secret')->nullable(); + $table->string('provider')->nullable(); + $table->text('redirect_uris'); + $table->text('grant_types'); + $table->boolean('revoked'); + $table->timestamps(); + }); + + + Schema::create('oauth_device_codes', function (Blueprint $table) { + $table->char('id', 80)->primary(); + $table->foreignId('user_id')->nullable()->index(); + $table->foreignUuid('client_id')->index(); + $table->char('user_code', 8)->unique(); + $table->text('scopes'); + $table->boolean('revoked'); + $table->dateTime('user_approved_at')->nullable(); + $table->dateTime('last_polled_at')->nullable(); + $table->dateTime('expires_at')->nullable(); + }); + + + + + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // + } +}; diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index fb2eaaa07b..e34b6592c7 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -1638,6 +1638,7 @@ return [ 'external_auth_disabled' => 'This action is not available when Firefly III isn\'t responsible for authentication handling.', 'delete_local_info_only' => "Because Firefly III isn't responsible for user management or authentication handling, this function will only delete local Firefly III information.", 'oauth' => 'OAuth', + 'oauth_tokens' => 'Remote access and tokens', 'profile_oauth_clients' => 'OAuth Clients', 'profile_oauth_no_clients' => 'You have not created any OAuth clients.', 'profile_oauth_clients_external_auth' => 'If you\'re using an external authentication provider like Authelia, OAuth Clients will not work. You can use Personal Access Tokens only.', diff --git a/resources/views/auth/oauth/authorize.blade.php b/resources/views/auth/oauth/authorize.blade.php new file mode 100644 index 0000000000..ca86a9b76a --- /dev/null +++ b/resources/views/auth/oauth/authorize.blade.php @@ -0,0 +1,94 @@ + + +
+ + + + +{{ $client->name }} is requesting permission to access your account.
+ + + @if (count($scopes) > 0) +This application will be able to:
+ +