diff --git a/CHANGELOG.md b/CHANGELOG.md index 73246e85..71ce9e29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # OAuth 2.0 Client Changelog +## x.x.x + +* Send scopes with access token request [#1029](https://github.com/thephpleague/oauth2-client/issues/1029) + ## 2.7.0 _Released: 2023-04-16_ diff --git a/composer.json b/composer.json index aa006dff..085fe514 100644 --- a/composer.json +++ b/composer.json @@ -6,6 +6,7 @@ "sort-packages": true }, "require": { + "ext-json": "*", "php": "^5.6 || ^7.0 || ^8.0", "guzzlehttp/guzzle": "^6.0 || ^7.0", "paragonie/random_compat": "^1 || ^2 || ^9.99" diff --git a/src/Provider/AbstractProvider.php b/src/Provider/AbstractProvider.php index 293a54d6..91caa58f 100644 --- a/src/Provider/AbstractProvider.php +++ b/src/Provider/AbstractProvider.php @@ -620,6 +620,15 @@ public function getAccessToken($grant, array $options = []) { $grant = $this->verifyGrant($grant); + if (empty($options['scope'])) { + $options['scope'] = $this->getDefaultScopes(); + } + + if (is_array($options['scope'])) { + $separator = $this->getScopeSeparator(); + $options['scope'] = implode($separator, $options['scope']); + } + $params = [ 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, @@ -757,7 +766,7 @@ protected function parseJson($content) */ protected function getContentType(ResponseInterface $response) { - return join(';', (array) $response->getHeader('content-type')); + return implode(';', $response->getHeader('content-type')); } /** diff --git a/test/src/Grant/GrantTestCase.php b/test/src/Grant/GrantTestCase.php index 5451b331..125c66ce 100644 --- a/test/src/Grant/GrantTestCase.php +++ b/test/src/Grant/GrantTestCase.php @@ -65,7 +65,7 @@ public function testGetAccessToken($grant, array $params = []) ->shouldReceive('getHeader') ->once() ->with('content-type') - ->andReturn('application/json'); + ->andReturn(['application/json']); /** @var ClientInterface & MockInterface $client */ $client = Mockery::spy(ClientInterface::class)->makePartial(); diff --git a/test/src/Grant/PasswordTest.php b/test/src/Grant/PasswordTest.php index f177eb86..85972fc0 100644 --- a/test/src/Grant/PasswordTest.php +++ b/test/src/Grant/PasswordTest.php @@ -20,7 +20,8 @@ protected function getParamExpectation() return !empty($body['grant_type']) && $body['grant_type'] === 'password' && !empty($body['username']) - && !empty($body['password']); + && !empty($body['password']) + && !empty($body['scope']); }; } diff --git a/test/src/Provider/AbstractProviderTest.php b/test/src/Provider/AbstractProviderTest.php index b9ebf6f1..6f388a04 100644 --- a/test/src/Provider/AbstractProviderTest.php +++ b/test/src/Provider/AbstractProviderTest.php @@ -50,7 +50,7 @@ public function testInvalidGrantString() public function testInvalidGrantObject() { $this->expectException(InvalidGrantException::class); - $grant = new \StdClass(); + $grant = new \stdClass(); $this->getMockProvider()->getAccessToken($grant, ['invalid_parameter' => 'none']); } @@ -213,7 +213,7 @@ public function testGetUserProperties($name = null, $email = null, $id = null) ->shouldReceive('getHeader') ->once() ->with('content-type') - ->andReturn('application/json'); + ->andReturn(['application/json']); $client = Mockery::spy(ClientInterface::class, [ @@ -262,7 +262,7 @@ public function testGetUserPropertiesThrowsExceptionWhenNonJsonResponseIsReceive $response ->shouldReceive('getHeader') ->with('content-type') - ->andReturn('text/html'); + ->andReturn(['text/html']); $client = Mockery::mock(ClientInterface::class, [ 'send' => $response, @@ -378,7 +378,7 @@ public function testPkceMethod($pkceMethod, $pkceCode, $expectedChallenge) ->shouldReceive('getHeader') ->once() ->with('content-type') - ->andReturn('application/json'); + ->andReturn(['application/json']); $client = Mockery::spy(ClientInterface::class, [ 'send' => $response, @@ -474,7 +474,7 @@ public function testErrorResponsesCanBeCustomizedAtTheProvider() ->shouldReceive('getHeader') ->once() ->with('content-type') - ->andReturn('application/json'); + ->andReturn(['application/json']); $client = Mockery::spy(ClientInterface::class, [ 'send' => $response, @@ -532,7 +532,7 @@ public function testClientErrorTriggersProviderException() $response ->shouldReceive('getHeader') ->with('content-type') - ->andReturn('application/json'); + ->andReturn(['application/json']); $client = Mockery::mock(ClientInterface::class); $client @@ -579,7 +579,7 @@ public function testAuthenticatedRequestAndResponse() $response ->shouldReceive('getHeader') ->with('content-type') - ->andReturn('application/json'); + ->andReturn(['application/json']); $client = Mockery::mock(ClientInterface::class); $client @@ -628,7 +628,7 @@ public function testGetAccessToken($method) ->once() ->with( ['client_id' => 'mock_client_id', 'client_secret' => 'mock_secret', 'redirect_uri' => 'none'], - ['code' => 'mock_authorization_code'] + ['code' => 'mock_authorization_code', 'scope' => 'test'] ) ->andReturn([]); @@ -647,7 +647,7 @@ public function testGetAccessToken($method) ->shouldReceive('getHeader') ->once() ->with('content-type') - ->andReturn('application/json'); + ->andReturn(['application/json']); $client = Mockery::spy(ClientInterface::class, [ 'send' => $response, @@ -686,7 +686,7 @@ public function testGetAccessTokenWithNonJsonResponse() $response ->shouldReceive('getHeader') ->with('content-type') - ->andReturn('text/plain'); + ->andReturn(['text/plain']); $client = Mockery::mock(ClientInterface::class, [ 'send' => $response, @@ -745,7 +745,7 @@ public function testParseResponse($body, $type, $parsed, $statusCode = 200) $response ->shouldReceive('getHeader') ->with('content-type') - ->andReturn($type); + ->andReturn([$type]); $method = $this->getMethod(AbstractProvider::class, 'parseResponse'); $result = $method->invoke($this->getMockProvider(), $response);