Authentication Integration
Note
此页面的内容需要进行重大更新。旧页面包含过时且可能不准确的信息。您仍然可以通过 Mautic Developer Documentation archived repository 访问它。
如果您有兴趣帮助开发此页面和其他新内容的,请考虑加入文档编写工作。
请阅读 Contributing Guidelines 和 Contributing to Mautic’s documentation 以开始您的贡献。
IntegrationsBundle 提供工厂类和辅助工具,用于创建 Guzzle Client 类,以支持常见的身份验证协议。
注册用于身份验证的集成
如果集成需要用户通过 Web 进行身份验证(OAuth2 三步流程),则该集成需要使用 mautic.auth_integration 标记一个服务,以处理身份验证过程,例如重定向到登录页面、请求访问令牌等。
<?php
return [
// ...
'services' => [
// ...
'integrations' => [
// ...
'helloworld.integration.authentication' => [
'class' => \MauticPlugin\HelloWorldBundle\Integration\Support\AuthSupport::class,
'tags' => [
'mautic.auth_integration',
],
],
// ...
],
// ...
],
// ...
];
此服务需要实现 \Mautic\IntegrationsBundle\Integration\Interfaces\AuthenticationInterface。
- interface Mautic\IntegrationsBundle\Integration\Interfaces\AuthenticationInterface
- public function isAuthenticated(): bool;
- Returns:
如果集成已通过第三方服务进行授权,则返回 true。
- Return type:
bool
- public function authenticateIntegration(Request $request): string;
- Parameters:
$request (
Request) – 请求对象。
- Returns:
成功时要渲染的消息。
- Return type:
string
请参考以下代码片段,
<?php
namespace MauticPlugin\HelloWorldBundle\Integration\Support;
use MauticPlugin\HelloWorldBundle\Connection\Client;
use MauticPlugin\HelloWorldBundle\Integration\HelloWorldIntegration;
use Mautic\IntegrationsBundle\Integration\ConfigurationTrait;
use Mautic\IntegrationsBundle\Integration\Interfaces\AuthenticationInterface;
use Symfony\Component\HttpFoundation\Request;
class AuthSupport implements AuthenticationInterface
{
use ConfigurationTrait;
private Client $client;
public function __construct(Client $client)
{
$this->client = $client;
}
- public function getName(): string
- {
return HelloWorldIntegration::NAME;
}
public function getDisplayName(): string {
return ‘Hello World’;
}
- /**
Returns true if the integration has already been authorized with the third party service.
@return bool
*/
public function isAuthenticated(): bool {
$apiKeys = $this->getIntegrationConfiguration()->getApiKeys();
return !empty($apiKeys[‘access_token’]) && !empty($apiKeys[‘refresh_token’]);
}
- /**
Authenticate and obtain the access token
@param Request $request
@return string
*/
public function authenticateIntegration(Request $request): string {
$code = $request->query->get(‘code’);
$this->client->authenticate($code);
return ‘Success!’;
}
}
Authentication providers
The Integration bundle comes with a number of popular authentication protocols available to use as Guzzle clients. New ones should implement:
\Mautic\IntegrationsBundle\Auth\Provider\AuthProviderInterface
The examples below use anonymous classes. Use Object Oriented Programming with services and factories to generate credential, configuration, and client classes.
The best way to get configuration values such as username, password, consumer key, consumer secret, and so forth is by using the mautic.integrations.helper (\Mautic\IntegrationsBundle\Helper\IntegrationsHelper) service to leverage the configuration stored in the Integration entity’s API keys.
<?php
use MauticPlugin\HelloWorldBundle\Integration\HelloWorldIntegration;
use Mautic\IntegrationsBundle\Helper\IntegrationsHelper;
/** @var $integrationsHelper IntegrationsHelper */
$integration = $integrationsHelper->getIntegration(HelloWorldIntegration::NAME);
$configuration = $integration->getIntegrationConfiguration();
$apiKeys = $configuration->getApiKeys();
$username = $apiKeys['username'] ?? null;
$password = $apiKeys['password'] ?? null;
//...
API key
Use the mautic.integrations.auth_provider.api_key service - \Mautic\IntegrationsBundle\Auth\Provider\ApiKey\HttpFactory - to obtain a GuzzleHttp\ClientInterface that uses an API key for all requests. Out of the box, the factory supports a parameter API key or a header API key.
Parameter based API key
To use the parameter based API key, create a credentials class that implements \Mautic\IntegrationsBundle\Auth\Provider\ApiKey\Credentials\ParameterCredentialsInterface.
- class \Mautic\IntegrationsBundle\Auth\Provider\ApiKey\Credentials\ParameterCredentialsInterface
- public function getKeyName(): string;
- return:
- Key name.
- returntype:
string
- public function getApiKey(): ?string;
- Returns:
API key or null.
- Return type:
?string
Find the code snippet as follows,
<?php
use MauticPlugin\HelloWorldBundle\Integration\HelloWorldIntegration;
use Mautic\IntegrationsBundle\Auth\Provider\ApiKey\Credentials\ParameterCredentialsInterface;
use Mautic\IntegrationsBundle\Auth\Provider\ApiKey\HttpFactory;
use Mautic\IntegrationsBundle\Helper\IntegrationsHelper;
/** @var $integrationsHelper IntegrationsHelper */
$integration = $integrationsHelper->getIntegration(HelloWorldIntegration::NAME);
$apiKeys = $integration->getIntegrationConfiguration()->getApiKeys();
$credentials = new class($apiKeys['api_key']) implements ParameterCredentialsInterface {
private $key;
public function __construct(string $key)
{
$this->key = $key;
}
public function getKeyName(): string
{
return 'apikey';
}
public function getApiKey(): string
{
return $this->key;
}
};
/** @var $factory HttpFactory */
$client = $factory->getClient($credentials);
$response = $client->get('https://example.com/api/fetch');
Header based API key
To use the header based API key, create a credentials class that implements \Mautic\IntegrationsBundle\Auth\Provider\ApiKey\Credentials\HeaderCredentialsInterface.
- class \Mautic\IntegrationsBundle\Auth\Provider\ApiKey\Credentials\HeaderCredentialsInterface
- public function getKeyName(): string;
- Returns:
Key name.
- Return type:
string
- public function getApiKey(): ?string;
- Returns:
API key or null.
- Return type:
?string
Find the code snippet as follows,
<?php
use MauticPlugin\HelloWorldBundle\Integration\HelloWorldIntegration;
use Mautic\IntegrationsBundle\Auth\Provider\ApiKey\Credentials\HeaderCredentialsInterface;
use Mautic\IntegrationsBundle\Auth\Provider\ApiKey\HttpFactory;
use Mautic\IntegrationsBundle\Helper\IntegrationsHelper;
/** @var $integrationsHelper IntegrationsHelper */
$integration = $integrationsHelper->getIntegration(HelloWorldIntegration::NAME);
$apiKeys = $integration->getIntegrationConfiguration()->getApiKeys();
$credentials = new class($apiKeys['api_key']) implements HeaderCredentialsInterface {
private $key;
public function __construct(string $key)
{
$this->key = $key;
}
public function getKeyName(): string
{
return 'X-API-KEY';
}
public function getApiKey(): string
{
return $this->key;
}
};
- /** @var $factory HttpFactory */
$client = $factory->getClient($credentials); $response = $client->get(’https://example.com/api/fetch’);
Basic auth
使用 mautic.integrations.auth_provider.basic_auth 服务 - MauticIntegrationsBundleAuthProviderBasicAuthHttpFactory - 以获取一个 GuzzleHttpClientInterface,该接口在所有请求中使用基本身份验证。
要使用基本身份验证,请创建一个实现 MauticIntegrationsBundleAuthProviderBasicAuthCredentialsInterface 的凭据类。
- class \Mautic\IntegrationsBundle\Auth\Provider\BasicAuth\CredentialsInterface
- public function getUsername(): ?string;
- Returns:
用户名。
- Return type:
?string
- public function getPassword(): ?string;
- Returns:
密码。
- Return type:
?string
请参考以下代码片段:
<?php
use MauticPlugin\HelloWorldBundle\Integration\HelloWorldIntegration;
use Mautic\IntegrationsBundle\Helper\IntegrationsHelper;
use Mautic\IntegrationsBundle\Auth\Provider\BasicAuth\HttpFactory;
use Mautic\IntegrationsBundle\Auth\Provider\BasicAuth\CredentialsInterface;
/** @var $integrationsHelper IntegrationsHelper */
$integration = $integrationsHelper->getIntegration(HelloWorldIntegration::NAME);
$configuration = $integration->getIntegrationConfiguration();
$apiKeys = $configuration->getApiKeys();
$credentials = new class($apiKeys['username'], $apiKeys['password']) implements CredentialsInterface {
private $username;
private $password;
public function __construct(string $username, string $password)
{
$this->username = $username;
$this->password = $password;
}
public function getUsername(): string
{
return $this->username;
}
public function getPassword(): string
{
return $this->password;
}
};
/** @var $factory HttpFactory */
$client = $factory->getClient($credentials);
$response = $client->get('https://example.com/api/fetch');
OAuth1a
OAuth1a 三段式认证
尚未在核心中实现。
OAuth1a 双段式认证
OAuth1a 双段式认证不需要用户登录,就像三段式认证一样。
<?php
use MauticPlugin\HelloWorldBundle\Integration\HelloWorldIntegration;
use Mautic\IntegrationsBundle\Helper\IntegrationsHelper;
use Mautic\IntegrationsBundle\Auth\Provider\OAuth1aTwoLegged\HttpFactory;
use Mautic\IntegrationsBundle\Auth\Provider\OAuth1aTwoLegged\CredentialsInterface;
/** @var $integrationsHelper IntegrationsHelper */
$integration = $integrationsHelper->getIntegration(HelloWorldIntegration::NAME);
$configuration = $integration->getIntegrationConfiguration();
$apiKeys = $configuration->getApiKeys();
```markdown $credentials = new class(
‘https://example.com/api/oauth/token’, $apiKeys[‘consumer_key’], $apiKeys[‘consumer_secret’]
- ) implements CredentialsInterface {
private $authUrl; private $consumerKey; private $consumerSecret;
public function __construct(string $authUrl, string $consumerKey, string $consumerSecret) {
$this->authUrl = $authUrl; $this->consumerKey = $consumerKey; $this->consumerSecret = $consumerSecret;
}
public function getAuthUrl(): string {
return $this->authUrl;
}
public function getConsumerKey(): ?string {
return $this->consumerKey;
}
public function getConsumerSecret(): ?string {
return $this->consumerSecret;
}
- /**
Not used in this example. Tsk tsk for breaking the interface segregation principle
@return string|null
*/
public function getToken(): ?string {
return null;
}
- /**
Not used in this example. Tsk tsk for breaking the interface segregation principle
@return string|null
*/
public function getTokenSecret(): ?string {
return null;
}
};
/** @var $factory HttpFactory */ $client = $factory->getClient($credentials); $response = $client->get(’https://example.com/api/fetch’);
OAuth2
使用 OAuth2 工厂,根据所需的授权类型。 MauticIntegrationsBundleAuthProviderOauth2ThreeLeggedHttpFactory 支持 code 和 refresh_token 授权类型。 MauticIntegrationsBundleAuthProviderOauth2TwoLeggedHttpFactory 支持 client_credentials 和 password 授权类型。
OAuth2 工厂利用 Guzzle Oauth2 Subscriber 作为中间件。
客户端配置
两个 OAuth2 工厂都使用 MauticIntegrationsBundleAuthProviderAuthConfigInterface 对象来管理诸如配置签名器(基本身份验证、post form data、自定义)、令牌工厂、令牌持久化以及令牌签名器(凭据身份验证、基本身份验证、查询字符串、自定义)等内容。 根据用例使用适当的接口 (请参阅 app/bundles/IntegrationsBundle/Auth/Support/Oauth2/ConfigAccess 中的接口)。
有关配置凭据和令牌签名器或创建自定义令牌持久化和工厂的更多详细信息,请参阅 Guzzle Oauth2 Subscriber。
令牌持久化
在大多数用例中,令牌持久化服务需要获取和存储使用刷新令牌等生成的访问令牌。 IntegrationBundle 提供了该功能,它原生使用 MauticPluginBundleEntityIntegration 实体的 API 密钥。 通过该服务存储的任何内容都会自动加密。 ```
Use the mautic.integrations.auth_provider.token_persistence_factory service - \Mautic\IntegrationsBundle\Auth\Support\Oauth2\Token\TokenPersistenceFactory - to generate a TokenFactoryInterface. The``MauticIntegrationsBundleAuthSupportOauth2ConfigAccessConfigTokenFactoryInterface`` interface returns it.
<?php
use kamermans\OAuth2\Persistence\TokenPersistenceInterface;
use MauticPlugin\HelloWorldBundle\Integration\HelloWorldIntegration;
use Mautic\IntegrationsBundle\Auth\Support\Oauth2\ConfigAccess\ConfigTokenPersistenceInterface;
use Mautic\IntegrationsBundle\Auth\Support\Oauth2\Token\TokenPersistenceFactory;
use Mautic\IntegrationsBundle\Helper\IntegrationsHelper;
/** @var $integrationsHelper IntegrationsHelper */
$integration = $integrationsHelper->getIntegration(HelloWorldIntegration::NAME);
/** @var $tokenPersistenceFactory TokenPersistenceFactory */
$tokenPersistence = $tokenPersistenceFactory->create($integration);
$config = new class($tokenPersistence) implements ConfigTokenPersistenceInterface {
private $tokenPersistence;
public function __construct(TokenPersistenceInterface$tokenPersistence)
{
$this->tokenPersistence = $tokenPersistence;
}
public function getTokenPersistence(): TokenPersistenceInterface
{
return $this->tokenPersistence;
}
};
The token persistence service automatically manages access_token, refresh_token, and expires_at from the authentication process and stores them in the Integration entity’s API keys array.
Token factory
In some cases, the third-party service may return additional values that aren’t traditionally part of the OAuth2 spec. Sometimes, the API service requires these values for further communication. In this case, the Integration bundle’s \Mautic\IntegrationsBundle\Auth\Support\Oauth2\Token\IntegrationTokenFactory can use to capture those extra values and store them in the Integration entity’s API keys array.
You can then return the IntegrationTokenFactory in a \Mautic\IntegrationsBundle\Auth\Support\Oauth2\ConfigAccess\ConfigTokenFactoryInterface when configuring the Client.
<?php
use Mautic\IntegrationsBundle\Auth\Support\Oauth2\ConfigAccess\ConfigTokenFactoryInterface;
use Mautic\IntegrationsBundle\Auth\Support\Oauth2\Token\IntegrationTokenFactory;
use Mautic\IntegrationsBundle\Auth\Support\Oauth2\Token\TokenFactoryInterface;
$tokenFactory = new IntegrationTokenFactory(['something_extra']);
$config = new class($tokenFactory) implements ConfigTokenFactoryInterface {
private $tokenFactory;
public function __construct(TokenFactoryInterface $tokenFactory)
{
$this->tokenFactory = $tokenFactory;
}
- public function getTokenFactory(): TokenFactoryInterface
- {
return $this->tokenFactory;
}
};
OAuth2 两步验证
密码授权
以下是一个使用范围的服务,其密码授权的示例。使用的接口是可选的。令牌持久化的使用假设访问令牌有效期为一小时。
<?php
use kamermans\OAuth2\Persistence\TokenPersistenceInterface;
use MauticPlugin\HelloWorldBundle\Integration\HelloWorldIntegration;
use Mautic\IntegrationsBundle\Auth\Provider\Oauth2TwoLegged\Credentials\PasswordCredentialsGrantInterface;
use Mautic\IntegrationsBundle\Auth\Provider\Oauth2TwoLegged\Credentials\ScopeInterface;
use Mautic\IntegrationsBundle\Auth\Provider\Oauth2TwoLegged\HttpFactory;
use Mautic\IntegrationsBundle\Auth\Support\Oauth2\ConfigAccess\ConfigTokenPersistenceInterface;
use Mautic\IntegrationsBundle\Helper\IntegrationsHelper;
/** @var $integrationsHelper IntegrationsHelper */
$integration = $integrationsHelper->getIntegration(HelloWorldIntegration::NAME);
$configuration = $integration->getIntegrationConfiguration();
$apiKeys = $configuration->getApiKeys();
$credentials = new class(
'https://example.com/api/oauth/token',
'scope1,scope2',
$apiKeys['client_id'],
$apiKeys['client_secret'],
$apiKeys['username'],
$apiKeys['password']
) implements PasswordCredentialsGrantInterface, ScopeInterface {
private $authorizeUrl;
private $scope;
private $clientId;
private $clientSecret;
private $username;
private $password;
public function getAuthorizationUrl(): string
{
return $this->authorizeUrl;
}
public function getClientId(): ?string
{
return $this->clientId;
}
public function getClientSecret(): ?string
{
return $this->clientSecret;
}
public function getPassword(): ?string
{
return $this->password;
}
public function getUsername(): ?string
{
return $this->username;
}
public function getScope(): ?string
{
return $this->scope;
}
};
/** @var $tokenPersistenceFactory TokenPersistenceFactory */
$tokenPersistence = $tokenPersistenceFactory->create($integration);
$config = new class($tokenPersistence) implements ConfigTokenPersistenceInterface {
private $tokenPersistence;
public function __construct(TokenPersistenceInterface$tokenPersistence)
{
$this->tokenPersistence = $tokenPersistence;
}
public function getTokenPersistence(): TokenPersistenceInterface
{
return $this->tokenPersistence;
}
};
- /** @var $factory HttpFactory */
$client = $factory->getClient($credentials, $config); $response = $client->get(’https://example.com/api/fetch’);
客户端凭据授权
以下是一个使用范围的服务示例,展示了客户端凭据授权。使用的接口是可选的。令牌持久化的使用假设访问令牌有效期为一小时。
<?php
use kamermans\OAuth2\Persistence\TokenPersistenceInterface;
use MauticPlugin\HelloWorldBundle\Integration\HelloWorldIntegration;
use Mautic\IntegrationsBundle\Auth\Provider\Oauth2TwoLegged\Credentials\ClientCredentialsGrantInterface;
use Mautic\IntegrationsBundle\Auth\Provider\Oauth2TwoLegged\Credentials\ScopeInterface;
use Mautic\IntegrationsBundle\Auth\Provider\Oauth2TwoLegged\HttpFactory;
use Mautic\IntegrationsBundle\Auth\Support\Oauth2\ConfigAccess\ConfigTokenPersistenceInterface;
use Mautic\IntegrationsBundle\Helper\IntegrationsHelper;
/** @var $integrationsHelper IntegrationsHelper */
$integration = $integrationsHelper->getIntegration(HelloWorldIntegration::NAME);
$configuration = $integration->getIntegrationConfiguration();
$apiKeys = $configuration->getApiKeys();
$credentials = new class(
'https://example.com/api/oauth/token',
'scope1,scope2',
$apiKeys['client_id'],
$apiKeys['client_secret']
) implements ClientCredentialsGrantInterface, ScopeInterface {
private $authorizeUrl;
private $scope;
private $clientId;
private $clientSecret;
public function getAuthorizationUrl(): string
{
return $this->authorizeUrl;
}
public function getClientId(): ?string
{
return $this->clientId;
}
public function getClientSecret(): ?string
{
return $this->clientSecret;
}
public function getScope(): ?string
{
return $this->scope;
}
};
/** @var $tokenPersistenceFactory TokenPersistenceFactory */
$tokenPersistence = $tokenPersistenceFactory->create($integration);
$config = new class($tokenPersistence) implements ConfigTokenPersistenceInterface {
private $tokenPersistence;
public function __construct(TokenPersistenceInterface$tokenPersistence)
{
$this->tokenPersistence = $tokenPersistence;
}
public function getTokenPersistence(): TokenPersistenceInterface
{
return $this->tokenPersistence;
}
};
/** @var $factory HttpFactory */
$client = $factory->getClient($credentials, $config);
$response = $client->get('https://example.com/api/fetch');
OAuth2 三步验证
三段式 OAuth2 使用授权码模式是最复杂的实现方式,因为它涉及到将用户重定向到第三方服务进行身份验证,然后再将其返回到 Mautic 以启动访问令牌流程,使用请求中返回的代码。
第一步是注册集成作为 \Mautic\IntegrationsBundle\Integration\Interfaces\AuthenticationInterface。 authenticateIntegration() 方法使用在用户登录到第三方服务后通过请求返回的 code 来启动访问令牌流程。
集成包提供了一个路由,该路由可以作为重定向或回调 URI 使用,命名为 mautic_integration_public_callback,它需要一个 integration 参数。
此重定向 URI 可以在 UI 中通过 ConfigFormCallbackInterface 显示。 此路由用于从 AuthIntegrationsHelper 中按名称查找集成,然后执行其 authenticateIntegration() 方法。
<?php
namespace MauticPlugin\HelloWorldBundle\Integration\Support;
use GuzzleHttp\ClientInterface;
use Mautic\IntegrationsBundle\Integration\Interfaces\AuthenticationInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class AuthSupport implements AuthenticationInterface {
private ClientInterface $client;
// ...
public function authenticateIntegration(Request $request): Response
{
$code = $request->query->get('code');
$this->client->authenticate($code);
return new Response('OK!');
}
}
这里的关键在于,Client 的 authenticate 方法配置一个 ClientInterface,然后调用任何有效的 API URL。 中间件通过发起一次调用并将其存储在 Integration 实体中的 API 密钥中来启动访问令牌流程,具体方法是使用 TokenPersistenceFactory。 Mautic 建议保持 URL 的简单性,例如检查版本或获取已验证用户的相关信息。
以下是一个客户端的示例,假设用户已经登录并且代码位于请求中。
use kamermansOAuth2PersistenceTokenPersistenceInterface; use MauticPluginHelloWorldBundleIntegrationHelloWorldIntegration; use MauticIntegrationsBundleAuthProviderOauth2ThreeLeggedCredentialsCodeInterface; use MauticIntegrationsBundleAuthProviderOauth2ThreeLeggedCredentialsCredentialsInterface; use MauticIntegrationsBundleAuthProviderOauth2ThreeLeggedCredentialsRedirectUriInterface; use MauticIntegrationsBundleAuthProviderOauth2TwoLeggedCredentialsScopeInterface; use MauticIntegrationsBundleAuthProviderOauth2TwoLeggedHttpFactory; use MauticIntegrationsBundleAuthSupportOauth2ConfigAccessConfigTokenPersistenceInterface; use MauticIntegrationsBundleHelperIntegrationsHelper; use SymfonyComponentHttpFoundationRequest; use SymfonyComponentRoutingRouter;
/** @var $integrationsHelper IntegrationsHelper */ $integration = $integrationsHelper->getIntegration(HelloWorldIntegration::NAME);
/** @var Router $router */ $redirectUrl = $router->generate(‘mautic_integration_public_callback’, [‘integration’ => HelloWorldIntegration::NAME]);
$configuration = $integration->getIntegrationConfiguration(); $apiKeys = $configuration->getApiKeys();
/** @var Request $request */ $code = $request->get(‘code’);
- $credentials = new class(
‘https://example.com/api/oauth/authorize’, ‘https://example.com/api/oauth/token’, $redirectUrl, ‘scope1,scope2’, $apiKeys[‘client_id’], $apiKeys[‘client_secret’], $code
- ) implements CredentialsInterface, RedirectUriInterface, ScopeInterface, CodeInterface {
private $authorizeUrl; private $tokenUrl; private $redirectUrl; private $scope; private $clientId; private $clientSecret; private $code;
public function __construct(string $authorizeUrl, string $tokenUrl, string $redirectUrl, string $scope, string $clientId, string $clientSecret, ?string $code) {
$this->authorizeUrl = $authorizeUrl; $this->tokenUrl = $tokenUrl; $this->redirectUrl = $redirectUrl; $this->scope = $scope; $this->clientId = $clientId; $this->clientSecret = $clientSecret; $this->code = $code;
}
public function getAuthorizationUrl(): string {
return $this->authorizeUrl;
}
public function getTokenUrl(): string {
return $this->tokenUrl;
}
public function getRedirectUri(): string {
return $this->redirectUrl;
}
public function getClientId(): ?string {
return $this->clientId;
}
public function getClientSecret(): ?string {
return $this->clientSecret;
}
```php public function getScope(): ?string
- {
return $this->scope;
}
public function getCode(): ?string {
return $this->code;
}
};
/** @var $tokenPersistenceFactory TokenPersistenceFactory */ $tokenPersistence = $tokenPersistenceFactory->create($integration); $config = new class($tokenPersistence) implements ConfigTokenPersistenceInterface {
private $tokenPersistence;
public function __construct(TokenPersistenceInterface$tokenPersistence) {
$this->tokenPersistence = $tokenPersistence;
}
public function getTokenPersistence(): TokenPersistenceInterface {
return $this->tokenPersistence;
}
};
/** @var $factory HttpFactory */ $client = $factory->getClient($credentials, $config); $response = $client->get(’https://example.com/api/fetch’);