1
0
mirror of synced 2025-02-16 20:23:15 +03:00

oauth code exchange

This commit is contained in:
Pavel 2020-10-14 14:55:27 +03:00
parent e1161596dd
commit b529e2b83b
12 changed files with 651 additions and 3 deletions

View File

@ -28,6 +28,7 @@ use RetailCrm\Component\DependencyInjection\Container;
use RetailCrm\Component\Environment;
use RetailCrm\Component\ServiceLocator;
use RetailCrm\Factory\FileItemFactory;
use RetailCrm\Factory\OAuthTokenFetcherFactory;
use RetailCrm\Factory\ProductSchemaStorageFactory;
use RetailCrm\Factory\SerializerFactory;
use RetailCrm\Factory\TopRequestFactory;
@ -220,6 +221,15 @@ class ContainerBuilder implements BuilderInterface
$container->set(Constants::SERIALIZER, function (ContainerInterface $container) {
return SerializerFactory::withContainer($container)->create();
});
$container->set(OAuthTokenFetcherFactory::class, function (ContainerInterface $container) {
return new OAuthTokenFetcherFactory(
$container->get(Constants::SERIALIZER),
$container->get(StreamFactoryInterface::class),
$container->get(RequestFactoryInterface::class),
$container->get(UriFactoryInterface::class),
$container->get(Constants::HTTP_CLIENT)
);
});
$container->set(FileItemFactoryInterface::class, function (ContainerInterface $container) {
return new FileItemFactory($container->get(StreamFactoryInterface::class));
});

View File

@ -0,0 +1,29 @@
<?php
/**
* PHP version 7.3
*
* @category OAuthTokenFetcherException
* @package RetailCrm\Component\Exception
* @author RetailCRM <integration@retailcrm.ru>
* @license http://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
namespace RetailCrm\Component\Exception;
use Exception;
/**
* Class OAuthTokenFetcherException
*
* @category OAuthTokenFetcherException
* @package RetailCrm\Component\Exception
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license https://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
*/
class OAuthTokenFetcherException extends Exception
{
}

View File

@ -0,0 +1,170 @@
<?php
/**
* PHP version 7.3
*
* @category OAuthTokenFetcher
* @package RetailCrm\Component
* @author RetailCRM <integration@retailcrm.ru>
* @license http://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
namespace RetailCrm\Component;
use Exception;
use JMS\Serializer\SerializerInterface;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriFactoryInterface;
use RetailCrm\Component\Exception\OAuthTokenFetcherException;
use RetailCrm\Interfaces\AppDataInterface;
use RetailCrm\Model\Request\OAuthTokenFetchRequest;
use RetailCrm\Model\Response\OAuthTokenFetcherResponse;
/**
* Class OAuthTokenFetcher
*
* @category OAuthTokenFetcher
* @package RetailCrm\Component
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license https://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
*/
class OAuthTokenFetcher
{
/**
* @var SerializerInterface|\JMS\Serializer\Serializer $serializer
*/
private $serializer;
/**
* @var StreamFactoryInterface $streamFactory
*/
private $streamFactory;
/**
* @var \Psr\Http\Message\RequestFactoryInterface $requestFactory
*/
private $requestFactory;
/**
* @var \Psr\Http\Message\UriFactoryInterface $uriFactory
*/
private $uriFactory;
/**
* @var ClientInterface $client
*/
private $client;
/**
* @var \RetailCrm\Interfaces\AppDataInterface $appData
*/
private $appData;
/**
* OAuthTokenFetcher constructor.
*
* @param \JMS\Serializer\SerializerInterface $serializer
* @param \Psr\Http\Message\StreamFactoryInterface $streamFactory
* @param \Psr\Http\Message\RequestFactoryInterface $requestFactory
* @param \Psr\Http\Message\UriFactoryInterface $uriFactory
* @param \Psr\Http\Client\ClientInterface $client
*/
public function __construct(
SerializerInterface $serializer,
StreamFactoryInterface $streamFactory,
RequestFactoryInterface $requestFactory,
UriFactoryInterface $uriFactory,
ClientInterface $client
) {
$this->serializer = $serializer;
$this->streamFactory = $streamFactory;
$this->requestFactory = $requestFactory;
$this->uriFactory = $uriFactory;
$this->client = $client;
}
/**
* @param \RetailCrm\Interfaces\AppDataInterface $appData
*
* @return OAuthTokenFetcher
*/
public function setAppData(AppDataInterface $appData): OAuthTokenFetcher
{
$this->appData = $appData;
return $this;
}
/**
* @param string $code
* @param string $state
*
* @return \RetailCrm\Model\Response\OAuthTokenFetcherResponse
* @throws \RetailCrm\Component\Exception\OAuthTokenFetcherException
*/
public function fetchToken(string $code, string $state = ''): OAuthTokenFetcherResponse
{
$request = new OAuthTokenFetchRequest();
$request->clientId = $this->appData->getAppKey();
$request->clientSecret = $this->appData->getAppSecret();
$request->redirectUri = $this->appData->getRedirectUri();
$request->code = $code;
if ('' !== $state) {
$request->state = $state;
}
$requestData = $this->serializer->toArray($request);
$postData = http_build_query($requestData);
$request = null;
$response = null;
try {
$request = $this->requestFactory
->createRequest(
'POST',
$this->uriFactory->createUri('https://oauth.aliexpress.com/token')
)->withBody($this->streamFactory->createStream($postData))
->withHeader('content-type', 'application/x-www-form-urlencoded; charset=UTF-8');
} catch (Exception $exception) {
throw new OAuthTokenFetcherException(
sprintf('Cannot create request: %s', $exception->getMessage()),
$exception
);
}
try {
$response = $this->client->sendRequest($request);
} catch (ClientExceptionInterface $exception) {
throw new OAuthTokenFetcherException(
sprintf('Cannot send request: %s', $exception->getMessage()),
$exception
);
}
return $this->serializer->deserialize(
self::getBodyContents($response->getBody()),
OAuthTokenFetcherResponse::class,
'json'
);
}
/**
* Returns body stream data (it should work like that in order to keep compatibility with some implementations).
*
* @param \Psr\Http\Message\StreamInterface $stream
*
* @return string
*/
protected static function getBodyContents(StreamInterface $stream): string
{
return $stream->isSeekable() ? $stream->__toString() : $stream->getContents();
}
}

