reader = new NodeReader([ 'prefixes' => [ 'oauth2' => 'coid://oauth2.co-n.net/' ] ]); try { Assert::true($this->reader->hasProperty($authServer, 'oauth2:hasTokenEndpoint'), "Authorization Server must have a token endpoint."); Assert::startsWith($this->reader->getFirstValueString($authServer, 'oauth2:hasTokenEndpoint'), "https://", "Token endpoint must be an https:// URL."); Assert::true($this->reader->hasProperty($authServer, 'oauth2:supportsGrantType'), "Authorization Server must support at least one grant type."); Assert::true($this->reader->hasProperty($authServer, 'oauth2:usesClientIDFrom'), "Authorization Server must define client ID property."); Assert::true($this->reader->hasProperty($authServer, 'oauth2:usesClientSecretFrom'), "Authorization Server must define client secret property."); } catch (InvalidArgumentException $e) { throw new InvalidObjectConfigurationException($e->getMessage()); } $this->authServer = $authServer; $this->cacheAndLog = $cacheAndLog; } public function configureConsumer(Node $consumer) : void { try { Assert::notNull($this->authServer, "Object wasn't initialized correctly."); Assert::notNull($this->cacheAndLog, "Object wasn't initialized correctly."); $clientIDProperty = $this->reader->getFirstValueString($this->authServer, 'oauth2:usesClientIDFrom'); $clientSecretProperty = $this->reader->getFirstValueString($this->authServer, 'oauth2:usesClientSecretFrom'); Assert::true($this->reader->hasProperty($consumer, $clientIDProperty), "Namespace must have Client ID"); Assert::true($this->reader->hasProperty($consumer, $clientSecretProperty), "Namespace must have Client Secret"); } catch (InvalidArgumentException $e) { throw new InvalidObjectConfigurationException($e->getMessage()); } if ($this->reader->hasPropertyValue($this->authServer, 'oauth2:supportsGrantType', 'oauth2:ClientCredentials')) { // No additional conditions for "client_credentials" flow $this->grantType = 'client_credentials'; } else { throw new InvalidObjectConfigurationException("No flow/grant_type found."); } $this->consumer = $consumer; $this->clientId = $this->reader->getFirstValueString($consumer, $clientIDProperty); $this->clientSecret = $this->reader->getFirstValueString($consumer, $clientSecretProperty); } public function getAccessToken() { try { Assert::notNull($this->authServer, "Object wasn't initialized correctly."); Assert::notNull($this->cacheAndLog, "Object wasn't initialized correctly."); Assert::notNull($this->consumer, "Missing consumer."); Assert::notNull($this->grantType, "Missing grant_type."); Assert::notNull($this->clientId, "Missing client_id."); Assert::notNull($this->clientSecret, "Missing client_secret."); } catch (InvalidArgumentException $e) { throw new InvalidObjectConfigurationException($e->getMessage()); } $client = new Client; $tokenEndpointUrl = $this->reader->getFirstValueString($this->authServer, 'oauth2:hasTokenEndpoint'); $params = [ 'grant_type' => $this->grantType, 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret ]; switch ($this->grantType) { case "client_credentials": // no additional params needed break; default: throw new Exception("No flow/grant_type found."); } $grantCacheKey = sha1(json_encode($params)); $ts = microtime(true); $tokenResponse = json_decode($this->cacheAndLog->getFromCacheCustom($grantCacheKey)); if (isset($tokenResponse)) { $this->cacheAndLog->logInfoWithTime("Reused access token for <".$this->authServer->getId()."> from cache.", $ts); } else { // Nothing cached, fetch from server $tokenResponse = json_decode($client->post($tokenEndpointUrl, [ 'form_params' => $params ])->getBody(true), true); Assert::keyExists($tokenResponse, 'access_token'); $expiry = isset($tokenResponse['expires_in']) ? $tokenResponse['expires_in'] : 84600; $this->cacheAndLog->logInfoWithTime("Retrieved access token for <".$this->authServer->getId()."> from token endpoint.", $ts); $this->cacheAndLog->putIntoCacheCustom($grantCacheKey, json_encode($tokenResponse), $expiry); } return $tokenResponse['access_token']; } }