Skip to content

Commit a39e4ba

Browse files
committed
Improve support for Client Credentials flow
1 parent 1bda67c commit a39e4ba

File tree

3 files changed

+51
-60
lines changed

3 files changed

+51
-60
lines changed

Classes/Authorization.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,14 @@ public function getScope(): string
153153
return $this->scope;
154154
}
155155

156+
/**
157+
* @param string $scope
158+
*/
159+
public function setScope(string $scope): void
160+
{
161+
$this->scope = $scope;
162+
}
163+
156164
/**
157165
* @return array
158166
*/

Classes/Controller/OAuthController.php

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
use Neos\Flow\Annotations\CompileStatic;
77
use Neos\Flow\Http\Uri;
88
use Neos\Flow\Mvc\Controller\ActionController;
9+
use Neos\Flow\Mvc\Exception\StopActionException;
10+
use Neos\Flow\Mvc\Exception\UnsupportedRequestTypeException;
911
use Neos\Flow\ObjectManagement\ObjectManagerInterface;
1012
use Neos\Flow\Reflection\ReflectionService;
1113

@@ -32,19 +34,21 @@ public function initializeObject(): void
3234
* @param Uri $returnToUri
3335
* @param string $serviceType
3436
* @param string $serviceName
35-
* @throws
36-
* FIXME: Re-Implement
37+
* @param string $scope
38+
* @throws OAuthClientException
39+
* @throws StopActionException
40+
* @throws UnsupportedRequestTypeException
3741
*/
38-
public function startAuthorizationAction(string $clientId, string $clientSecret, Uri $returnToUri, string $serviceType, string $serviceName): void
42+
public function startAuthorizationAction(string $clientId, string $clientSecret, Uri $returnToUri, string $serviceType, string $serviceName, string $scope): void
3943
{
40-
// if (!isset($this->serviceTypes[$serviceType])) {
41-
// throw new OAuthClientException(sprintf('Failed starting OAuth2 authorization, because the given service type "%s" is unknown.', $serviceType), 1511187873921);
42-
// }
43-
//
44-
// $client = new $this->serviceTypes[$serviceName]($serviceName);
45-
// assert($client instanceof OAuthClient);
46-
// $authorizeUri = $client->startAuthorization($clientId, $clientSecret, $returnToUri);
47-
// $this->redirectToUri($authorizeUri);
44+
if (!isset($this->serviceTypes[$serviceType])) {
45+
throw new OAuthClientException(sprintf('Failed starting OAuth2 authorization, because the given service type "%s" is unknown.', $serviceType), 1511187873921);
46+
}
47+
48+
$client = new $this->serviceTypes[$serviceName]($serviceName);
49+
assert($client instanceof OAuthClient);
50+
$authorizeUri = $client->startAuthorization($clientId, $clientSecret, $returnToUri, $scope);
51+
$this->redirectToUri($authorizeUri);
4852
}
4953