View File

@ -13,6 +13,7 @@
namespace RetailCrm\Component;
use RetailCrm\Factory\FileItemFactory;
use RetailCrm\Factory\OAuthTokenFetcherFactory;
use RetailCrm\Interfaces\ContainerAwareInterface;
use RetailCrm\Interfaces\FileItemFactoryInterface;
use RetailCrm\Traits\ContainerAwareTrait;
@ -31,6 +32,14 @@ class ServiceLocator implements ContainerAwareInterface
{
use ContainerAwareTrait;
/**
* @return \RetailCrm\Factory\OAuthTokenFetcherFactory
*/
public function getOAuthTokenFetcherFactory(): OAuthTokenFetcherFactory
{
return $this->getContainer()->get(OAuthTokenFetcherFactory::class);
}
/**
* @return \RetailCrm\Interfaces\FileItemFactoryInterface
*/

View File

@ -0,0 +1,98 @@
<?php
/**
* PHP version 7.3
*
* @category OAuthTokenFetcherFactory
* @package RetailCrm\Factory
* @author RetailCRM <integration@retailcrm.ru>
* @license http://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
namespace RetailCrm\Factory;
use JMS\Serializer\SerializerInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UriFactoryInterface;
use RetailCrm\Component\OAuthTokenFetcher;
use RetailCrm\Interfaces\ParametrizedFactoryInterface;
/**
* Class OAuthTokenFetcherFactory
*
* @category OAuthTokenFetcherFactory
* @package RetailCrm\Factory
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license https://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
*/
class OAuthTokenFetcherFactory implements ParametrizedFactoryInterface
{
/**
* @var SerializerInterface|\JMS\Serializer\Serializer $serializer
*/
private $serializer;
/**
* @var StreamFactoryInterface $streamFactory
*/
private $streamFactory;
/**
* @var \Psr\Http\Message\RequestFactoryInterface $requestFactory
*/
private $requestFactory;
/**
* @var \Psr\Http\Message\UriFactoryInterface $uriFactory
*/
private $uriFactory;
/**
* @var ClientInterface $client
*/
private $client;
/**
* OAuthTokenFetcherFactory constructor.
*
* @param \JMS\Serializer\SerializerInterface $serializer
* @param \Psr\Http\Message\StreamFactoryInterface $streamFactory
* @param \Psr\Http\Message\RequestFactoryInterface $requestFactory
* @param \Psr\Http\Message\UriFactoryInterface $uriFactory
* @param \Psr\Http\Client\ClientInterface $client
*/
public function __construct(
SerializerInterface $serializer,
StreamFactoryInterface $streamFactory,
RequestFactoryInterface $requestFactory,
UriFactoryInterface $uriFactory,
ClientInterface $client
) {
$this->serializer = $serializer;
$this->streamFactory = $streamFactory;
$this->requestFactory = $requestFactory;
$this->uriFactory = $uriFactory;
$this->client = $client;
}
/**
* @param \RetailCrm\Interfaces\AppDataInterface $params
*
* @return \RetailCrm\Component\OAuthTokenFetcher
*/
public function create($params): OAuthTokenFetcher
{
return (new OAuthTokenFetcher(
$this->serializer,
$this->streamFactory,
$this->requestFactory,
$this->uriFactory,
$this->client
))->setAppData($params);
}
}

View File

@ -0,0 +1,34 @@
<?php
/**
* PHP version 7.3
*
* @category ParametrizedFactoryInterface
* @package RetailCrm\Interfaces
* @author RetailCRM <integration@retailcrm.ru>
* @license MIT https://mit-license.org
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
namespace RetailCrm\Interfaces;
/**
* Interface ParametrizedFactoryInterface
*
* @category ParametrizedFactoryInterface
* @package RetailCrm\Interfaces
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license MIT https://mit-license.org
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
*/
interface ParametrizedFactoryInterface
{
/**
* @param mixed $params
*
* @return object
*/
public function create($params);
}

View File

@ -12,6 +12,7 @@
*/
namespace RetailCrm\Interfaces;
use RetailCrm\Component\OAuthTokenFetcher;
use RetailCrm\Component\ServiceLocator;
use RetailCrm\Model\Request\BaseRequest;
use RetailCrm\Model\Response\TopResponseInterface;
@ -48,6 +49,11 @@ interface TopClientInterface
*/
public function getAuthorizationUriBuilder(string $state = ''): BuilderInterface;
/**
* @return \RetailCrm\Component\OAuthTokenFetcher
*/
public function getTokenFetcher(): OAuthTokenFetcher;
/**
* Send TOP request
*

View File

@ -0,0 +1,92 @@
<?php
/**
* PHP version 7.3
*
* @category OAuthTokenFetchRequest
* @package RetailCrm\Model\Request
* @author RetailCRM <integration@retailcrm.ru>
* @license http://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
namespace RetailCrm\Model\Request;
use JMS\Serializer\Annotation as JMS;
/**
* Class OAuthTokenFetchRequest
*
* @category OAuthTokenFetchRequest
* @package RetailCrm\Model\Request
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license https://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
*/
class OAuthTokenFetchRequest
{
/**
* @var string $clientId
*
* @JMS\Type("string")
* @JMS\SerializedName("client_id")
*/
public $clientId;
/**
* @var string $clientSecret
*
* @JMS\Type("string")
* @JMS\SerializedName("client_secret")
*/
public $clientSecret;
/**
* @var string $grantType
*
* @JMS\Type("string")
* @JMS\SerializedName("grant_type")
*/
public $grantType = 'authorization_code';
/**
* @var string $code
*
* @JMS\Type("string")
* @JMS\SerializedName("code")
*/
public $code;
/**
* @var string $redirectUri
*
* @JMS\Type("string")
* @JMS\SerializedName("redirect_uri")
*/
public $redirectUri;
/**
* @var string $sp
*
* @JMS\Type("string")
* @JMS\SerializedName("sp")
*/
public $sp = 'ae';
/**
* @var string $state
*
* @JMS\Type("string")
* @JMS\SerializedName("state")
*/
public $state;
/**
* @var string $view
*
* @JMS\Type("string")
* @JMS\SerializedName("view")
*/
public $view = 'web';
}

View File

@ -0,0 +1,124 @@
<?php
/**
* PHP version 7.3
*
* @category OAuthTokenFetcherResponse
* @package RetailCrm\Model\Response
* @author RetailCRM <integration@retailcrm.ru>
* @license http://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
namespace RetailCrm\Model\Response;
use JMS\Serializer\Annotation as JMS;
/**
* Class OAuthTokenFetcherResponse
*
* @category OAuthTokenFetcherResponse
* @package RetailCrm\Model\Response
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license https://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
*/
class OAuthTokenFetcherResponse
{
/**
* @var string $accessToken
*
* @JMS\Type("string")
* @JMS\SerializedName("access_token")
*/
public $accessToken;
/**
* @var string $refreshToken
*
* @JMS\Type("string")
* @JMS\SerializedName("refresh_token")
*/
public $refreshToken;
/**
* @var int $w1Valid
*
* @JMS\Type("int")
* @JMS\SerializedName("w1_valid")
*/
public $w1Valid;
/**
* @var int $refreshTokenValidTime
*
* @JMS\Type("int")
* @JMS\SerializedName("refresh_token_valid_time")
*/
public $refreshTokenValidTime;
/**
* @var int $w2Valid
*
* @JMS\Type("int")
* @JMS\SerializedName("w2_valid")
*/
public $w2Valid;
/**
* @var string $userId
*
* @JMS\Type("string")
* @JMS\SerializedName("user_id")
*/
public $userId;
/**
* @var int $expireTime
*
* @JMS\Type("int")
* @JMS\SerializedName("expire_time")
*/
public $expireTime;
/**
* @var int $r2Valid
*
* @JMS\Type("int")
* @JMS\SerializedName("r2_valid")
*/
public $r2Valid;
/**
* @var string $locale
*
* @JMS\Type("string")
* @JMS\SerializedName("locale")
*/
public $locale;
/**
* @var int $r1Valid
*
* @JMS\Type("int")
* @JMS\SerializedName("r1_valid")
*/
public $r1Valid;
/**
* @var string $sp
*
* @JMS\Type("string")
* @JMS\SerializedName("sp")
*/
public $sp;
/**
* @var string $userNick
*
* @JMS\Type("string")
* @JMS\SerializedName("user_nick")
*/
public $userNick;
}

View File

@ -22,6 +22,7 @@ use RetailCrm\Builder\AuthorizationUriBuilder;
use RetailCrm\Component\Environment;
use RetailCrm\Component\Exception\TopApiException;
use RetailCrm\Component\Exception\TopClientException;
use RetailCrm\Component\OAuthTokenFetcher;
use RetailCrm\Component\ServiceLocator;
use RetailCrm\Component\Storage\ProductSchemaStorage;
use RetailCrm\Factory\ProductSchemaStorageFactory;
@ -211,6 +212,14 @@ class TopClient implements TopClientInterface
return new AuthorizationUriBuilder($this->appData->getAppKey(), $this->appData->getRedirectUri(), $state);
}
/**
* @return \RetailCrm\Component\OAuthTokenFetcher
*/
public function getTokenFetcher(): OAuthTokenFetcher
{
return $this->getServiceLocator()->getOAuthTokenFetcherFactory()->create($this->appData);
}
/**
* @return \RetailCrm\Component\Storage\ProductSchemaStorage
*/

