diff --git a/README.md b/README.md index 81f947b8..e372db67 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ through a SimpleSAMLphp module installable through Composer. It is based on Currently supported flows are: * Authorization Code flow, with PKCE support (response_type 'code') * Implicit flow (response_type 'id_token token' or 'id_token') -* Plain OAuth2 Implicit flow (response_type 'token') * Refresh Token flow [![Build Status](https://github.com/simplesamlphp/simplesamlphp-module-oidc/actions/workflows/test.yaml/badge.svg)](https://github.com/simplesamlphp/simplesamlphp-module-oidc/actions/workflows/test.yaml) diff --git a/UPGRADE.md b/UPGRADE.md index 6904dea5..14ac992c 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -75,6 +75,8 @@ known 'issue': https://github.com/symfony/symfony/issues/19693). If you don't se about this situation in your logs. - The new authproc filter processing will look in an additional location for filters, in the main `config.php` under key `authproc.oidc` +- Removed support for plain OAuth2 Implicit flow (response_type `token`), because of very low usage. Note that the OIDC +Implicit flow is still supported (response_type `id_token token` or `id_token`). ## Low impact changes @@ -95,7 +97,7 @@ has been refactored: - Upgraded to v3 of laminas/laminas-diactoros https://github.com/laminas/laminas-diactoros - SimpleSAMLphp version used during development was bumped to v2.3 - In Authorization Code Flow, a new validation was added which checks for 'openid' value in 'scope' parameter. Up to -now, 'openid' value was dynamically added if not present. In Implicit Code Flow this validation was already present. +now, 'openid' value was dynamically added if not present. In Implicit Code Flow this validation was already present. - Removed importer from legacy OAuth2 module, as it is very unlikely that someone will upgrade from legacy OAuth2 module to v6 of oidc module. If needed, one can upgrade to earlier versions of oidc module, and then to v6. diff --git a/routing/services/services.yml b/routing/services/services.yml index f120f146..60f08be9 100644 --- a/routing/services/services.yml +++ b/routing/services/services.yml @@ -56,8 +56,6 @@ services: # Grants SimpleSAML\Module\oidc\Server\Grants\AuthCodeGrant: factory: ['@SimpleSAML\Module\oidc\Factories\Grant\AuthCodeGrantFactory', 'build'] - SimpleSAML\Module\oidc\Server\Grants\OAuth2ImplicitGrant: - factory: ['@SimpleSAML\Module\oidc\Factories\Grant\OAuth2ImplicitGrantFactory', 'build'] SimpleSAML\Module\oidc\Server\Grants\ImplicitGrant: factory: ['@SimpleSAML\Module\oidc\Factories\Grant\ImplicitGrantFactory', 'build'] SimpleSAML\Module\oidc\Server\Grants\RefreshTokenGrant: diff --git a/src/Factories/AuthorizationServerFactory.php b/src/Factories/AuthorizationServerFactory.php index c2fa0f7e..54cf5d38 100644 --- a/src/Factories/AuthorizationServerFactory.php +++ b/src/Factories/AuthorizationServerFactory.php @@ -24,7 +24,6 @@ use SimpleSAML\Module\oidc\Server\AuthorizationServer; use SimpleSAML\Module\oidc\Server\Grants\AuthCodeGrant; use SimpleSAML\Module\oidc\Server\Grants\ImplicitGrant; -use SimpleSAML\Module\oidc\Server\Grants\OAuth2ImplicitGrant; use SimpleSAML\Module\oidc\Server\Grants\RefreshTokenGrant; use SimpleSAML\Module\oidc\Server\RequestRules\RequestRulesManager; use SimpleSAML\Module\oidc\Server\ResponseTypes\IdTokenResponse; @@ -37,7 +36,6 @@ public function __construct( private readonly AccessTokenRepository $accessTokenRepository, private readonly ScopeRepository $scopeRepository, private readonly AuthCodeGrant $authCodeGrant, - private readonly OAuth2ImplicitGrant $oAuth2ImplicitGrant, private readonly ImplicitGrant $implicitGrant, private readonly RefreshTokenGrant $refreshTokenGrant, private readonly IdTokenResponse $idTokenResponse, @@ -63,11 +61,6 @@ public function build(): AuthorizationServer $this->moduleConfig->getAccessTokenDuration(), ); - $authorizationServer->enableGrantType( - $this->oAuth2ImplicitGrant, - $this->moduleConfig->getAccessTokenDuration(), - ); - $authorizationServer->enableGrantType( $this->implicitGrant, $this->moduleConfig->getAccessTokenDuration(), diff --git a/src/Factories/Grant/OAuth2ImplicitGrantFactory.php b/src/Factories/Grant/OAuth2ImplicitGrantFactory.php deleted file mode 100644 index 3beaad1c..00000000 --- a/src/Factories/Grant/OAuth2ImplicitGrantFactory.php +++ /dev/null @@ -1,34 +0,0 @@ -moduleConfig->getAccessTokenDuration(), '#', $this->requestRulesManager); - } -} diff --git a/src/Server/AuthorizationServer.php b/src/Server/AuthorizationServer.php index 4c444e70..65c83e98 100644 --- a/src/Server/AuthorizationServer.php +++ b/src/Server/AuthorizationServer.php @@ -16,7 +16,7 @@ use Psr\Http\Message\ServerRequestInterface; use SimpleSAML\Error\BadRequest; use SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException; -use SimpleSAML\Module\oidc\Server\Grants\Interfaces\AuthorizationValidatableWithCheckerResultBagInterface; +use SimpleSAML\Module\oidc\Server\Grants\Interfaces\AuthorizationValidatableWithRequestRules; use SimpleSAML\Module\oidc\Server\RequestRules\RequestRulesManager; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ClientIdRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\IdTokenHintRule; @@ -103,12 +103,12 @@ public function validateAuthorizationRequest(ServerRequestInterface $request): O foreach ($this->enabledGrantTypes as $grantType) { if ($grantType->canRespondToAuthorizationRequest($request)) { - if (! $grantType instanceof AuthorizationValidatableWithCheckerResultBagInterface) { + if (! $grantType instanceof AuthorizationValidatableWithRequestRules) { throw OidcServerException::serverError('grant type must be validatable with already validated ' . 'result bag'); } - return $grantType->validateAuthorizationRequestWithCheckerResultBag($request, $resultBag); + return $grantType->validateAuthorizationRequestWithRequestRules($request, $resultBag); } } diff --git a/src/Server/Grants/AuthCodeGrant.php b/src/Server/Grants/AuthCodeGrant.php index 5d73bcaf..e056081e 100644 --- a/src/Server/Grants/AuthCodeGrant.php +++ b/src/Server/Grants/AuthCodeGrant.php @@ -31,7 +31,7 @@ use SimpleSAML\Module\oidc\Repositories\Interfaces\AuthCodeRepositoryInterface; use SimpleSAML\Module\oidc\Repositories\Interfaces\RefreshTokenRepositoryInterface; use SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException; -use SimpleSAML\Module\oidc\Server\Grants\Interfaces\AuthorizationValidatableWithCheckerResultBagInterface; +use SimpleSAML\Module\oidc\Server\Grants\Interfaces\AuthorizationValidatableWithRequestRules; use SimpleSAML\Module\oidc\Server\Grants\Interfaces\OidcCapableGrantTypeInterface; use SimpleSAML\Module\oidc\Server\Grants\Interfaces\PkceEnabledGrantTypeInterface; use SimpleSAML\Module\oidc\Server\Grants\Traits\IssueAccessTokenTrait; @@ -72,7 +72,7 @@ class AuthCodeGrant extends OAuth2AuthCodeGrant implements // phpcs:ignore OidcCapableGrantTypeInterface, // phpcs:ignore - AuthorizationValidatableWithCheckerResultBagInterface + AuthorizationValidatableWithRequestRules { use IssueAccessTokenTrait; @@ -641,7 +641,7 @@ protected function validateAuthorizationCode( * @inheritDoc * @throws \Throwable */ - public function validateAuthorizationRequestWithCheckerResultBag( + public function validateAuthorizationRequestWithRequestRules( ServerRequestInterface $request, ResultBagInterface $resultBag, ): OAuth2AuthorizationRequest { diff --git a/src/Server/Grants/ImplicitGrant.php b/src/Server/Grants/ImplicitGrant.php index c4872748..4e2026bc 100644 --- a/src/Server/Grants/ImplicitGrant.php +++ b/src/Server/Grants/ImplicitGrant.php @@ -5,6 +5,7 @@ namespace SimpleSAML\Module\oidc\Server\Grants; use DateInterval; +use League\OAuth2\Server\Grant\ImplicitGrant as OAuth2ImplicitGrant; use League\OAuth2\Server\RequestTypes\AuthorizationRequest as OAuth2AuthorizationRequest; use League\OAuth2\Server\ResponseTypes\RedirectResponse; use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface; @@ -17,24 +18,32 @@ use SimpleSAML\Module\oidc\Factories\Entities\AccessTokenEntityFactory; use SimpleSAML\Module\oidc\Repositories\Interfaces\AccessTokenRepositoryInterface; use SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException; +use SimpleSAML\Module\oidc\Server\Grants\Interfaces\AuthorizationValidatableWithRequestRules; use SimpleSAML\Module\oidc\Server\Grants\Traits\IssueAccessTokenTrait; use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface; use SimpleSAML\Module\oidc\Server\RequestRules\RequestRulesManager; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\AcrValuesRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\AddClaimsToIdTokenRule; +use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ClientIdRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\MaxAgeRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\PromptRule; +use SimpleSAML\Module\oidc\Server\RequestRules\Rules\RedirectUriRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\RequestedClaimsRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\RequestObjectRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\RequiredNonceRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\RequiredOpenIdScopeRule; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ResponseTypeRule; +use SimpleSAML\Module\oidc\Server\RequestRules\Rules\ScopeRule; +use SimpleSAML\Module\oidc\Server\RequestRules\Rules\StateRule; use SimpleSAML\Module\oidc\Server\RequestTypes\AuthorizationRequest; use SimpleSAML\Module\oidc\Services\IdTokenBuilder; use SimpleSAML\Module\oidc\Utils\RequestParamsResolver; use SimpleSAML\OpenID\Codebooks\HttpMethodsEnum; -class ImplicitGrant extends OAuth2ImplicitGrant +/** + * @psalm-suppress PropertyNotSetInConstructor + */ +class ImplicitGrant extends OAuth2ImplicitGrant implements AuthorizationValidatableWithRequestRules { use IssueAccessTokenTrait; @@ -49,14 +58,15 @@ class ImplicitGrant extends OAuth2ImplicitGrant public function __construct( protected IdTokenBuilder $idTokenBuilder, - DateInterval $accessTokenTTL, + protected DateInterval $accessTokenTTL, AccessTokenRepositoryInterface $accessTokenRepository, - RequestRulesManager $requestRulesManager, + protected RequestRulesManager $requestRulesManager, protected RequestParamsResolver $requestParamsResolver, - string $queryDelimiter, + protected string $queryDelimiter, AccessTokenEntityFactory $accessTokenEntityFactory, ) { - parent::__construct($accessTokenTTL, $queryDelimiter, $requestRulesManager); + parent::__construct($accessTokenTTL, $queryDelimiter); + $this->accessTokenRepository = $accessTokenRepository; $this->accessTokenEntityFactory = $accessTokenEntityFactory; } @@ -108,14 +118,12 @@ public function completeAuthorizationRequest( * @throws \SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException * @throws \Throwable */ - public function validateAuthorizationRequestWithCheckerResultBag( + public function validateAuthorizationRequestWithRequestRules( ServerRequestInterface $request, ResultBagInterface $resultBag, ): OAuth2AuthorizationRequest { - $oAuth2AuthorizationRequest = - parent::validateAuthorizationRequestWithCheckerResultBag($request, $resultBag); - $rulesToExecute = [ + ScopeRule::class, RequestObjectRule::class, PromptRule::class, MaxAgeRule::class, @@ -129,6 +137,17 @@ public function validateAuthorizationRequestWithCheckerResultBag( $this->requestRulesManager->predefineResultBag($resultBag); + /** @var string $redirectUri */ + $redirectUri = $resultBag->getOrFail(RedirectUriRule::class)->getValue(); + /** @var string|null $state */ + $state = $resultBag->getOrFail(StateRule::class)->getValue(); + /** @var \SimpleSAML\Module\oidc\Entities\Interfaces\ClientEntityInterface $client */ + $client = $resultBag->getOrFail(ClientIdRule::class)->getValue(); + + // Some rules need certain things available in order to work properly... + $this->requestRulesManager->setData('default_scope', $this->defaultScope); + $this->requestRulesManager->setData('scope_delimiter_string', self::SCOPE_DELIMITER_STRING); + $resultBag = $this->requestRulesManager->check( $request, $rulesToExecute, @@ -136,7 +155,17 @@ public function validateAuthorizationRequestWithCheckerResultBag( $this->allowedAuthorizationHttpMethods, ); - $authorizationRequest = AuthorizationRequest::fromOAuth2AuthorizationRequest($oAuth2AuthorizationRequest); + /** @var \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes */ + $scopes = $resultBag->getOrFail(ScopeRule::class)->getValue(); + + $authorizationRequest = new AuthorizationRequest(); + $authorizationRequest->setClient($client); + $authorizationRequest->setRedirectUri($redirectUri); + $authorizationRequest->setScopes($scopes); + $authorizationRequest->setGrantTypeId($this->getIdentifier()); + if ($state !== null) { + $authorizationRequest->setState($state); + } // nonce existence is validated using a rule, so we can get it from there. $authorizationRequest->setNonce((string)$resultBag->getOrFail(RequiredNonceRule::class)->getValue()); diff --git a/src/Server/Grants/Interfaces/AuthorizationValidatableWithCheckerResultBagInterface.php b/src/Server/Grants/Interfaces/AuthorizationValidatableWithRequestRules.php similarity index 84% rename from src/Server/Grants/Interfaces/AuthorizationValidatableWithCheckerResultBagInterface.php rename to src/Server/Grants/Interfaces/AuthorizationValidatableWithRequestRules.php index 3a2af68b..c0e1d49b 100644 --- a/src/Server/Grants/Interfaces/AuthorizationValidatableWithCheckerResultBagInterface.php +++ b/src/Server/Grants/Interfaces/AuthorizationValidatableWithRequestRules.php @@ -8,14 +8,14 @@ use Psr\Http\Message\ServerRequestInterface; use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface; -interface AuthorizationValidatableWithCheckerResultBagInterface +interface AuthorizationValidatableWithRequestRules { /** * Validate authorization request using an existing ResultBag instance (with already validated checkers). * This is to evade usage of original validateAuthorizationRequest() method in which it is expected to * validate client and redirect_uri (which was already validated). */ - public function validateAuthorizationRequestWithCheckerResultBag( + public function validateAuthorizationRequestWithRequestRules( ServerRequestInterface $request, ResultBagInterface $resultBag, ): OAuth2AuthorizationRequest; diff --git a/src/Server/Grants/OAuth2ImplicitGrant.php b/src/Server/Grants/OAuth2ImplicitGrant.php deleted file mode 100644 index 5914dccc..00000000 --- a/src/Server/Grants/OAuth2ImplicitGrant.php +++ /dev/null @@ -1,131 +0,0 @@ -accessTokenTTL = $accessTokenTTL; - $this->queryDelimiter = $queryDelimiter; - $this->requestRulesManager = $requestRulesManager; - } - - /** - * @throws \SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException - * @throws \Throwable - */ - public function validateAuthorizationRequestWithCheckerResultBag( - ServerRequestInterface $request, - ResultBagInterface $resultBag, - ): OAuth2AuthorizationRequest { - $rulesToExecute = [ - ScopeRule::class, - ]; - - // Since we have already validated redirect_uri, and we have state, make it available for other checkers. - $this->requestRulesManager->predefineResultBag($resultBag); - - /** @var string $redirectUri */ - $redirectUri = $resultBag->getOrFail(RedirectUriRule::class)->getValue(); - /** @var string|null $state */ - $state = $resultBag->getOrFail(StateRule::class)->getValue(); - /** @var \SimpleSAML\Module\oidc\Entities\Interfaces\ClientEntityInterface $client */ - $client = $resultBag->getOrFail(ClientIdRule::class)->getValue(); - - // Some rules have to have certain things available in order to work properly... - $this->requestRulesManager->setData('default_scope', $this->defaultScope); - $this->requestRulesManager->setData('scope_delimiter_string', self::SCOPE_DELIMITER_STRING); - - $resultBag = $this->requestRulesManager->check( - $request, - $rulesToExecute, - false, - [HttpMethodsEnum::GET, HttpMethodsEnum::POST], - ); - - /** @var \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes */ - $scopes = $resultBag->getOrFail(ScopeRule::class)->getValue(); - - $oAuth2AuthorizationRequest = new OAuth2AuthorizationRequest(); - - $oAuth2AuthorizationRequest->setClient($client); - $oAuth2AuthorizationRequest->setRedirectUri($redirectUri); - $oAuth2AuthorizationRequest->setScopes($scopes); - $oAuth2AuthorizationRequest->setGrantTypeId($this->getIdentifier()); - - if ($state !== null) { - $oAuth2AuthorizationRequest->setState($state); - } - - return $oAuth2AuthorizationRequest; - } -} diff --git a/src/Services/Container.php b/src/Services/Container.php index 0e3bf969..78f57483 100644 --- a/src/Services/Container.php +++ b/src/Services/Container.php @@ -50,7 +50,6 @@ use SimpleSAML\Module\oidc\Factories\FormFactory; use SimpleSAML\Module\oidc\Factories\Grant\AuthCodeGrantFactory; use SimpleSAML\Module\oidc\Factories\Grant\ImplicitGrantFactory; -use SimpleSAML\Module\oidc\Factories\Grant\OAuth2ImplicitGrantFactory; use SimpleSAML\Module\oidc\Factories\Grant\RefreshTokenGrantFactory; use SimpleSAML\Module\oidc\Factories\IdTokenResponseFactory; use SimpleSAML\Module\oidc\Factories\JwksFactory; @@ -71,7 +70,6 @@ use SimpleSAML\Module\oidc\Server\AuthorizationServer; use SimpleSAML\Module\oidc\Server\Grants\AuthCodeGrant; use SimpleSAML\Module\oidc\Server\Grants\ImplicitGrant; -use SimpleSAML\Module\oidc\Server\Grants\OAuth2ImplicitGrant; use SimpleSAML\Module\oidc\Server\Grants\RefreshTokenGrant; use SimpleSAML\Module\oidc\Server\RequestRules\RequestRulesManager; use SimpleSAML\Module\oidc\Server\RequestRules\Rules\AcrValuesRule; @@ -436,9 +434,6 @@ public function __construct() ); $this->services[AuthCodeGrant::class] = $authCodeGrantFactory->build(); - $oAuth2ImplicitGrantFactory = new OAuth2ImplicitGrantFactory($moduleConfig, $requestRuleManager); - $this->services[OAuth2ImplicitGrant::class] = $oAuth2ImplicitGrantFactory->build(); - $implicitGrantFactory = new ImplicitGrantFactory( $moduleConfig, $this->services[IdTokenBuilder::class], @@ -463,7 +458,6 @@ public function __construct() $accessTokenRepository, $scopeRepository, $this->services[AuthCodeGrant::class], - $this->services[OAuth2ImplicitGrant::class], $this->services[ImplicitGrant::class], $this->services[RefreshTokenGrant::class], $this->services[IdTokenResponse::class], diff --git a/tests/unit/src/Server/Grants/ImplicitGrantTest.php b/tests/unit/src/Server/Grants/ImplicitGrantTest.php index 6c7045d4..d28dabda 100644 --- a/tests/unit/src/Server/Grants/ImplicitGrantTest.php +++ b/tests/unit/src/Server/Grants/ImplicitGrantTest.php @@ -4,15 +4,171 @@ namespace SimpleSAML\Test\Module\oidc\unit\Server\Grants; +use League\OAuth2\Server\Entities\ScopeEntityInterface; +use League\OAuth2\Server\Repositories\ScopeRepositoryInterface; +use League\OAuth2\Server\ResponseTypes\RedirectResponse; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Psr\Http\Message\ServerRequestInterface; +use SimpleSAML\Module\oidc\Entities\ClientEntity; +use SimpleSAML\Module\oidc\Entities\UserEntity; +use SimpleSAML\Module\oidc\Factories\Entities\AccessTokenEntityFactory; +use SimpleSAML\Module\oidc\Repositories\AccessTokenRepository; +use SimpleSAML\Module\oidc\Repositories\Interfaces\AccessTokenRepositoryInterface; +use SimpleSAML\Module\oidc\Server\Grants\ImplicitGrant; +use SimpleSAML\Module\oidc\Server\RequestRules\Interfaces\ResultBagInterface; +use SimpleSAML\Module\oidc\Server\RequestRules\RequestRulesManager; +use SimpleSAML\Module\oidc\Server\RequestTypes\AuthorizationRequest; +use SimpleSAML\Module\oidc\Services\IdTokenBuilder; +use SimpleSAML\Module\oidc\Utils\RequestParamsResolver; -/** - * @covers \SimpleSAML\Module\oidc\Server\Grants\ImplicitGrant - */ +#[CoversClass(ImplicitGrant::class)] class ImplicitGrantTest extends TestCase { - public function testIncomplete(): never + protected MockObject $idTokenBuilderMock; + protected \DateInterval $accessTokenTtl1h; + protected MockObject $accessTokenRepositoryMock; + protected MockObject $requestRulesManagerMock; + protected MockObject $requestParamsResolverMock; + protected string $queryDelimiter; + protected MockObject $accessTokenEntityFactoryMock; + protected MockObject $scopeRepositoryMock; + protected MockObject $serverRequestMock; + protected MockObject $authorizationRequestMock; + protected MockObject $userEntityMock; + protected MockObject $scopeEntityMock; + protected MockObject $clientEntityMock; + protected MockObject $resultBagMock; + + protected function setUp(): void + { + $this->idTokenBuilderMock = $this->createMock(IdTokenBuilder::class); + $this->accessTokenTtl1h = new \DateInterval('PT1H'); + $this->accessTokenRepositoryMock = $this->createMock(AccessTokenRepository::class); + $this->requestRulesManagerMock = $this->createMock(RequestRulesManager::class); + $this->requestParamsResolverMock = $this->createMock(RequestParamsResolver::class); + $this->queryDelimiter = '#'; + $this->accessTokenEntityFactoryMock = $this->createMock(AccessTokenEntityFactory::class); + $this->scopeRepositoryMock = $this->createMock(ScopeRepositoryInterface::class); + + $this->serverRequestMock = $this->createMock(ServerRequestInterface::class); + $this->authorizationRequestMock = $this->createMock(AuthorizationRequest::class); + $this->userEntityMock = $this->createMock(UserEntity::class); + $this->scopeEntityMock = $this->createMock(ScopeEntityInterface::class); + $this->clientEntityMock = $this->createMock(ClientEntity::class); + $this->resultBagMock = $this->createMock(ResultBagInterface::class); + } + + protected function sut( + ?IdTokenBuilder $idTokenBuilder = null, + ?\DateInterval $accessTokenTtl = null, + ?AccessTokenRepositoryInterface $accessTokenRepository = null, + ?RequestRulesManager $requestRulesManager = null, + ?RequestParamsResolver $requestParamsResolver = null, + ?string $queryDelimiter = null, + ?AccessTokenEntityFactory $accessTokenEntityFactory = null, + ?ScopeRepositoryInterface $scopeRepository = null, + ): ImplicitGrant { + $idTokenBuilder ??= $this->idTokenBuilderMock; + $accessTokenTtl ??= $this->accessTokenTtl1h; + $accessTokenRepository ??= $this->accessTokenRepositoryMock; + $requestRulesManager ??= $this->requestRulesManagerMock; + $requestParamsResolver ??= $this->requestParamsResolverMock; + $queryDelimiter ??= $this->queryDelimiter; + $accessTokenEntityFactory ??= $this->accessTokenEntityFactoryMock; + $scopeRepository ??= $this->scopeRepositoryMock; + + + $implicitGrant = new ImplicitGrant( + $idTokenBuilder, + $accessTokenTtl, + $accessTokenRepository, + $requestRulesManager, + $requestParamsResolver, + $queryDelimiter, + $accessTokenEntityFactory, + ); + + $implicitGrant->setScopeRepository($scopeRepository); + + return $implicitGrant; + } + + public function testCanConstruct(): void + { + $this->assertInstanceOf(ImplicitGrant::class, $this->sut()); + } + + public function testCanRespondToAuthorizationRequestForIdTokenTokenResponseType(): void + { + $this->requestParamsResolverMock->expects($this->once()) + ->method('getAllBasedOnAllowedMethods') + ->willReturn(['client_id' => 'clientId', 'response_type' => 'id_token token']); + + $this->assertTrue($this->sut()->canRespondToAuthorizationRequest($this->serverRequestMock)); + } + + public function testCanRespondToAuthorizationRequestForIdTokenResponseType(): void + { + $this->requestParamsResolverMock->expects($this->once()) + ->method('getAllBasedOnAllowedMethods') + ->willReturn(['client_id' => 'clientId', 'response_type' => 'id_token']); + + $this->assertTrue($this->sut()->canRespondToAuthorizationRequest($this->serverRequestMock)); + } + + public function testCanRespondToAuthorizationRequestReturnsFalseIfNoClientId(): void + { + $this->requestParamsResolverMock->expects($this->once()) + ->method('getAllBasedOnAllowedMethods') + ->willReturn(['response_type' => 'id_token']); + + $this->assertFalse($this->sut()->canRespondToAuthorizationRequest($this->serverRequestMock)); + } + + public function testCanRespondToAuthorizationRequestReturnsFalseForHybridFlow(): void + { + $this->requestParamsResolverMock->expects($this->once()) + ->method('getAllBasedOnAllowedMethods') + ->willReturn(['response_type' => 'code id_token']); + + $this->assertFalse($this->sut()->canRespondToAuthorizationRequest($this->serverRequestMock)); + } + + public function testCompleteAuthorizationRequestThrowsForNonOidcRequests(): void + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Unexpected'); + + $this->sut()->completeAuthorizationRequest($this->createMock( + \League\OAuth2\Server\RequestTypes\AuthorizationRequest::class, + )); + } + + public function testCanCompleteAuthorizationRequest(): void + { + $this->authorizationRequestMock->expects($this->once())->method('getUser') + ->willReturn($this->userEntityMock); + $this->authorizationRequestMock->expects($this->once())->method('getRedirectUri') + ->willReturn('redirectUri'); + $this->authorizationRequestMock->expects($this->once())->method('isAuthorizationApproved') + ->willReturn(true); + $this->authorizationRequestMock->expects($this->once())->method('getScopes') + ->willReturn([$this->scopeEntityMock]); + $this->authorizationRequestMock->method('getClient') + ->willReturn($this->clientEntityMock); + $this->scopeRepositoryMock->expects($this->once())->method('finalizeScopes') + ->willReturn([$this->scopeEntityMock]); + + $this->assertInstanceOf( + RedirectResponse::class, + $this->sut()->completeAuthorizationRequest($this->authorizationRequestMock), + ); + } + + public function testCanValidateAuthorizationRequestWithRequestRules(): void { - $this->markTestIncomplete(); + $this->markTestIncomplete('RequestRulesManager needs to be refactored so it can be strongly typed.'); } } diff --git a/tests/unit/src/Server/Grants/OAuth2ImplicitGrantTest.php b/tests/unit/src/Server/Grants/OAuth2ImplicitGrantTest.php deleted file mode 100644 index 50732b35..00000000 --- a/tests/unit/src/Server/Grants/OAuth2ImplicitGrantTest.php +++ /dev/null @@ -1,18 +0,0 @@ -markTestIncomplete(); - } -}