From 52393814d931672987fad00950fa08aff32de54e Mon Sep 17 00:00:00 2001 From: Ron Rise Date: Wed, 19 Nov 2025 14:25:08 -0500 Subject: [PATCH] fix: enhance scope handling by adding RequireAllScopes attribute and updating Scope usage --- src/Attributes/Guards/RequireAllScopes.php | 12 ++++++++++++ src/Attributes/Guards/Scope.php | 7 ++++++- src/Controllers/IndexController.php | 3 ++- src/Http/Middleware/ScopeMiddleware.php | 11 ++++++++++- 4 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 src/Attributes/Guards/RequireAllScopes.php diff --git a/src/Attributes/Guards/RequireAllScopes.php b/src/Attributes/Guards/RequireAllScopes.php new file mode 100644 index 0000000..95bd0c8 --- /dev/null +++ b/src/Attributes/Guards/RequireAllScopes.php @@ -0,0 +1,12 @@ + $scopes the required scopes + * @param string $claim the claim to check for scopes + * @param string $separator the separator used to split scopes in the claim + */ public function __construct( private array $scopes = [], private string $claim = 'scope', - private string $separator = ' ', + private string $separator = ' ' ) { } diff --git a/src/Controllers/IndexController.php b/src/Controllers/IndexController.php index c0a8e57..1547df5 100644 --- a/src/Controllers/IndexController.php +++ b/src/Controllers/IndexController.php @@ -22,7 +22,8 @@ class IndexController extends Controller * @throws \JsonException */ #[Guards\Jwt] - #[Guards\Scope(['get.index'])] + #[Guards\Scope(['get.index', 'status.check'])] + #[Guards\RequireAllScopes] public function get(ServerRequest $request): ResponseInterface { return JsonResponseFactory::createJsonResponse(['status_code' => 200, 'message' => 'Server is running']); diff --git a/src/Http/Middleware/ScopeMiddleware.php b/src/Http/Middleware/ScopeMiddleware.php index f20dcd8..334afde 100644 --- a/src/Http/Middleware/ScopeMiddleware.php +++ b/src/Http/Middleware/ScopeMiddleware.php @@ -8,6 +8,7 @@ use League\Route\Dispatcher; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; +use Siteworxpro\App\Attributes\Guards\RequireAllScopes; use Siteworxpro\App\Attributes\Guards\Scope; use Siteworxpro\App\Controllers\Controller; use Siteworxpro\App\Http\JsonResponseFactory; @@ -63,6 +64,7 @@ class ScopeMiddleware extends Middleware // Fetch all Scope attributes declared on the method. $attributes = $reflectionMethod->getAttributes(Scope::class); + $requireAllAttributes = $reflectionMethod->getAttributes(RequireAllScopes::class); if (empty($attributes)) { // No scope attributes; delegate to the next handler. @@ -71,12 +73,16 @@ class ScopeMiddleware extends Middleware $requiredScopes = []; $userScopes = []; + $requireAll = false; foreach ($attributes as $attribute) { /** @var Scope $scopeInstance Concrete Scope attribute instance. */ $scopeInstance = $attribute->newInstance(); $requiredScopes = array_merge($requiredScopes, $scopeInstance->getScopes()); + // If any attribute requires all scopes, set the flag. + $requireAll = $requireAll || !empty($requireAllAttributes); + $scopes = $request->getAttribute($scopeInstance->getClaim()); if (!is_array($scopes)) { // If user scopes are not an array, treat as no scopes provided. @@ -92,7 +98,10 @@ class ScopeMiddleware extends Middleware $userScopes = array_unique($userScopes); // Deny if any required scope is missing from the user's scopes. - if (array_intersect($userScopes, $requiredScopes) === []) { + if ( + (!$requireAll && array_intersect($userScopes, $requiredScopes) === []) || + ($requireAll && array_diff($requiredScopes, $userScopes) !== []) + ) { return JsonResponseFactory::createJsonResponse([ 'error' => 'insufficient_scope', 'error_description' =>