View File

@ -0,0 +1,70 @@
<?php
/**
* PHP version 7.3
*
* @category OAuthTokenFetcherTest
* @package RetailCrm\Tests\Component
* @author RetailCRM <integration@retailcrm.ru>
* @license http://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
namespace RetailCrm\Tests\Component;
use RetailCrm\Builder\TopClientBuilder;
use RetailCrm\Test\RequestMatcher;
use RetailCrm\Test\TestCase;
/**
* Class OAuthTokenFetcherTest
*
* @category OAuthTokenFetcherTest
* @package RetailCrm\Tests\Component
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license https://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
*/
class OAuthTokenFetcherTest extends TestCase
{
public function testFetchToken()
{
$jsonResponse = <<<'EOF'
{
"access_token": "accessToken",
"refresh_token": "refreshToken",
"w1_valid": 1,
"refresh_token_valid_time": 1602676186888,
"w2_valid": 1,
"user_id": "0000000000",
"expire_time": 1,
"r2_valid": 1,
"locale": "zh_CN",
"r1_valid": 1,
"sp": "ae",
"user_nick": "ru0000000000"
}
EOF;
$mock = self::getMockClient();
$mock->on(
RequestMatcher::createMatcher('oauth.aliexpress.com')
->setPath('/token')
->setOptionalPostFields([
'code' => 'oauthCode',
'state' => '{"accountId":5,"token":"login-5f86e5579dad92"}'
]),
$this->responseJson(200, $jsonResponse)
);
$tokenFetcher = TopClientBuilder::create()
->setContainer($this->getContainer($mock))
->setAppData($this->getEnvAppData())
->setAuthenticator($this->getEnvTokenAuthenticator())
->build()
->getTokenFetcher();
$response = $tokenFetcher->fetchToken('oauthCode', '{"accountId":5,"token":"login-5f86e5579dad92"}');
self::assertEquals('accessToken', $response->accessToken);
self::assertEquals('refreshToken', $response->refreshToken);
}
}

View File

@ -16,9 +16,6 @@ use DateTime;
use Http\Message\RequestMatcher\CallbackRequestMatcher;
use Psr\Http\Message\RequestInterface;
use RetailCrm\Builder\TopClientBuilder;
use RetailCrm\Component\Constants;
use RetailCrm\Component\Logger\FileLogger;
use RetailCrm\Component\Logger\StdoutLogger;
use RetailCrm\Model\Entity\CategoryInfo;
use RetailCrm\Model\Enum\DropshippingAreas;
use RetailCrm\Model\Enum\FeedOperationTypes;