mirror of
https://github.com/Karaka-Management/phpOMS.git
synced 2026-01-11 09:48:40 +00:00
323 lines
8.7 KiB
PHP
323 lines
8.7 KiB
PHP
<?php
|
|
/**
|
|
* Orange Management
|
|
*
|
|
* PHP Version 7.4
|
|
*
|
|
* @package phpOMS\Auth\OAuth2
|
|
* @copyright Dennis Eichhorn
|
|
* @license OMS License 1.0
|
|
* @version 1.0.0
|
|
* @link https://orange-management.org
|
|
* @see https://tools.ietf.org/html/rfc6749
|
|
*/
|
|
declare(strict_types=1);
|
|
|
|
namespace phpOMS\Auth\OAuth2\Provider;
|
|
|
|
use phpOMS\Message\Http\HttpResponse;
|
|
use phpOMS\Message\Http\RequestMethod;
|
|
use phpOMS\Uri\UriFactory;
|
|
use phpOMS\Utils\ArrayUtils;
|
|
|
|
/**
|
|
* Provider class.
|
|
*
|
|
* @package phpOMS\Auth\OAuth2
|
|
* @license OMS License 1.0
|
|
* @link https://orange-management.org
|
|
* @since 1.0.0
|
|
*/
|
|
abstract class ProviderAbstract
|
|
{
|
|
protected const ACCESS_TOKEN_RESOURCE_OWNER_ID = null;
|
|
|
|
protected string $clientId;
|
|
|
|
protected string $clientSecret;
|
|
|
|
protected string $redirectUri;
|
|
|
|
protected string $state;
|
|
|
|
protected GrantFactory $grantFactory;
|
|
|
|
protected ReuqestFactory $requestFactory;
|
|
|
|
protected OptionProviderInterface $optionProvider;
|
|
|
|
public function __construct(array $options = [], array $collaborators = [])
|
|
{
|
|
foreach ($options as $key => $option) {
|
|
if (\property_exists($this, $key)) {
|
|
$this->{$key} = $option;
|
|
}
|
|
}
|
|
|
|
$this->setGrantFactory($collaborators['grantFactory'] ?? new GrantFactory());
|
|
$this->setRequestFactory($collaborators['requestFactory'] ?? new RequestFactory());
|
|
$this->setOptionProvider($collaborators['optionProvider'] ?? new PostAuthOptionProvider());
|
|
}
|
|
|
|
public function setGrantFactory(GrantFactory $factory) : self
|
|
{
|
|
$this->grantFactory = $factory;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getGrantFactory() : GrantFactory
|
|
{
|
|
return $this->grantFactory;
|
|
}
|
|
|
|
public function setRequestFactory(RequestFactory $factory) : self
|
|
{
|
|
$this->requestFactory = $factory;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getRequestFactory() : RequestFactory
|
|
{
|
|
return $this->requestFactory;
|
|
}
|
|
|
|
public function setOptionProvider(OptionProviderInterface $provider) : self
|
|
{
|
|
$this->optionProvider = $provider;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getOptionProvider() : OptionProviderInterface
|
|
{
|
|
return $this->optionProvider;
|
|
}
|
|
|
|
public function getState() : string
|
|
{
|
|
return $this->state;
|
|
}
|
|
|
|
abstract public function getBaseAuthorizationUrl() : string;
|
|
|
|
abstract public function getBaseAccessTokenUrl(array $params = []) : string;
|
|
|
|
abstract public function getResourceOwnerDetailsUrl(AccessToken $token) : string;
|
|
|
|
protected function getRandomState(int $length = 32) : string
|
|
{
|
|
return \bin2hex(\random_bytes($length / 2));
|
|
}
|
|
|
|
abstract protected function getDefaultScopes() : array;
|
|
|
|
protected function getScopeSeparator() : string
|
|
{
|
|
return ',';
|
|
}
|
|
|
|
protected function getAuthorizationParameters(array $options) : array
|
|
{
|
|
$options['state'] ??= $this->getRandomState();
|
|
$options['scope'] ??= $this->getDefaultScopes();
|
|
|
|
$this->state = $options['state'];
|
|
|
|
$options += [
|
|
'response_type' => 'code',
|
|
'approval_prompt' => 'auto',
|
|
];
|
|
|
|
if (\is_array($options['scope'])) {
|
|
$options['scope'] = \implode($this->getScopeSeparator(), $options['scope']);
|
|
}
|
|
|
|
$options['redirect_uri'] ??= $this->redirectUri;
|
|
$options['client_id'] = $this->clientId;
|
|
|
|
return $options;
|
|
}
|
|
|
|
protected function getAuthorizationQuery(array $params) : string
|
|
{
|
|
return \http_build_query($params, null, '&', \PHP_QUERY_RFC3986);
|
|
}
|
|
|
|
public function getauthorizationUrl(array $options = []) : string
|
|
{
|
|
$base = $this->getBaseAuthorizationUrl();
|
|
$params = $this->getAuthorizationParameters($options);
|
|
$query = $this->getAuthorizationQuery($params);
|
|
|
|
return UriFactory::build($base . '?' . $query);
|
|
}
|
|
|
|
public function authorize(array $options = [], callable $redirectHander = null)
|
|
{
|
|
$url = $this->getAuthorizationUrl($options);
|
|
if ($redirectHander !== null) {
|
|
return $redirectHandler($url, $this);
|
|
}
|
|
|
|
// @codeCoverageIgnoreStart
|
|
\header('Location: ' . $url);
|
|
exit;
|
|
// @codeCoverageIgnoreEnd
|
|
}
|
|
|
|
protected function getAccessTokenMethod() : string
|
|
{
|
|
return RequestMethod::POST;
|
|
}
|
|
|
|
protected function getAccessTokenResourceOwnerId() : ?string
|
|
{
|
|
return static::ACCESS_TOKEN_RESOURCE_OWNER_ID;
|
|
}
|
|
|
|
protected function verifyGrant($grant) : AbstractGrant
|
|
{
|
|
$this->grantFactory->checkGrant($grant);
|
|
|
|
return $grant;
|
|
}
|
|
|
|
protected function getAccessTokenUrl(array $params) : string
|
|
{
|
|
$url = $this->getBaseAccessTokenUrl($params);
|
|
|
|
if ($this->getAccessTokenMethod() === RequestMethod::GET) {
|
|
$query = $this->getAccessTokenQuery($params);
|
|
|
|
return UriFactory::build($ur . '?' . $query);
|
|
}
|
|
|
|
return $url;
|
|
}
|
|
|
|
protected function getAccessTokenRequest(array $params) : HttpRequest
|
|
{
|
|
$method = $this->getAccessTokenMethod();
|
|
$url = $this->getAccessTokenUrl($params);
|
|
$options = $this->getoptionProvider->getAccessTokenOptions($this->getAccessTokenMethod(), $params);
|
|
|
|
return $this->createRequest($method, $url, null, $options);
|
|
}
|
|
|
|
// string | Grant
|
|
public function getAccessToken($grant, array $options = []) : AccessTokenInterface
|
|
{
|
|
$grant = \is_string($grant) ? $this->grantFactory->getGrant($grant) : $this->verifyGrant();
|
|
|
|
$params = [
|
|
'client_id' => $this->clientId,
|
|
'client_secret' => $this->clientSecret,
|
|
'redirect_uri' => $this->redirectUri,
|
|
];
|
|
|
|
$params = $grant->prepareRequestParameters($params, $options);
|
|
$request = $this->getAccessTokenRequest($params);
|
|
$response = $this->getParsedResponse($request);
|
|
|
|
$prepared = $this->prepareAccessTokenResponse($response);
|
|
$token = $this->createAccessToken($prepared, $grant);
|
|
|
|
return $token;
|
|
}
|
|
|
|
public function createRequest(string $method, string $url, $token, array $options) : HttpRequest
|
|
{
|
|
$defaults = [
|
|
'headers' => $this->getHeaders($token),
|
|
];
|
|
|
|
$options = \array_merge_recursive($defaults, $options);
|
|
$factory = $this->getRequestFactory();
|
|
|
|
return $factory->getRequestWithOptions($method, $url, $options);
|
|
}
|
|
|
|
public function getParsedResponse(HttpRequest $request)
|
|
{
|
|
$response = $request->rest();
|
|
$parsed = $this->parseResponse($response);
|
|
|
|
$this->checkResponse($response, $parsed);
|
|
|
|
return $parsed;
|
|
}
|
|
|
|
protected function parseResponse(HttpResponse $response) : array
|
|
{
|
|
$content = $response->getBody();
|
|
$type = \implode(';', (array) $response->getHeader()->get('Content-Type'));
|
|
|
|
if (\stripos($type, 'urlencoded') !== false) {
|
|
\parse_str($content, $parsed);
|
|
|
|
return $parsed;
|
|
}
|
|
|
|
try {
|
|
return \json_decode($content, true);
|
|
} catch (\Throwable $t) {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
abstract protected function checkResponse(HttpResponse $response, $data) : void;
|
|
|
|
// todo: consider to make bool
|
|
|
|
protected function prepareAccessTokenResponse(array $result) : array
|
|
{
|
|
if (($id = $this->getAccesstokenResourceOwnerId()) !== null) {
|
|
$result['resource_owner_id'] = ArrayUtils::getArray($id, $result, '.');
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
protected function createAccessToken(array $response, AbstractGrant $grant) : AccessTokenInterface
|
|
{
|
|
return new AccessToken($response);
|
|
}
|
|
|
|
abstract protected function createResourceOwner(array $response, AccessToken $token) : ResourceOwnerInterface;
|
|
|
|
public function getResourceOwner(AccessToken $token) : ResourceOwnerInterface
|
|
{
|
|
$response = $this->fetchResourceOwnerDetails($token);
|
|
|
|
return $this->createResourceOwner($response, $token);
|
|
}
|
|
|
|
protected function fetchResourceOwnerDetails(AccessToken $token)
|
|
{
|
|
$url = $this->getResourceOwnerDetailsUrl($token);
|
|
$request = $this->getAuthenticatedRequest(RequestMethod::GET, $url, $token);
|
|
$response = $this->getParsedResponse($request);
|
|
|
|
return $response;
|
|
}
|
|
|
|
protected function getDefaultHeaders() : array
|
|
{
|
|
return [];
|
|
}
|
|
|
|
protected function getAuthorizationHeaders($token = null) : array
|
|
{
|
|
return [];
|
|
}
|
|
|
|
public function getHeaders($token = null) : array
|
|
{
|
|
return $token === null
|
|
? $this->getDefaultHeaders()
|
|
: \array_merge($this->getDefaultHeaders(), $this->getAuthorizationHeaders());
|
|
}
|
|
}
|