5054
/**
@@ -58,9 +62,12 @@ public function startAuthorizationAction(string $clientId, string $clientSecret,
5862
* @param string $serviceName The OAuth service name, ie. the identifier of the concrete configuration of the given OAuth service implementation
5963
* @param string $state The state by which the OAuth client can find the authorization in progress
6064
* @param string $code The code issued by the OAuth server
61-
* @throws
65+
* @param string $scope The scope issued by the OAuth server
66+
* @throws OAuthClientException
67+
* @throws StopActionException
68+
* @throws UnsupportedRequestTypeException
6269
*/
63-
public function finishAuthorizationAction(string $serviceType, string $serviceName, string $state, string $code): void
70+
public function finishAuthorizationAction(string $serviceType, string $serviceName, string $state, string $code, string $scope = ''): void
6471
{
6572
if (!isset($this->serviceTypes[$serviceType])) {
6673
throw new OAuthClientException(sprintf('OAuth: Failed finishing OAuth2 authorization because the given service type "%s" is unknown.', $serviceName), 1511193117184);
@@ -69,7 +76,7 @@ public function finishAuthorizationAction(string $serviceType, string $serviceNa
6976
if (!$client instanceof OAuthClient) {
7077
throw new OAuthClientException(sprintf('OAuth: Failed finishing authorization because of unexpected class type: "%s" must implement %s.', get_class($client), OAuthClient::class), 1568735389);
7178
}
72-
$this->redirectToUri($client->finishAuthorization($state, $code));
79+
$this->redirectToUri($client->finishAuthorization($state, $code, $scope));
7380
}
7481

7582
/**

Classes/OAuthClient.php

Lines changed: 22 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Doctrine\ORM\EntityManagerInterface;
66
use Doctrine\ORM\ORMException;
77
use GuzzleHttp\Client;
8+
use GuzzleHttp\Exception\GuzzleException;
89
use GuzzleHttp\Psr7\Response;
910
use GuzzleHttp\Psr7\Uri;
1011
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
@@ -16,7 +17,7 @@
1617
use Neos\Flow\Annotations as Flow;
1718
use Neos\Flow\Core\Bootstrap;
1819
use Neos\Flow\Http\HttpRequestHandlerInterface;
19-
use Neos\Flow\Log\Utility\LogEnvironment;
20+
use Neos\Flow\Http\Request;
2021
use Neos\Flow\Mvc\ActionRequest;
2122
use Neos\Flow\Mvc\Routing\Exception\MissingActionNameException;
2223
use Neos\Flow\Mvc\Routing\UriBuilder;
@@ -281,10 +282,11 @@ public function startAuthorization(string $clientId, string $clientSecret, UriIn
281282
*
282283
* @param string $stateIdentifier The state identifier, passed back by the OAuth server as the "state" parameter
283284
* @param string $code The authorization code given by the OAuth server
285+
* @param string $scope The scope granted by the OAuth server
284286
* @return UriInterface The URI to return to
285287
* @throws OAuthClientException
286288
*/
287-
public function finishAuthorization(string $stateIdentifier, string $code): UriInterface
289+
public function finishAuthorization(string $stateIdentifier, string $code, string $scope): UriInterface
288290
{
289291
$stateFromCache = $this->stateCache->get($stateIdentifier);
290292
if (empty($stateFromCache)) {
@@ -307,6 +309,7 @@ public function finishAuthorization(string $stateIdentifier, string $code): UriI
307309
$this->logger->info(sprintf('OAuth (%s): Persisting OAuth token for authorization "%s" with expiry time %s.', $this->getServiceType(), $authorizationId, $accessToken->getExpires()));
308310

309311
$authorization->setAccessToken($accessToken);
312+
$authorization->setScope($scope);
310313

311314
$this->entityManager->persist($authorization);
312315
$this->entityManager->flush();
@@ -368,87 +371,60 @@ public function getAuthorization(string $authorizationId): ?Authorization
368371
}
369372

370373
/**
371-
* Returns a prepared request which provides the needed header for OAuth authentication
374+
* Returns a prepared request to an OAuth 2.0 service provider using Bearer token authentication
372375
*
376+
* @param Authorization $authorization
373377
* @param string $relativeUri A relative URI of the web server, prepended by the base URI
374378
* @param string $method The HTTP method, for example "GET" or "POST"
375379
* @param array $bodyFields Associative array of body fields to send (optional)
376380
* @return RequestInterface
377-
* @throws IdentityProviderException
378381
* @throws OAuthClientException
379382
*/
380-
public function getAuthenticatedRequest(string $relativeUri, string $method = 'GET', array $bodyFields = []): RequestInterface
383+
public function getAuthenticatedRequest(Authorization $authorization, string $relativeUri, string $method = 'GET', array $bodyFields = []): RequestInterface
381384
{
382-
$oAuthToken = $this->getAuthorization();
383-
if (!$oAuthToken instanceof Authorization) {
384-
throw new OAuthClientException('No OAuthToken found.', 1505321014388);
385-
}
386-
387-
$oAuthProvider = $this->createOAuthProvider($oAuthToken->getClientId(), $oAuthToken->getClientSecret());
388-
389-
if ($oAuthToken->expires < new \DateTimeImmutable()) {
390-
switch ($oAuthToken->getGrantType()) {
391-
case Authorization::GRANT_AUTHORIZATION_CODE:
392-
$this->refreshAuthorization('fixme-authorization-id', $oAuthToken->getClientId(), 'fixme-return-to-uri');
393-
$oAuthToken = $this->getAuthorization();
394-
break;
395-
case Authorization::GRANT_CLIENT_CREDENTIALS:
396-
try {
397-
$newAccessToken = $oAuthProvider->getAccessToken(Authorization::GRANT_CLIENT_CREDENTIALS);
398-
} catch (IdentityProviderException $exception) {
399-
$this->logger->error(sprintf($this->getServiceType() . 'Failed retrieving new OAuth access token for client "%s" (client credentials grant): %s', $oAuthToken->clientId, $exception->getMessage()));
400-
throw $exception;
401-
}
402-
403-
$oAuthToken->accessToken = $newAccessToken->getToken();
404-
$oAuthToken->expires = ($newAccessToken->getExpires() ? \DateTimeImmutable::createFromFormat('U', $newAccessToken->getExpires()) : null);
405-
406-
$this->logger->info(sprintf('OAuth (%s): Persisted new OAuth token for client "%s" with expiry time %s.', $this->getServiceType(), $oAuthToken->clientId, $newAccessToken->getExpires()));
407-
408-
$this->entityManager->persist($oAuthToken);
409-
$this->entityManager->flush();
410-
break;
411-
}
385+
$accessToken = $authorization->getAccessToken();
386+
if ($accessToken === null) {
387+
throw new OAuthClientException(sprintf($this->getServiceType() . 'Failed getting an authenticated request for client ID "%s" because the authorization contained no access token', $authorization->getClientId()), 1589300319);
412388
}
413389

414-
$body = ($bodyFields !== [] ? \GuzzleHttp\json_encode($bodyFields) : '');
415-
390+
$oAuthProvider = $this->createOAuthProvider($authorization->getClientId(), $authorization->getClientSecret());
416391
return $oAuthProvider->getAuthenticatedRequest(
417392
$method,
418393
$this->getBaseUri() . $relativeUri,
419-
$oAuthToken->getAccessToken(),
394+
$authorization->getAccessToken(),
420395
[
421396
'headers' => [
422397
'Content-Type' => 'application/json'
423398
],
424-
'body' => $body
399+
'body' => ($bodyFields !== [] ? \GuzzleHttp\json_encode($bodyFields) : '')
425400
]
426401
);
427402
}
428403

429404
/**
405+
* Sends an HTTP request to an OAuth 2.0 service provider using Bearer token authentication
406+
*
407+
* @param Authorization $authorization
430408
* @param string $relativeUri
431409
* @param string $method
432410
* @param array $bodyFields
433411
* @return Response
412+
* @throws GuzzleException
413+
* @throws OAuthClientException
434414
*/
435-
public function sendAuthenticatedRequest(string $relativeUri, string $method = 'GET', array $bodyFields = []): Response
415+
public function sendAuthenticatedRequest(Authorization $authorization, string $relativeUri, string $method = 'GET', array $bodyFields = []): Response
436416
{
437417
if ($this->httpClient === null) {
438-
$this->httpClient = new Client();
418+
$this->httpClient = new Client(['allow_redirects' => false]);
439419
}
440-
// FIXME
441-
# return $this->httpClient->send($this->getAuthenticatedRequest($relativeUri, $method, $bodyFields));
420+
return $this->httpClient->send($this->getAuthenticatedRequest($authorization, $relativeUri, $method, $bodyFields));
442421
}
443422

444423
/**
445424
* @return string
446-
* @throws
447-
* FIXME
448425
*/
449426
public function renderFinishAuthorizationUri(): string
450427
{
451-
return '';
452428
$currentRequestHandler = $this->bootstrap->getActiveRequestHandler();
453429
if ($currentRequestHandler instanceof HttpRequestHandlerInterface) {
454430
$httpRequest = $currentRequestHandler->getHttpRequest();

0 commit comments

Comments
 (0)