From b5b6caa400972dd853fb556c978587866a79d1bc Mon Sep 17 00:00:00 2001 From: Ron Rise Date: Fri, 2 Jan 2026 13:57:41 -0500 Subject: [PATCH] more updates --- composer.json | 6 +- composer.lock | 1391 ++++++++--------- .../000001_create_users_table.down.sql | 3 +- .../000001_create_users_table.up.sql | 21 +- src/Api.php | 15 +- src/Async/Consumer.php | 1 + src/Cli/App.php | 44 +- src/Cli/ClimateOutput.php | 213 +++ src/Cli/Commands/Command.php | 15 +- src/Cli/Commands/CommandInterface.php | 7 +- src/Cli/Commands/Crypt/GenerateKey.php | 16 +- src/Cli/Commands/OAuth/AddRedirectUri.php | 61 +- src/Cli/Commands/OAuth/AddScope.php | 46 + src/Cli/Commands/OAuth/CreateClient.php | 49 +- src/Cli/Commands/OAuth/ListClients.php | 75 + src/Cli/Commands/Queue/Start.php | 32 +- src/Cli/Commands/User/Add.php | 68 +- src/Controllers/AccessTokenController.php | 35 +- src/Models/Model.php | 1 + src/OAuth/Entities/AccessToken.php | 24 +- 20 files changed, 1251 insertions(+), 872 deletions(-) create mode 100644 src/Cli/ClimateOutput.php create mode 100644 src/Cli/Commands/OAuth/AddScope.php create mode 100644 src/Cli/Commands/OAuth/ListClients.php diff --git a/composer.json b/composer.json index ed8837d..dc85331 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,6 @@ "predis/predis": "^v3.2.0", "siteworxpro/http-status": "0.0.2", "lcobucci/jwt": "^5.6", - "adhocore/cli": "^1.9", "robinvdvleuten/ulid": "^5.0", "monolog/monolog": "^3.9", "react/promise": "^3", @@ -32,13 +31,14 @@ "league/oauth2-server": "^9.3", "ext-sodium": "*", "league/climate": "^3.10", - "hansott/psr7-cookies": "^4.0" + "hansott/psr7-cookies": "^4.0", + "symfony/console": "^v7.4.3" }, "require-dev": { "phpunit/phpunit": "^12.4", "mockery/mockery": "^1.6", "squizlabs/php_codesniffer": "^4.0", - "lendable/composer-license-checker": "^1.2", + "lendable/composer-license-checker": "^1.3.0", "phpstan/phpstan": "^2.1.31", "kwn/php-rdkafka-stubs": "^2.2" }, diff --git a/composer.lock b/composer.lock index 6c41197..238f701 100644 --- a/composer.lock +++ b/composer.lock @@ -4,93 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9344732ab599b7f8ea85a31743adcee1", + "content-hash": "7aee32dcb1c3e870610dda4d399383f0", "packages": [ - { - "name": "adhocore/cli", - "version": "v1.9.4", - "source": { - "type": "git", - "url": "https://github.com/adhocore/php-cli.git", - "reference": "474dc3d7ab139796be98b104d891476e3916b6f4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/adhocore/php-cli/zipball/474dc3d7ab139796be98b104d891476e3916b6f4", - "reference": "474dc3d7ab139796be98b104d891476e3916b6f4", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "Ahc\\Cli\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jitendra Adhikari", - "email": "jiten.adhikary@gmail.com" - } - ], - "description": "Command line interface library for PHP", - "keywords": [ - "argument-parser", - "argv-parser", - "cli", - "cli-action", - "cli-app", - "cli-color", - "cli-option", - "cli-writer", - "command", - "console", - "console-app", - "php-cli", - "php8", - "stream-input", - "stream-output" - ], - "support": { - "issues": "https://github.com/adhocore/php-cli/issues", - "source": "https://github.com/adhocore/php-cli/tree/v1.9.4" - }, - "funding": [ - { - "url": "https://paypal.me/ji10", - "type": "custom" - }, - { - "url": "https://github.com/adhocore", - "type": "github" - } - ], - "time": "2025-05-11T13:23:54+00:00" - }, { "name": "brick/math", - "version": "0.14.0", + "version": "0.14.1", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2" + "reference": "f05858549e5f9d7bb45875a75583240a38a281d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2", - "reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2", + "url": "https://api.github.com/repos/brick/math/zipball/f05858549e5f9d7bb45875a75583240a38a281d0", + "reference": "f05858549e5f9d7bb45875a75583240a38a281d0", "shasum": "" }, "require": { @@ -129,7 +56,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.14.0" + "source": "https://github.com/brick/math/tree/0.14.1" }, "funding": [ { @@ -137,7 +64,7 @@ "type": "github" } ], - "time": "2025-08-29T12:40:03+00:00" + "time": "2025-11-24T14:40:29+00:00" }, { "name": "carbonphp/carbon-doctrine-types", @@ -426,16 +353,16 @@ }, { "name": "google/protobuf", - "version": "v4.33.1", + "version": "v4.33.2", "source": { "type": "git", "url": "https://github.com/protocolbuffers/protobuf-php.git", - "reference": "0cd73ccf0cd26c3e72299cce1ea6144091a57e12" + "reference": "fbd96b7bf1343f4b0d8fb358526c7ba4d72f1318" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/0cd73ccf0cd26c3e72299cce1ea6144091a57e12", - "reference": "0cd73ccf0cd26c3e72299cce1ea6144091a57e12", + "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/fbd96b7bf1343f4b0d8fb358526c7ba4d72f1318", + "reference": "fbd96b7bf1343f4b0d8fb358526c7ba4d72f1318", "shasum": "" }, "require": { @@ -464,9 +391,9 @@ "proto" ], "support": { - "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.33.1" + "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.33.2" }, - "time": "2025-11-12T21:58:05+00:00" + "time": "2025-12-05T22:12:22+00:00" }, { "name": "guzzlehttp/guzzle", @@ -862,16 +789,16 @@ }, { "name": "illuminate/collections", - "version": "v12.38.1", + "version": "v12.44.0", "source": { "type": "git", "url": "https://github.com/illuminate/collections.git", - "reference": "deb291b109b6f7fd776a3550a120771137b3c5d1" + "reference": "62b357e85711932da73f90a606d80ac09027d54c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/collections/zipball/deb291b109b6f7fd776a3550a120771137b3c5d1", - "reference": "deb291b109b6f7fd776a3550a120771137b3c5d1", + "url": "https://api.github.com/repos/illuminate/collections/zipball/62b357e85711932da73f90a606d80ac09027d54c", + "reference": "62b357e85711932da73f90a606d80ac09027d54c", "shasum": "" }, "require": { @@ -879,6 +806,7 @@ "illuminate/contracts": "^12.0", "illuminate/macroable": "^12.0", "php": "^8.2", + "symfony/polyfill-php83": "^1.33", "symfony/polyfill-php84": "^1.33", "symfony/polyfill-php85": "^1.33" }, @@ -917,11 +845,11 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-10-30T12:22:05+00:00" + "time": "2025-12-21T14:16:59+00:00" }, { "name": "illuminate/conditionable", - "version": "v12.38.1", + "version": "v12.44.0", "source": { "type": "git", "url": "https://github.com/illuminate/conditionable.git", @@ -967,20 +895,21 @@ }, { "name": "illuminate/container", - "version": "v12.38.1", + "version": "v12.44.0", "source": { "type": "git", "url": "https://github.com/illuminate/container.git", - "reference": "d6eaa8afd48dbe16b6b3c412a87479cad67eeb12" + "reference": "326667a4c813e3ad5a645969a7e3f5c10d159de2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/container/zipball/d6eaa8afd48dbe16b6b3c412a87479cad67eeb12", - "reference": "d6eaa8afd48dbe16b6b3c412a87479cad67eeb12", + "url": "https://api.github.com/repos/illuminate/container/zipball/326667a4c813e3ad5a645969a7e3f5c10d159de2", + "reference": "326667a4c813e3ad5a645969a7e3f5c10d159de2", "shasum": "" }, "require": { "illuminate/contracts": "^12.0", + "illuminate/reflection": "^12.0", "php": "^8.2", "psr/container": "^1.1.1|^2.0.1", "symfony/polyfill-php84": "^1.33", @@ -1024,20 +953,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-09-12T14:35:11+00:00" + "time": "2025-12-08T22:34:25+00:00" }, { "name": "illuminate/contracts", - "version": "v12.38.1", + "version": "v12.43.1", "source": { "type": "git", "url": "https://github.com/illuminate/contracts.git", - "reference": "5ab717c8f0dd4e84be703796bbb415ccff8de57a" + "reference": "19e8938edb73047017cfbd443b96844b86da4a59" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/contracts/zipball/5ab717c8f0dd4e84be703796bbb415ccff8de57a", - "reference": "5ab717c8f0dd4e84be703796bbb415ccff8de57a", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/19e8938edb73047017cfbd443b96844b86da4a59", + "reference": "19e8938edb73047017cfbd443b96844b86da4a59", "shasum": "" }, "require": { @@ -1072,20 +1001,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-10-07T19:59:08+00:00" + "time": "2025-11-26T21:36:01+00:00" }, { "name": "illuminate/database", - "version": "v12.38.1", + "version": "v12.44.0", "source": { "type": "git", "url": "https://github.com/illuminate/database.git", - "reference": "eacbdddf31f655fba5406fdf31bd264d880dd1a8" + "reference": "90e57a3b7d2ae57b00846c6572796b585264927f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/database/zipball/eacbdddf31f655fba5406fdf31bd264d880dd1a8", - "reference": "eacbdddf31f655fba5406fdf31bd264d880dd1a8", + "url": "https://api.github.com/repos/illuminate/database/zipball/90e57a3b7d2ae57b00846c6572796b585264927f", + "reference": "90e57a3b7d2ae57b00846c6572796b585264927f", "shasum": "" }, "require": { @@ -1098,6 +1027,7 @@ "illuminate/support": "^12.0", "laravel/serializable-closure": "^1.3|^2.0", "php": "^8.2", + "symfony/polyfill-php83": "^1.33", "symfony/polyfill-php85": "^1.33" }, "suggest": { @@ -1143,11 +1073,11 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-11-11T14:13:21+00:00" + "time": "2025-12-22T15:31:12+00:00" }, { "name": "illuminate/macroable", - "version": "v12.38.1", + "version": "v12.43.1", "source": { "type": "git", "url": "https://github.com/illuminate/macroable.git", @@ -1192,17 +1122,68 @@ "time": "2024-07-23T16:31:01+00:00" }, { - "name": "illuminate/support", - "version": "v12.38.1", + "name": "illuminate/reflection", + "version": "v12.43.1", "source": { "type": "git", - "url": "https://github.com/illuminate/support.git", - "reference": "008b6c0d45f548de0f801d60a5854a7f9e4dd32f" + "url": "https://github.com/illuminate/reflection.git", + "reference": "7b86bc570d5b75e4a3ad79f8cca1491ba24b7c75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/support/zipball/008b6c0d45f548de0f801d60a5854a7f9e4dd32f", - "reference": "008b6c0d45f548de0f801d60a5854a7f9e4dd32f", + "url": "https://api.github.com/repos/illuminate/reflection/zipball/7b86bc570d5b75e4a3ad79f8cca1491ba24b7c75", + "reference": "7b86bc570d5b75e4a3ad79f8cca1491ba24b7c75", + "shasum": "" + }, + "require": { + "illuminate/collections": "^12.0", + "illuminate/contracts": "^12.0", + "php": "^8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "12.x-dev" + } + }, + "autoload": { + "files": [ + "helpers.php" + ], + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Reflection package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2025-12-09T15:11:22+00:00" + }, + { + "name": "illuminate/support", + "version": "v12.44.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/support.git", + "reference": "82c5c520506618fdbf6eceb1164c770ed722d35a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/support/zipball/82c5c520506618fdbf6eceb1164c770ed722d35a", + "reference": "82c5c520506618fdbf6eceb1164c770ed722d35a", "shasum": "" }, "require": { @@ -1214,6 +1195,7 @@ "illuminate/conditionable": "^12.0", "illuminate/contracts": "^12.0", "illuminate/macroable": "^12.0", + "illuminate/reflection": "^12.0", "nesbot/carbon": "^3.8.4", "php": "^8.2", "symfony/polyfill-php83": "^1.33", @@ -1268,20 +1250,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-11-06T14:27:18+00:00" + "time": "2025-12-21T14:15:54+00:00" }, { "name": "laravel/serializable-closure", - "version": "v2.0.6", + "version": "v2.0.7", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "038ce42edee619599a1debb7e81d7b3759492819" + "reference": "cb291e4c998ac50637c7eeb58189c14f5de5b9dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/038ce42edee619599a1debb7e81d7b3759492819", - "reference": "038ce42edee619599a1debb7e81d7b3759492819", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/cb291e4c998ac50637c7eeb58189c14f5de5b9dd", + "reference": "cb291e4c998ac50637c7eeb58189c14f5de5b9dd", "shasum": "" }, "require": { @@ -1290,7 +1272,7 @@ "require-dev": { "illuminate/support": "^10.0|^11.0|^12.0", "nesbot/carbon": "^2.67|^3.0", - "pestphp/pest": "^2.36|^3.0", + "pestphp/pest": "^2.36|^3.0|^4.0", "phpstan/phpstan": "^2.0", "symfony/var-dumper": "^6.2.0|^7.0.0" }, @@ -1329,7 +1311,7 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2025-10-09T13:42:30+00:00" + "time": "2025-11-21T20:52:36+00:00" }, { "name": "lcobucci/clock", @@ -1781,16 +1763,16 @@ }, { "name": "league/tactician", - "version": "v1.1.0", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/thephpleague/tactician.git", - "reference": "e79f763170f3d5922ec29e85cffca0bac5cd8975" + "reference": "93b2eafa4321f84164f3d3f607a32a953f3fff0b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/tactician/zipball/e79f763170f3d5922ec29e85cffca0bac5cd8975", - "reference": "e79f763170f3d5922ec29e85cffca0bac5cd8975", + "url": "https://api.github.com/repos/thephpleague/tactician/zipball/93b2eafa4321f84164f3d3f607a32a953f3fff0b", + "reference": "93b2eafa4321f84164f3d3f607a32a953f3fff0b", "shasum": "" }, "require": { @@ -1830,9 +1812,9 @@ ], "support": { "issues": "https://github.com/thephpleague/tactician/issues", - "source": "https://github.com/thephpleague/tactician/tree/v1.1.0" + "source": "https://github.com/thephpleague/tactician/tree/v1.2.0" }, - "time": "2021-02-14T15:29:04+00:00" + "time": "2025-12-21T18:05:37+00:00" }, { "name": "league/uri", @@ -2018,16 +2000,16 @@ }, { "name": "monolog/monolog", - "version": "3.9.0", + "version": "3.10.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6" + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/10d85740180ecba7896c87e06a166e0c95a0e3b6", - "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/b321dd6749f0bf7189444158a3ce785cc16d69b0", + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0", "shasum": "" }, "require": { @@ -2045,7 +2027,7 @@ "graylog2/gelf-php": "^1.4.2 || ^2.0", "guzzlehttp/guzzle": "^7.4.5", "guzzlehttp/psr7": "^2.2", - "mongodb/mongodb": "^1.8", + "mongodb/mongodb": "^1.8 || ^2.0", "php-amqplib/php-amqplib": "~2.4 || ^3", "php-console/php-console": "^3.1.8", "phpstan/phpstan": "^2", @@ -2105,7 +2087,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/3.9.0" + "source": "https://github.com/Seldaek/monolog/tree/3.10.0" }, "funding": [ { @@ -2117,20 +2099,20 @@ "type": "tidelift" } ], - "time": "2025-03-24T10:02:05+00:00" + "time": "2026-01-02T08:56:05+00:00" }, { "name": "nesbot/carbon", - "version": "3.10.3", + "version": "3.11.0", "source": { "type": "git", "url": "https://github.com/CarbonPHP/carbon.git", - "reference": "8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f" + "reference": "bdb375400dcd162624531666db4799b36b64e4a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f", - "reference": "8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/bdb375400dcd162624531666db4799b36b64e4a1", + "reference": "bdb375400dcd162624531666db4799b36b64e4a1", "shasum": "" }, "require": { @@ -2138,9 +2120,9 @@ "ext-json": "*", "php": "^8.1", "psr/clock": "^1.0", - "symfony/clock": "^6.3.12 || ^7.0", + "symfony/clock": "^6.3.12 || ^7.0 || ^8.0", "symfony/polyfill-mbstring": "^1.0", - "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0" + "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0 || ^8.0" }, "provide": { "psr/clock-implementation": "1.0" @@ -2222,7 +2204,7 @@ "type": "tidelift" } ], - "time": "2025-09-06T13:39:36+00:00" + "time": "2025-12-02T21:04:28+00:00" }, { "name": "nikic/fast-route", @@ -2276,16 +2258,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.6.2", + "version": "v5.7.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "3a454ca033b9e06b63282ce19562e892747449bb" + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", - "reference": "3a454ca033b9e06b63282ce19562e892747449bb", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { @@ -2328,9 +2310,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "time": "2025-10-21T19:32:17+00:00" + "time": "2025-12-06T11:56:16+00:00" }, { "name": "nyholm/psr7", @@ -2509,16 +2491,16 @@ }, { "name": "predis/predis", - "version": "v3.2.0", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/predis/predis.git", - "reference": "9e9deec4dfd3ebf65d32eb368f498c646ba2ecd8" + "reference": "153097374b39a2f737fe700ebcd725642526cdec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/predis/predis/zipball/9e9deec4dfd3ebf65d32eb368f498c646ba2ecd8", - "reference": "9e9deec4dfd3ebf65d32eb368f498c646ba2ecd8", + "url": "https://api.github.com/repos/predis/predis/zipball/153097374b39a2f737fe700ebcd725642526cdec", + "reference": "153097374b39a2f737fe700ebcd725642526cdec", "shasum": "" }, "require": { @@ -2560,7 +2542,7 @@ ], "support": { "issues": "https://github.com/predis/predis/issues", - "source": "https://github.com/predis/predis/tree/v3.2.0" + "source": "https://github.com/predis/predis/tree/v3.3.0" }, "funding": [ { @@ -2568,7 +2550,7 @@ "type": "github" } ], - "time": "2025-08-06T06:41:24+00:00" + "time": "2025-11-24T17:48:50+00:00" }, { "name": "psr/clock", @@ -3216,16 +3198,16 @@ }, { "name": "react/event-loop", - "version": "v1.5.0", + "version": "v1.6.0", "source": { "type": "git", "url": "https://github.com/reactphp/event-loop.git", - "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354" + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", - "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/ba276bda6083df7e0050fd9b33f66ad7a4ac747a", + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a", "shasum": "" }, "require": { @@ -3276,7 +3258,7 @@ ], "support": { "issues": "https://github.com/reactphp/event-loop/issues", - "source": "https://github.com/reactphp/event-loop/tree/v1.5.0" + "source": "https://github.com/reactphp/event-loop/tree/v1.6.0" }, "funding": [ { @@ -3284,7 +3266,7 @@ "type": "open_collective" } ], - "time": "2023-11-13T13:48:05+00:00" + "time": "2025-11-17T20:46:25+00:00" }, { "name": "react/promise", @@ -3766,16 +3748,16 @@ }, { "name": "spiral/roadrunner", - "version": "v2025.1.5", + "version": "v2025.1.6", "source": { "type": "git", "url": "https://github.com/roadrunner-server/roadrunner.git", - "reference": "d68bee29eb689c5310f8ede935c95a13bd7cc153" + "reference": "207b2b4ba75f2529ecccf801b3d8dd7038f22732" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/roadrunner-server/roadrunner/zipball/d68bee29eb689c5310f8ede935c95a13bd7cc153", - "reference": "d68bee29eb689c5310f8ede935c95a13bd7cc153", + "url": "https://api.github.com/repos/roadrunner-server/roadrunner/zipball/207b2b4ba75f2529ecccf801b3d8dd7038f22732", + "reference": "207b2b4ba75f2529ecccf801b3d8dd7038f22732", "shasum": "" }, "type": "metapackage", @@ -3803,7 +3785,7 @@ "chat": "https://discord.gg/V6EK4he", "docs": "https://docs.roadrunner.dev/", "issues": "https://github.com/roadrunner-server/roadrunner/issues", - "source": "https://github.com/roadrunner-server/roadrunner/tree/v2025.1.5" + "source": "https://github.com/roadrunner-server/roadrunner/tree/v2025.1.6" }, "funding": [ { @@ -3811,7 +3793,7 @@ "type": "github" } ], - "time": "2025-11-13T17:24:29+00:00" + "time": "2025-12-11T14:45:22+00:00" }, { "name": "spiral/roadrunner-grpc", @@ -4071,22 +4053,21 @@ }, { "name": "symfony/clock", - "version": "v7.3.0", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/clock.git", - "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24" + "reference": "832119f9b8dbc6c8e6f65f30c5969eca1e88764f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", - "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + "url": "https://api.github.com/repos/symfony/clock/zipball/832119f9b8dbc6c8e6f65f30c5969eca1e88764f", + "reference": "832119f9b8dbc6c8e6f65f30c5969eca1e88764f", "shasum": "" }, "require": { - "php": ">=8.2", - "psr/clock": "^1.0", - "symfony/polyfill-php83": "^1.28" + "php": ">=8.4", + "psr/clock": "^1.0" }, "provide": { "psr/clock-implementation": "1.0" @@ -4125,7 +4106,7 @@ "time" ], "support": { - "source": "https://github.com/symfony/clock/tree/v7.3.0" + "source": "https://github.com/symfony/clock/tree/v8.0.0" }, "funding": [ { @@ -4136,12 +4117,114 @@ "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": "2024-09-25T14:21:43+00:00" + "time": "2025-11-12T15:46:48+00:00" + }, + { + "name": "symfony/console", + "version": "v7.4.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "732a9ca6cd9dfd940c639062d5edbde2f6727fb6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/732a9ca6cd9dfd940c639062d5edbde2f6727fb6", + "reference": "732a9ca6cd9dfd940c639062d5edbde2f6727fb6", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "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" + }, + "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" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.4.3" + }, + "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": "2025-12-23T14:50:43+00:00" }, { "name": "symfony/deprecation-contracts", @@ -4212,16 +4295,16 @@ }, { "name": "symfony/finder", - "version": "v8.0.0", + "version": "v8.0.3", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "7598dd5770580fa3517ec83e8da0c9b9e01f4291" + "reference": "dd3a2953570a283a2ba4e17063bb98c734cf5b12" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/7598dd5770580fa3517ec83e8da0c9b9e01f4291", - "reference": "7598dd5770580fa3517ec83e8da0c9b9e01f4291", + "url": "https://api.github.com/repos/symfony/finder/zipball/dd3a2953570a283a2ba4e17063bb98c734cf5b12", + "reference": "dd3a2953570a283a2ba4e17063bb98c734cf5b12", "shasum": "" }, "require": { @@ -4256,7 +4339,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v8.0.0" + "source": "https://github.com/symfony/finder/tree/v8.0.3" }, "funding": [ { @@ -4276,7 +4359,7 @@ "type": "tidelift" } ], - "time": "2025-11-05T14:36:47+00:00" + "time": "2025-12-23T14:52:06+00:00" }, { "name": "symfony/polyfill-ctype", @@ -4361,6 +4444,173 @@ ], "time": "2024-09-09T11:45:10+00:00" }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "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 for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.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": "2025-06-27T09:58:17+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "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 for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.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": "2024-09-09T11:45:10+00:00" + }, { "name": "symfony/polyfill-mbstring", "version": "v1.33.0", @@ -4687,35 +4937,205 @@ "time": "2025-06-23T16:12:55+00:00" }, { - "name": "symfony/translation", - "version": "v7.3.4", + "name": "symfony/service-contracts", + "version": "v3.6.1", "source": { "type": "git", - "url": "https://github.com/symfony/translation.git", - "reference": "ec25870502d0c7072d086e8ffba1420c85965174" + "url": "https://github.com/symfony/service-contracts.git", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/ec25870502d0c7072d086e8ffba1420c85965174", - "reference": "ec25870502d0c7072d086e8ffba1420c85965174", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^2.5|^3.0" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "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": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" + }, + "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": "2025-07-15T11:30:57+00:00" + }, + { + "name": "symfony/string", + "version": "v8.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/ba65a969ac918ce0cc3edfac6cdde847eba231dc", + "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-intl-grapheme": "^1.33", + "symfony/polyfill-intl-normalizer": "^1.0", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "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": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v8.0.1" + }, + "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": "2025-12-01T09:13:36+00:00" + }, + { + "name": "symfony/translation", + "version": "v8.0.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "60a8f11f0e15c48f2cc47c4da53873bb5b62135d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/60a8f11f0e15c48f2cc47c4da53873bb5b62135d", + "reference": "60a8f11f0e15c48f2cc47c4da53873bb5b62135d", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation-contracts": "^3.6.1" }, "conflict": { "nikic/php-parser": "<5.0", - "symfony/config": "<6.4", - "symfony/console": "<6.4", - "symfony/dependency-injection": "<6.4", "symfony/http-client-contracts": "<2.5", - "symfony/http-kernel": "<6.4", - "symfony/service-contracts": "<2.5", - "symfony/twig-bundle": "<6.4", - "symfony/yaml": "<6.4" + "symfony/service-contracts": "<2.5" }, "provide": { "symfony/translation-implementation": "2.3|3.0" @@ -4723,17 +5143,17 @@ "require-dev": { "nikic/php-parser": "^5.0", "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", "symfony/http-client-contracts": "^2.5|^3.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", "symfony/polyfill-intl-icu": "^1.21", - "symfony/routing": "^6.4|^7.0", + "symfony/routing": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^6.4|^7.0" + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -4764,7 +5184,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v7.3.4" + "source": "https://github.com/symfony/translation/tree/v8.0.3" }, "funding": [ { @@ -4784,7 +5204,7 @@ "type": "tidelift" } ], - "time": "2025-09-07T11:39:36+00:00" + "time": "2025-12-21T10:59:45+00:00" }, { "name": "symfony/translation-contracts", @@ -4870,28 +5290,27 @@ }, { "name": "symfony/yaml", - "version": "v7.4.0", + "version": "v8.0.1", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "6c84a4b55aee4cd02034d1c528e83f69ddf63810" + "reference": "7a1a90ba1df6e821a6b53c4cabdc32a56cabfb14" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/6c84a4b55aee4cd02034d1c528e83f69ddf63810", - "reference": "6c84a4b55aee4cd02034d1c528e83f69ddf63810", + "url": "https://api.github.com/repos/symfony/yaml/zipball/7a1a90ba1df6e821a6b53c4cabdc32a56cabfb14", + "reference": "7a1a90ba1df6e821a6b53c4cabdc32a56cabfb14", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=8.4", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/console": "<6.4" + "symfony/console": "<7.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0" + "symfony/console": "^7.4|^8.0" }, "bin": [ "Resources/bin/yaml-lint" @@ -4922,7 +5341,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.4.0" + "source": "https://github.com/symfony/yaml/tree/v8.0.1" }, "funding": [ { @@ -4942,7 +5361,7 @@ "type": "tidelift" } ], - "time": "2025-11-16T10:14:42+00:00" + "time": "2025-12-04T18:17:06+00:00" }, { "name": "voku/portable-ascii", @@ -5020,16 +5439,16 @@ }, { "name": "zircote/swagger-php", - "version": "5.7.5", + "version": "5.7.6", "source": { "type": "git", "url": "https://github.com/zircote/swagger-php.git", - "reference": "9a37739401485b42d779495e70548309820d11d6" + "reference": "e4727bad28cf426b026421162af384f893c0142c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zircote/swagger-php/zipball/9a37739401485b42d779495e70548309820d11d6", - "reference": "9a37739401485b42d779495e70548309820d11d6", + "url": "https://api.github.com/repos/zircote/swagger-php/zipball/e4727bad28cf426b026421162af384f893c0142c", + "reference": "e4727bad28cf426b026421162af384f893c0142c", "shasum": "" }, "require": { @@ -5102,9 +5521,9 @@ ], "support": { "issues": "https://github.com/zircote/swagger-php/issues", - "source": "https://github.com/zircote/swagger-php/tree/5.7.5" + "source": "https://github.com/zircote/swagger-php/tree/5.7.6" }, - "time": "2025-11-28T23:22:21+00:00" + "time": "2025-12-04T01:33:01+00:00" } ], "packages-dev": [ @@ -5199,21 +5618,21 @@ }, { "name": "lendable/composer-license-checker", - "version": "1.2.2", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/Lendable/composer-license-checker.git", - "reference": "476039e57ceb3e6899a2d9d448ba5203cdfd6e97" + "reference": "c211364ae0c8139de36b35d14a675734047161a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Lendable/composer-license-checker/zipball/476039e57ceb3e6899a2d9d448ba5203cdfd6e97", - "reference": "476039e57ceb3e6899a2d9d448ba5203cdfd6e97", + "url": "https://api.github.com/repos/Lendable/composer-license-checker/zipball/c211364ae0c8139de36b35d14a675734047161a9", + "reference": "c211364ae0c8139de36b35d14a675734047161a9", "shasum": "" }, "require": { "composer-runtime-api": "^2.2", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", "symfony/console": "^5.4 || ^6.0 || ^7.0", "symfony/process": "^5.4.46 || ^6.4.14 || ^7.1.7" }, @@ -5251,9 +5670,9 @@ "description": "Composer license checker", "support": { "issues": "https://github.com/Lendable/composer-license-checker/issues", - "source": "https://github.com/Lendable/composer-license-checker/tree/1.2.2" + "source": "https://github.com/Lendable/composer-license-checker/tree/1.3.0" }, - "time": "2024-11-29T18:58:25+00:00" + "time": "2025-12-05T17:34:04+00:00" }, { "name": "mockery/mockery", @@ -5518,11 +5937,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.32", + "version": "2.1.33", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e126cad1e30a99b137b8ed75a85a676450ebb227", - "reference": "e126cad1e30a99b137b8ed75a85a676450ebb227", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9e800e6bee7d5bd02784d4c6069b48032d16224f", + "reference": "9e800e6bee7d5bd02784d4c6069b48032d16224f", "shasum": "" }, "require": { @@ -5567,27 +5986,27 @@ "type": "github" } ], - "time": "2025-11-11T15:18:17+00:00" + "time": "2025-12-05T10:24:31+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "12.4.0", + "version": "12.5.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "67e8aed88f93d0e6e1cb7effe1a2dfc2fee6022c" + "reference": "4a9739b51cbcb355f6e95659612f92e282a7077b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/67e8aed88f93d0e6e1cb7effe1a2dfc2fee6022c", - "reference": "67e8aed88f93d0e6e1cb7effe1a2dfc2fee6022c", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4a9739b51cbcb355f6e95659612f92e282a7077b", + "reference": "4a9739b51cbcb355f6e95659612f92e282a7077b", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^5.6.1", + "nikic/php-parser": "^5.7.0", "php": ">=8.3", "phpunit/php-file-iterator": "^6.0", "phpunit/php-text-template": "^5.0", @@ -5595,10 +6014,10 @@ "sebastian/environment": "^8.0.3", "sebastian/lines-of-code": "^4.0", "sebastian/version": "^6.0", - "theseer/tokenizer": "^1.2.3" + "theseer/tokenizer": "^2.0.1" }, "require-dev": { - "phpunit/phpunit": "^12.3.7" + "phpunit/phpunit": "^12.5.1" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -5607,7 +6026,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "12.4.x-dev" + "dev-main": "12.5.x-dev" } }, "autoload": { @@ -5636,7 +6055,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.4.0" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.5.2" }, "funding": [ { @@ -5656,7 +6075,7 @@ "type": "tidelift" } ], - "time": "2025-09-24T13:44:41+00:00" + "time": "2025-12-24T07:03:04+00:00" }, { "name": "phpunit/php-file-iterator", @@ -5905,16 +6324,16 @@ }, { "name": "phpunit/phpunit", - "version": "12.4.3", + "version": "12.5.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "d8f644d8d9bb904867f7a0aeb1bd306e0d966949" + "reference": "4ba0e923f9d3fc655de22f9547c01d15a41fc93a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d8f644d8d9bb904867f7a0aeb1bd306e0d966949", - "reference": "d8f644d8d9bb904867f7a0aeb1bd306e0d966949", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4ba0e923f9d3fc655de22f9547c01d15a41fc93a", + "reference": "4ba0e923f9d3fc655de22f9547c01d15a41fc93a", "shasum": "" }, "require": { @@ -5928,7 +6347,7 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.3", - "phpunit/php-code-coverage": "^12.4.0", + "phpunit/php-code-coverage": "^12.5.1", "phpunit/php-file-iterator": "^6.0.0", "phpunit/php-invoker": "^6.0.0", "phpunit/php-text-template": "^5.0.0", @@ -5950,7 +6369,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "12.4-dev" + "dev-main": "12.5-dev" } }, "autoload": { @@ -5982,7 +6401,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.4.3" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.4" }, "funding": [ { @@ -6006,7 +6425,7 @@ "type": "tidelift" } ], - "time": "2025-11-13T07:20:26+00:00" + "time": "2025-12-15T06:05:34+00:00" }, { "name": "sebastian/cli-parser", @@ -7036,283 +7455,18 @@ ], "time": "2024-10-20T05:08:20+00:00" }, - { - "name": "symfony/console", - "version": "v7.3.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c28ad91448f86c5f6d9d2c70f0cf68bf135f252a", - "reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.2" - }, - "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/dotenv": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/lock": "<6.4", - "symfony/process": "<6.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0|3.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Eases the creation of beautiful and testable command line interfaces", - "homepage": "https://symfony.com", - "keywords": [ - "cli", - "command-line", - "console", - "terminal" - ], - "support": { - "source": "https://github.com/symfony/console/tree/v7.3.6" - }, - "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": "2025-11-04T01:21:42+00:00" - }, - { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - } - }, - "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 for intl's grapheme_* functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "grapheme", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.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": "2025-06-27T09:58:17+00:00" - }, - { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, - "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 for intl's Normalizer class and related functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.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": "2024-09-09T11:45:10+00:00" - }, { "name": "symfony/process", - "version": "v7.3.4", + "version": "v7.4.3", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b" + "reference": "2f8e1a6cdf590ca63715da4d3a7a3327404a523f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b", - "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b", + "url": "https://api.github.com/repos/symfony/process/zipball/2f8e1a6cdf590ca63715da4d3a7a3327404a523f", + "reference": "2f8e1a6cdf590ca63715da4d3a7a3327404a523f", "shasum": "" }, "require": { @@ -7344,7 +7498,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.4" + "source": "https://github.com/symfony/process/tree/v7.4.3" }, "funding": [ { @@ -7364,204 +7518,27 @@ "type": "tidelift" } ], - "time": "2025-09-11T10:12:26+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v3.6.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "conflict": { - "ext-psr": "<1.1|>=2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] - }, - "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": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" - }, - "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": "2025-07-15T11:30:57+00:00" - }, - { - "name": "symfony/string", - "version": "v7.3.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "f96476035142921000338bad71e5247fbc138872" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872", - "reference": "f96476035142921000338bad71e5247fbc138872", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/translation-contracts": "<2.5" - }, - "require-dev": { - "symfony/emoji": "^7.1", - "symfony/http-client": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", - "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0" - }, - "type": "library", - "autoload": { - "files": [ - "Resources/functions.php" - ], - "psr-4": { - "Symfony\\Component\\String\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "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": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", - "homepage": "https://symfony.com", - "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" - ], - "support": { - "source": "https://github.com/symfony/string/tree/v7.3.4" - }, - "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": "2025-09-11T14:36:48+00:00" + "time": "2025-12-19T10:00:43+00:00" }, { "name": "theseer/tokenizer", - "version": "1.3.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "d74205c497bfbca49f34d4bc4c19c17e22db4ebb" + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/d74205c497bfbca49f34d4bc4c19c17e22db4ebb", - "reference": "d74205c497bfbca49f34d4bc4c19c17e22db4ebb", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/7989e43bf381af0eac72e4f0ca5bcbfa81658be4", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" + "php": "^8.1" }, "type": "library", "autoload": { @@ -7583,7 +7560,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.3.0" + "source": "https://github.com/theseer/tokenizer/tree/2.0.1" }, "funding": [ { @@ -7591,7 +7568,7 @@ "type": "github" } ], - "time": "2025-11-13T13:44:09+00:00" + "time": "2025-12-08T11:19:18+00:00" } ], "aliases": [], diff --git a/db/migrations/000001_create_users_table.down.sql b/db/migrations/000001_create_users_table.down.sql index eb39283..b4e1d0d 100644 --- a/db/migrations/000001_create_users_table.down.sql +++ b/db/migrations/000001_create_users_table.down.sql @@ -3,4 +3,5 @@ drop table if exists client_scopes; drop table if exists scopes; drop table if exists client_redirect_uris; drop table if exists clients; -drop table if exists users; \ No newline at end of file +drop table if exists users; +drop table if exists user_scopes; \ No newline at end of file diff --git a/db/migrations/000001_create_users_table.up.sql b/db/migrations/000001_create_users_table.up.sql index 809d7eb..1bb4474 100644 --- a/db/migrations/000001_create_users_table.up.sql +++ b/db/migrations/000001_create_users_table.up.sql @@ -38,7 +38,9 @@ create table scopes name varchar not null constraint scopes_name_key unique, - description varchar + description varchar not null, + created_at timestamp default now(), + updated_at timestamp default now() ); create table client_scopes @@ -89,3 +91,20 @@ create table client_users constraint client_users_client_id_user_id_key unique (client_id, user_id) ); + +create table user_scopes +( + id VARCHAR(26) not null + constraint user_scopes_pk + primary key, + user_id VARCHAR(26) not null + constraint user_scopes_user_id_fk + references users + on delete cascade, + scope_id VARCHAR(26) not null + constraint user_scopes_scope_id_fk + references scopes + on delete cascade, + constraint user_scopes_user_id_scope_id_key + unique (user_id, scope_id) +); \ No newline at end of file diff --git a/src/Api.php b/src/Api.php index c7b8c64..e57e882 100644 --- a/src/Api.php +++ b/src/Api.php @@ -78,22 +78,17 @@ class Api $this->router = new Router(); $this->router->get('/healthz', HealthcheckController::class . '::get'); - $this->router->group('/.well-known', function (RouteGroup $router) { - $router->get('/swagger.yaml', OpenApiController::class . '::get'); - $router->get('/swagger.json', OpenApiController::class . '::get'); - }); - - $this->router->group('/client', function (RouteGroup $group) { - $group->get('/capabilities', CapabilitiesController::class . '::get'); - }); - // Authorize URL $this->router->get('/authorize', AuthorizeController::class . '::get'); $this->router->post('/authorize', AuthorizeController::class . '::post'); + $this->router->group('/client', function (RouteGroup $group) { + $group->get('/capabilities', CapabilitiesController::class . '::get'); + $group->post('/access_token', AccessTokenController::class . '::post'); + }); + $this->router->group('/client/{client_id}', function (RouteGroup $group) { $group->get('/.well-known/openid-configuration', OpenIdController::class . '::get'); - $group->post('/access_token', AccessTokenController::class . '::post'); }); $this->router->middleware(new CorsMiddleware()); diff --git a/src/Async/Consumer.php b/src/Async/Consumer.php index ed7723e..3cc57b5 100644 --- a/src/Async/Consumer.php +++ b/src/Async/Consumer.php @@ -7,6 +7,7 @@ namespace Siteworxpro\App\Async; use Siteworxpro\App\Attributes\Async\HandlesMessage; use Siteworxpro\App\Async\Messages\Message; use Siteworxpro\App\Async\Queues\Queue; +use Siteworxpro\App\Cli\ClimateOutput; use Siteworxpro\App\Services\Facades\Broker; use Siteworxpro\App\Services\Facades\Logger; diff --git a/src/Cli/App.php b/src/Cli/App.php index cef6371..4acc449 100644 --- a/src/Cli/App.php +++ b/src/Cli/App.php @@ -4,14 +4,17 @@ declare(strict_types=1); namespace Siteworxpro\App\Cli; -use Ahc\Cli\Application; use Siteworxpro\App\Cli\Commands\Crypt\GenerateKey; use Siteworxpro\App\Cli\Commands\OAuth\AddRedirectUri; +use Siteworxpro\App\Cli\Commands\OAuth\AddScope; use Siteworxpro\App\Cli\Commands\OAuth\CreateClient; +use Siteworxpro\App\Cli\Commands\OAuth\ListClients; use Siteworxpro\App\Cli\Commands\Queue\Start; use Siteworxpro\App\Cli\Commands\User\Add; use Siteworxpro\App\Helpers\Version; use Siteworxpro\App\Kernel; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\ArgvInput; class App { @@ -23,27 +26,32 @@ class App public function __construct() { Kernel::boot(); - $this->app = new Application('Php-Auth', Version::VERSION); - $this->app->add(new CreateClient()); - $this->app->add(new AddRedirectUri()); - $this->app->add(new Add()); - $this->app->add(new Start()); - $this->app->add(new GenerateKey()); + $this->app = new Application('Siteworxpro Auth', Version::VERSION); + + $this->app->setCatchErrors(); + + $this->app->addCommand(new CreateClient()); + $this->app->addCommand(new ListClients()); + $this->app->addCommand(new AddRedirectUri()); + $this->app->addCommand(new Add()); + $this->app->addCommand(new Start()); + $this->app->addCommand(new GenerateKey()); + $this->app->addCommand(new AddScope()); } public function run(): int { - $this->app->logo( - <<app->handle($_SERVER['argv']); + + $output = new ClimateOutput(); + + try { + return $this->app->run(new ArgvInput(), $output); + } catch (\Exception $e) { + $output->error($e->getMessage()); + $output->error($e->getTraceAsString()); + + return $e->getCode() ?: 1; + } } } diff --git a/src/Cli/ClimateOutput.php b/src/Cli/ClimateOutput.php new file mode 100644 index 0000000..0c5152d --- /dev/null +++ b/src/Cli/ClimateOutput.php @@ -0,0 +1,213 @@ +CLImate = new CLImate(); + $this->formatter = new OutputFormatter($this->isDecorated()); + + // create stream output + $output = fopen('php://stdout', 'w'); + stream_set_blocking($output, true); + + $sections = []; + + parent::__construct( + $output, + $sections, + $this->getVerbosity(), + $this->isDecorated(), + $this->getFormatter() + ); + } + + public function write(iterable|string $messages, bool $newline = false, int $options = 0): void + { + if (is_string($messages)) { + $messages = [$messages]; + } + + foreach ($messages as $message) { + $message = $this->formatter->format($message); + if (!$newline) { + $this->CLImate->inline($message); + continue; + } + + $this->CLImate->out($message); + } + } + + protected function doWrite(string $message, bool $newline): void + { + if ($newline) { + $this->CLImate->out($message); + } else { + $this->CLImate->inline($message); + } + } + + public function setVerbosity(int $level): void + { + $this->verbosity = $level; + } + + public function getVerbosity(): int + { + return $this->verbosity; + } + + public function isQuiet(): bool + { + return $this->verbosity === self::VERBOSITY_QUIET; + } + + public function isVerbose(): bool + { + return $this->verbosity >= self::VERBOSITY_VERBOSE; + } + + public function isVeryVerbose(): bool + { + return $this->verbosity >= self::VERBOSITY_VERY_VERBOSE; + } + + public function isDebug(): bool + { + return $this->verbosity >= self::VERBOSITY_DEBUG; + } + + public function setDecorated(bool $decorated): void + { + $this->decorated = $decorated; + } + + public function isDecorated(): bool + { + return $this->decorated; + } + + public function setFormatter(OutputFormatterInterface $formatter): void + { + $this->formatter = $formatter; + } + + public function getFormatter(): OutputFormatterInterface + { + return $this->formatter; + } + + public function __call(string $name, array $arguments) + { + return $this->CLImate->$name(...$arguments); + } + + public function getErrorOutput(): OutputInterface + { + return $this; + } + + public function setErrorOutput(OutputInterface $error): void + { + // no-op + } + + public function section(): ConsoleSectionOutput + { + return $this; + } +} diff --git a/src/Cli/Commands/Command.php b/src/Cli/Commands/Command.php index f09641f..f06b6b7 100644 --- a/src/Cli/Commands/Command.php +++ b/src/Cli/Commands/Command.php @@ -4,17 +4,18 @@ declare(strict_types=1); namespace Siteworxpro\App\Cli\Commands; -use Ahc\Cli\Application as App; -use League\CLImate\CLImate; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\QuestionHelper; -abstract class Command extends \Ahc\Cli\Input\Command implements CommandInterface +abstract class Command extends \Symfony\Component\Console\Command\Command implements CommandInterface { - protected Climate $climate; + protected QuestionHelper $helper; - public function __construct(string $_name, string $_desc = '', bool $_allowUnknown = false, ?App $_app = null) + public function __construct(?string $name = null, ?callable $code = null) { - parent::__construct($_name, $_desc, $_allowUnknown, $_app); + parent::__construct($name, $code); - $this->climate = new CLImate(); + $this->helper = new QuestionHelper(); + $this->setHelperSet(new HelperSet([$this->helper])); } } diff --git a/src/Cli/Commands/CommandInterface.php b/src/Cli/Commands/CommandInterface.php index 990c3c2..0653e56 100644 --- a/src/Cli/Commands/CommandInterface.php +++ b/src/Cli/Commands/CommandInterface.php @@ -4,6 +4,11 @@ declare(strict_types=1); namespace Siteworxpro\App\Cli\Commands; +use Siteworxpro\App\Cli\ClimateOutput; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + interface CommandInterface { /** @@ -11,5 +16,5 @@ interface CommandInterface * * @return int */ - public function execute(): int; + public function __invoke(InputInterface | ArgvInput $input, OutputInterface | ClimateOutput $output): int; } diff --git a/src/Cli/Commands/Crypt/GenerateKey.php b/src/Cli/Commands/Crypt/GenerateKey.php index b60a753..c797563 100644 --- a/src/Cli/Commands/Crypt/GenerateKey.php +++ b/src/Cli/Commands/Crypt/GenerateKey.php @@ -5,25 +5,23 @@ declare(strict_types=1); namespace Siteworxpro\App\Cli\Commands\Crypt; use Random\RandomException; +use Siteworxpro\App\Cli\ClimateOutput; use Siteworxpro\App\Cli\Commands\Command; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Input\ArgvInput; +#[AsCommand('crypt:generate-key', 'Generate a new encryption key for the application')] class GenerateKey extends Command { - - public function __construct() - { - parent::__construct('crypt:generate-key', 'Generate a new encryption key for the application'); - } - /** * @throws RandomException */ - public function execute(): int + public function __invoke($input, $output): int { $key = base64_encode(random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES)); - $this->climate->info('Generated Encryption Key:'); - $this->climate->out('base64:' . $key); + $output->info('Generated Encryption Key:'); + $output->out('base64:' . $key); return 0; } diff --git a/src/Cli/Commands/OAuth/AddRedirectUri.php b/src/Cli/Commands/OAuth/AddRedirectUri.php index e892493..0be6645 100644 --- a/src/Cli/Commands/OAuth/AddRedirectUri.php +++ b/src/Cli/Commands/OAuth/AddRedirectUri.php @@ -5,51 +5,52 @@ declare(strict_types=1); namespace Siteworxpro\App\Cli\Commands\OAuth; use League\CLImate\TerminalObject\Dynamic\Input; +use Siteworxpro\App\Cli\ClimateOutput; use Siteworxpro\App\Models\ClientRedirectUri; use Siteworxpro\App\OAuth\Entities\Client; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Question\Question; +#[AsCommand(name: 'oauth:redirect-uri:create', description: 'Add a redirect URI to an existing OAuth client.')] class AddRedirectUri extends \Siteworxpro\App\Cli\Commands\Command { - public function __construct() - { - parent::__construct('oauth:redirect-uri:add', 'Add a redirect URI to an existing OAuth client.'); - } - - public function execute(): int + public function __invoke($input, $output): int { $clients = Client::all('id', 'name'); - /** @var Input $input */ - $input = $this->climate->input( - 'Select the OAuth client to add a redirect URI to' . PHP_EOL . - $clients->map(fn(Client $client) => "[$client->id $client->name]")->implode(PHP_EOL) . - PHP_EOL . - 'Enter the client ID: ' - ); - $input->accept( - $clients->pluck('id')->toArray() - ); + $clients->map(function (Client $client) use ($output) { + $output->info("[$client->id] $client->name"); + }); - $id = $input->prompt(); + $question = new Question('Enter the client ID: '); + $question->setAutocompleterValues($clients->pluck('id')->toArray()); - $client = Client::find($id); + $clientId = $this->helper->ask($input, $output, $question); + $client = Client::find($clientId); if (!$client) { - $this->climate->error('Client not found.'); - return 1; + $output->error('Client not found. Not a valid client ID.'); + + return Command::FAILURE; } - /** @var Input $uriInput */ - $uriInput = $this->climate->input('Enter the redirect URI to add: '); - $uriInput->accept(function (string $value) { - return filter_var($value, FILTER_VALIDATE_URL) !== false; - }, 'Please enter a valid URL.'); + $question = new Question('Enter the redirect URI to add: '); + $question->setValidator(function ($input): string { + if (filter_var($input, FILTER_VALIDATE_URL) === false) { + throw new \InvalidArgumentException('Please enter a valid URL.'); + } - $redirectUri = $uriInput->prompt(); + return $input; + }); + + $redirectUri = $this->helper->ask($input, $output, $question); $redirectUris = $client->clientRedirectUris; if (in_array($redirectUri, $redirectUris->toArray(), true)) { - $this->climate->error('The redirect URI already exists for this client.'); - return 1; + $output->error('The redirect URI already exists for this client.'); + + return Command::FAILURE; } $clientRedirectUri = new ClientRedirectUri(); @@ -57,6 +58,8 @@ class AddRedirectUri extends \Siteworxpro\App\Cli\Commands\Command $clientRedirectUri->redirect_uri = $redirectUri; $clientRedirectUri->save(); - return 0; + $output->green('Redirect URI added successfully.'); + + return Command::SUCCESS; } } diff --git a/src/Cli/Commands/OAuth/AddScope.php b/src/Cli/Commands/OAuth/AddScope.php new file mode 100644 index 0000000..4b48488 --- /dev/null +++ b/src/Cli/Commands/OAuth/AddScope.php @@ -0,0 +1,46 @@ +addArgument('name', InputArgument::REQUIRED, 'The name of the scope') + ->addArgument('description', InputArgument::OPTIONAL, 'The description of the scope'); + } + + public function __invoke(ArgvInput | InputInterface $input, $output): int + { + $name = $input->getArgument('name'); + $description = $input->getArgument('description') ?? ''; + + $scope = Scope::where('name', $name)->first(); + if ($scope) { + $output->error("Scope with name '$name' already exists."); + + return SympCommand::FAILURE; + } + + $scope = new Scope(); + $scope->name = $name; + $scope->description = $description; + $scope->save(); + + $output->green("Scope '$name' added successfully."); + + return SympCommand::SUCCESS; + } +} diff --git a/src/Cli/Commands/OAuth/CreateClient.php b/src/Cli/Commands/OAuth/CreateClient.php index 7056598..57ed955 100644 --- a/src/Cli/Commands/OAuth/CreateClient.php +++ b/src/Cli/Commands/OAuth/CreateClient.php @@ -4,45 +4,54 @@ declare(strict_types=1); namespace Siteworxpro\App\Cli\Commands\OAuth; -use Ahc\Cli\IO\Interactor; +use Siteworxpro\App\Cli\ClimateOutput; use Siteworxpro\App\CommandBus\Commands\CreateClient as CreateClientCommand; use Siteworxpro\App\CommandBus\Exceptions\CommandHandlerException; use Siteworxpro\App\OAuth\Entities\Client; use Siteworxpro\App\Services\Facades\CommandBus; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\Question; +#[AsCommand(name: 'oauth:client:create', description: 'Create a new OAuth client.')] class CreateClient extends \Siteworxpro\App\Cli\Commands\Command { - public function __construct() + public function __invoke($input, $output): int { - parent::__construct('oauth:client:create', 'Create a new OAuth client.'); - } + $question = new Question('Enter client name: '); + $clientName = $this->helper->ask($input, $output, $question); - public function execute(): int - { - $interactor = new Interactor(); - $clientName = $interactor->prompt('Enter client name'); - $clientDescription = $interactor->prompt('Enter client description (optional)', ''); - $clientGrantsString = $interactor->prompt( - 'Enter client grants (comma separated, e.g. "authorization_code,refresh_token")', - false - ); + $question = new Question('Enter client description (optional): ', ''); + $clientDescription = $this->helper->ask($input, $output, $question); - $grants = explode(',', $clientGrantsString); + $question = new ChoiceQuestion('Enter client grants', [ + 'authorization_code', + 'client_credentials', + 'refresh_token', + 'password', + ], 0); + $question->setMultiselect(true); + + $grants = $this->helper->ask($input, $output, $question); $command = new CreateClientCommand($clientName, $grants, $clientDescription); try { /** @var Client $client */ $client = CommandBus::handle($command); - $this->climate->green('OAuth client created successfully'); - $this->climate->info('Client ID: ' . $client->client_id); - $this->climate->info('Client Secret: ' . $client->client_secret)->br(2); + $output->green('OAuth client created successfully'); + $output->info('Client ID: ' . $client->client_id); + $output->info('Client Secret: ' . $client->client_secret)->br(2); } catch (CommandHandlerException $exception) { - $this->climate->error($exception->getMessage()); + $output->error($exception->getMessage()); - return 1; + return Command::FAILURE; } - return 0; + return Command::SUCCESS; } } diff --git a/src/Cli/Commands/OAuth/ListClients.php b/src/Cli/Commands/OAuth/ListClients.php new file mode 100644 index 0000000..fa8e9fc --- /dev/null +++ b/src/Cli/Commands/OAuth/ListClients.php @@ -0,0 +1,75 @@ +addArgument('client-id', null, 'Filter by client ID'); + } + + public function __invoke(ArgvInput|InputInterface $input, ClimateOutput|OutputInterface $output): int + { + if ($input->getArgument('client-id')) { + $client = Client::find($input->getArgument('client-id')); + + if (!$client) { + $output->red('No OAuth client found with the specified client ID.'); + + return self::SUCCESS; + } + + $output->json([ + 'ID' => $client->id, + 'Name' => $client->name, + 'Client ID' => $client->client_id, + 'Client Secret' => $client->client_secret, + 'Grant Types' => implode(', ', $client->grant_types->toArray()), + 'Access Token Url' => Config::get('app.url') . '/client/access_token', + 'OAuth Config Url' => Config::get('app.url') . + '/client/' . $client->id . '/.well-known/openid-configuration', + 'Scopes' => $client->scopes->toArray() + ]); + + return self::SUCCESS; + } + + $clients = Client::all('id', 'name', 'client_id', 'client_secret', 'grant_types'); + + if ($clients->isEmpty()) { + $output->yellow('No OAuth clients found.'); + + return self::SUCCESS; + } + + $outputArray = []; + + $clients->map(function (Client $client) use (&$outputArray, $input) { + $outputValues = [ + 'ID' => $client->id, + 'Name' => $client->name, + 'Client ID' => $client->client_id, + 'Grant Types' => implode(', ', $client->grant_types->toArray()), + ]; + + $outputArray[] = $outputValues; + }); + + $output->table($outputArray); + + return self::SUCCESS; + } +} diff --git a/src/Cli/Commands/Queue/Start.php b/src/Cli/Commands/Queue/Start.php index 76440c8..e1dd87a 100644 --- a/src/Cli/Commands/Queue/Start.php +++ b/src/Cli/Commands/Queue/Start.php @@ -4,29 +4,41 @@ declare(strict_types=1); namespace Siteworxpro\App\Cli\Commands\Queue; -use Ahc\Cli\Input\Command; use Siteworxpro\App\Async\Consumer; -use Siteworxpro\App\Async\Messages\SayHelloMessage; +use Siteworxpro\App\Cli\Commands\Command; use Siteworxpro\App\Cli\Commands\CommandInterface; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Input\InputArgument; +#[AsCommand('queue:start', 'Start the queue consumer')] class Start extends Command implements CommandInterface { - public function __construct() + protected function configure(): void { - parent::__construct('queue:start', 'Start the queue consumer to process messages.'); - $this->argument('[queues]', 'The name of the queue to consume from. ex. "first_queue,second_queue"'); + $this->addArgument( + 'queues', + InputArgument::OPTIONAL | InputArgument::IS_ARRAY, + 'Space separated list of queues to consume from. If omitted the default queue is used.', + ['default'] + ); } - public function execute(): int + public function __invoke($input, $output): int { - $queues = []; - if ($this->values()['queues'] !== null) { - $queues = explode(',', $this->values()['queues']); + $queues = $input->getArgument('queues'); + + if ($output->isVerbose()) { + $output->lightCyan('Consuming from queues: ' . implode(', ', $queues))->br(); } $consumer = new Consumer($queues); + + if ($output->isVerbose()) { + $output->lightCyan('Queue consumer started. Waiting for messages...')->br(); + } + $consumer->start(); - return 0; + return \Symfony\Component\Console\Command\Command::SUCCESS; } } diff --git a/src/Cli/Commands/User/Add.php b/src/Cli/Commands/User/Add.php index 8cfa50f..a54983d 100644 --- a/src/Cli/Commands/User/Add.php +++ b/src/Cli/Commands/User/Add.php @@ -8,60 +8,70 @@ use Siteworxpro\App\Cli\Commands\Command; use Siteworxpro\App\Models\ClientUser; use Siteworxpro\App\Models\User; use Siteworxpro\App\OAuth\Entities\Client; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command as SCommand; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Question\Question as QuestionInput; +#[AsCommand('user:add', 'Add a new user associated with an OAuth client')] class Add extends Command { - public function __construct() + public function __invoke($input, $output): int { - parent::__construct('user:add', 'Add a new user to the system'); - } + $clients = Client::whereJsonContains('grant_types', 'authorization_code') + ->get(['id', 'name']); - public function execute(): int - { - $clients = Client::all(['id', 'name']); - - $this->climate->info('Available OAuth Clients:'); + $output->info('Available OAuth Clients:'); foreach ($clients as $client) { - $this->climate->out("[$client->id] $client->name"); + $output->out("[$client->id] $client->name"); } - $input = $this->climate->input('Enter the client ID to associate the new user with: '); - $input->accept( - $clients->pluck('id')->toArray() - ); + $question = new QuestionInput('Enter the client ID to associate the new user with: '); + $question->setAutocompleterValues($clients->pluck('id')->toArray()); - $id = $input->prompt(); + /** @var QuestionHelper $helper */ + $helper = $this->getHelper('question'); + $id = $helper->ask($input, $output, $question); $client = Client::find($id); if (!$client) { - $this->climate->error('Client not found.'); - return 1; + $output->error('Client not found.'); + return SCommand::FAILURE; } - $this->climate->info("Adding a new user for client: $client->name"); + $output->info("Adding a new user for client: $client->name"); + $emailQuestion = new QuestionInput('Enter the user\'s email: '); + $emailQuestion->setValidator(function ($input): string { + if (empty($input) || !filter_var($input, FILTER_VALIDATE_EMAIL)) { + throw new \InvalidArgumentException('Invalid email address.'); + } - $input = $this->climate->input('Enter the user\'s email:'); - $email = $input->prompt(); + return $input; + }); + + $email = $helper->ask($input, $output, $emailQuestion); $user = User::where('email', $email)->first(); if ($user) { - $this->climate->error('A user with this email already exists. Associating the user with the client.'); + $output->yellow('A user with this email already exists. Associating the user with the client.'); $clientUser = new ClientUser(); $clientUser->client_id = $client->id; $clientUser->user_id = $user->id; $clientUser->save(); - return 0; + return SCommand::SUCCESS; } - $passwordInput = $this->climate->password('Enter the user\'s password: 🔐'); - $password = $passwordInput->prompt(); + $passwordQuestion = new QuestionInput('Enter the user\'s password: '); + $passwordQuestion->setHidden(true); + $passwordQuestion->setHiddenFallback(false); + $password = $helper->ask($input, $output, $passwordQuestion); - $firstNameInput = $this->climate->input('Enter the user\'s first name: '); - $firstName = $firstNameInput->prompt(); + $firstNameQuestion = new QuestionInput('Enter the user\'s first name: '); + $firstName = $helper->ask($input, $output, $firstNameQuestion); - $lastNameInput = $this->climate->input('Enter the user\'s last name: '); - $lastName = $lastNameInput->prompt(); + $lastNameQuestion = new QuestionInput('Enter the user\'s last name: '); + $lastName = $helper->ask($input, $output, $lastNameQuestion); $user = new User(); $user->email = $email; @@ -75,6 +85,8 @@ class Add extends Command $clientUser->user_id = $user->id; $clientUser->save(); - return 0; + $output->green('User added and associated with the client successfully.'); + + return SCommand::SUCCESS; } } diff --git a/src/Controllers/AccessTokenController.php b/src/Controllers/AccessTokenController.php index 880deab..fdab052 100644 --- a/src/Controllers/AccessTokenController.php +++ b/src/Controllers/AccessTokenController.php @@ -11,7 +11,6 @@ use Nyholm\Psr7\ServerRequest; use Psr\Http\Message\ResponseInterface; use Siteworxpro\App\Http\JsonResponseFactory; use Siteworxpro\App\Http\Responses\GenericResponse; -use Siteworxpro\App\OAuth\Entities\AuthorizationCode; use Siteworxpro\App\OAuth\Entities\Client; use Siteworxpro\HttpStatus\CodesEnum; @@ -23,13 +22,11 @@ final class AccessTokenController extends Controller * @throws BadFormatException * @throws EnvironmentIsBrokenException * @throws \JsonException - * @throws OAuthServerException */ public function post(ServerRequest $request): ResponseInterface { try { - $grantType = $request->getParsedBody()['grant_type'] ?? null; - $client = Client::find($request->getAttribute('client_id')); + $client = Client::byClientId($request->getParsedBody()['client_id'] ?? ''); if ($client === null) { return JsonResponseFactory::createJsonResponse( new GenericResponse('Invalid client'), @@ -37,30 +34,14 @@ final class AccessTokenController extends Controller ); } - switch ($grantType) { - case 'authorization_code': - return $client - ->getAuthorizationServer() - ->respondToAccessTokenRequest($request, JsonResponseFactory::createJsonResponse([])); - - case 'refresh_token': - break; - default: - return JsonResponseFactory::createJsonResponse( - new GenericResponse('Unsupported grant type'), - CodesEnum::BAD, - ); - } - - - $response = $this->authorizationServer->respondToAccessTokenRequest( - $request, - new Response(), - ); - - return $response; + return $client + ->getAuthorizationServer() + ->respondToAccessTokenRequest($request, JsonResponseFactory::createJsonResponse([])); } catch (OAuthServerException $e) { - return $e->generateHttpResponse(new Response()); + return JsonResponseFactory::createJsonResponse( + $e->getPayload(), + CodesEnum::fromCode($e->getHttpStatusCode()), + ); } } } diff --git a/src/Models/Model.php b/src/Models/Model.php index 1f38413..a7f444d 100644 --- a/src/Models/Model.php +++ b/src/Models/Model.php @@ -13,6 +13,7 @@ use Siteworxpro\App\Helpers\Ulid; * @package Siteworxpro\App\Models * @method static static|null find(string $id, array $columns = ['*']) * @method static where(string $column, string $operator = null, string $value = null, string $boolean = 'and') + * @method static whereJsonContains(string $column, mixed $value, string $boolean = 'and', bool $not = false) */ abstract class Model extends ORM { diff --git a/src/OAuth/Entities/AccessToken.php b/src/OAuth/Entities/AccessToken.php index c053de1..ac02f42 100644 --- a/src/OAuth/Entities/AccessToken.php +++ b/src/OAuth/Entities/AccessToken.php @@ -6,10 +6,12 @@ namespace Siteworxpro\App\OAuth\Entities; use Carbon\Carbon; use DateTimeImmutable; +use Lcobucci\JWT\Token; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\ClientEntityInterface; use League\OAuth2\Server\Entities\ScopeEntityInterface; use League\OAuth2\Server\Entities\Traits\AccessTokenTrait; +use Siteworxpro\App\Services\Facades\Config; class AccessToken extends RedisModel implements AccessTokenEntityInterface { @@ -27,6 +29,26 @@ class AccessToken extends RedisModel implements AccessTokenEntityInterface return $this->client; } + private function convertToJWT(): Token + { + $this->initJwtConfiguration(); + + $builder = $this->jwtConfiguration->builder() + ->permittedFor($this->getClient()->getIdentifier()) + ->identifiedBy($this->getIdentifier()) + ->issuedAt(new DateTimeImmutable()) + ->issuedBy(Config::get('app.url') . '/' . $this->getClient()->getIdentifier()) + ->canOnlyBeUsedAfter(new DateTimeImmutable()) + ->expiresAt($this->getExpiryDateTime()) + ->withClaim('scopes', $this->getScopes()); + + if ($this->getUserIdentifier() !== null) { + $builder = $builder->relatedTo($this->getSubjectIdentifier()); + } + + return $builder->getToken($this->jwtConfiguration->signer(), $this->jwtConfiguration->signingKey()); + } + public function getExpiryDateTime(): DateTimeImmutable { return $this->expiryDateTime->toDateTimeImmutable(); @@ -52,7 +74,7 @@ class AccessToken extends RedisModel implements AccessTokenEntityInterface $this->expiryDateTime = Carbon::instance($dateTime); } - public function setUserIdentifier(string $identifier): void + public function setUserIdentifier(?string $identifier): void { $this->userIdentifier = $identifier; }