diff --git a/README.txt b/README.txt
index f689305..7a02f5a 100644
--- a/README.txt
+++ b/README.txt
@@ -203,6 +203,17 @@ On the client website:
    (admin/content/entity_share/pull), and select your remote website, the
    available channels will be listed and when selecting a channel, the entities
    exposed on this channel will be available to synchronize.
+ * Optional Key Module Integration
+   - https://www.drupal.org/project/key
+   - https://www.drupal.org/docs/8/modules/key/concepts-and-terminology
+   Credentials used to authorize pulling from remotes may be more securely stored
+   using the Key module.  Additional optional modules allow the storage in an
+   external key/value storage service.  With only the Key module credentials may
+   be stored in JSON format in files outside the web root.
+   1. Configure Keys: Key types for Entity Share are listed in Key config form
+   (/admin/config/system/keys). Instructions for each type are shown in the form.
+   2. Create a remote and select Key module as the credential provider, then
+   select the appropriate key.
 
 
 TROUBLESHOOTING
diff --git a/modules/entity_share_client/config/schema/remote.schema.yml b/modules/entity_share_client/config/schema/remote.schema.yml
index cd30b16..bd4bdb1 100644
--- a/modules/entity_share_client/config/schema/remote.schema.yml
+++ b/modules/entity_share_client/config/schema/remote.schema.yml
@@ -11,9 +11,28 @@ entity_share_client.remote.*:
     url:
       type: string
       label: 'URL'
-    basic_auth_username:
-      type: string
-      label: 'Basic auth username'
-    basic_auth_password:
-      type: string
-      label: 'Basic auth password'
+    auth:
+      type: mapping
+      mapping:
+        pid:
+          label: 'Plugin ID'
+          type: string
+        uuid:
+            label: 'UUID'
+            type: string
+        verified:
+          label: 'Verified'
+          type: boolean
+        data:
+          type: mapping
+          label: 'Credential data'
+          mapping:
+            credential_provider:
+              type: string
+              label: 'Credential provider'
+            storage_key:
+              type: string
+              label: 'Storage key'
+
+key.type.entity_share_basic_auth:
+  type: sequence
diff --git a/modules/entity_share_client/entity_share_client.install b/modules/entity_share_client/entity_share_client.install
new file mode 100644
index 0000000..5ffe354
--- /dev/null
+++ b/modules/entity_share_client/entity_share_client.install
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall hooks for entity_share_client.
+ */
+
+use Drupal\entity_share_client\Entity\Remote;
+
+/**
+ * Move any basic auth credentials stored in configuration into the new plugin.
+ */
+function entity_share_client_update_8201() {
+  /** @var \Drupal\entity_share_client\Plugin\ClientAuthorizationManager $manager */
+  $manager = \Drupal::service('plugin.manager.entity_share_auth');
+  $state = \Drupal::state();
+  // Iterate remotes.
+  /** @var \Drupal\entity_share_client\Entity\Remote[] $remotes */
+  $remotes = Remote::loadMultiple();
+  foreach ($remotes as $remote) {
+    /** @var \Drupal\entity_share_client\Plugin\ClientAuthorization\BasicAuth $plugin */
+    $plugin = $manager->createInstance('basic_auth');
+    $configuration = $plugin->getConfiguration();
+    $credentials = [];
+    $credentials['username'] = $remote->get('basic_auth_username');
+    $credentials['password'] = $remote->get('basic_auth_password');
+    unset($remote->basic_auth_username);
+    unset($remote->basic_auth_password);
+    $state->set($configuration['uuid'], $credentials);
+    $key = $configuration['uuid'];
+    $configuration['data'] = [
+      'credential_provider' => 'entity_share',
+      'storage_key' => $key,
+    ];
+    $plugin->setConfiguration($configuration);
+    $remote->mergePluginConfig($plugin);
+    $remote->save();
+  }
+}
diff --git a/modules/entity_share_client/entity_share_client.services.yml b/modules/entity_share_client/entity_share_client.services.yml
index 45bc927..222038a 100644
--- a/modules/entity_share_client/entity_share_client.services.yml
+++ b/modules/entity_share_client/entity_share_client.services.yml
@@ -9,7 +9,6 @@ services:
   entity_share_client.remote_manager:
     class: Drupal\entity_share_client\Service\RemoteManager
     arguments:
-      - '@http_client_factory'
       - '@entity_share_client.request'
 
   entity_share_client.jsonapi_helper:
@@ -61,3 +60,13 @@ services:
       - '@entity_share_client.jsonapi_helper'
       - '@entity_type.manager'
       - '@entity_share_client.request'
+
+  entity_share_client.key_provider:
+    class: Drupal\entity_share_client\Service\KeyProvider
+    arguments: ['@state']
+    calls:
+      - [setKeyRepository, ['@?key.repository']]
+
+  plugin.manager.entity_share_auth:
+    class: Drupal\entity_share_client\Plugin\ClientAuthorizationManager
+    parent: default_plugin_manager
diff --git a/modules/entity_share_client/src/Annotation/ClientAuthorization.php b/modules/entity_share_client/src/Annotation/ClientAuthorization.php
new file mode 100644
index 0000000..8a8a5b2
--- /dev/null
+++ b/modules/entity_share_client/src/Annotation/ClientAuthorization.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Drupal\entity_share_client\Annotation;
+
+use Drupal\Component\Annotation\Plugin;
+
+/**
+ * Defines an Entity Share Client authorization annotation object.
+ *
+ * The Entity Share Client must be authorized to pull entities from the
+ * Entity Share Server offering the content.  Such authorization could be
+ * by authenticating as an authorized user or it could be by presententing
+ * some previously created proof of authorization, such as an OAuth2 token.
+ *
+ * @see \Drupal\entity_share_client\Plugin\ClientAuthorizationManager
+ * @see plugin_api
+ *
+ * @Annotation
+ */
+class ClientAuthorization extends Plugin {
+
+
+  /**
+   * The plugin ID.
+   *
+   * A machine name for the authorization type provided by this plugin.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * A human readable name for the authorization type provided by this plugin.
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   *
+   * @ingroup plugin_translatable
+   */
+  public $label;
+
+}
diff --git a/modules/entity_share_client/src/Entity/Remote.php b/modules/entity_share_client/src/Entity/Remote.php
index 30c27af..8e6b82f 100644
--- a/modules/entity_share_client/src/Entity/Remote.php
+++ b/modules/entity_share_client/src/Entity/Remote.php
@@ -6,6 +6,7 @@ namespace Drupal\entity_share_client\Entity;
 
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\entity_share_client\Plugin\ClientAuthorizationInterface;
 
 /**
  * Defines the Remote entity.
@@ -35,8 +36,7 @@ use Drupal\Core\Entity\EntityStorageInterface;
  *     "id",
  *     "label",
  *     "url",
- *     "basic_auth_username",
- *     "basic_auth_password",
+ *     "auth",
  *   },
  *   links = {
  *     "canonical" = "/admin/config/services/entity_share/remote/{remote}",
@@ -71,18 +71,11 @@ class Remote extends ConfigEntityBase implements RemoteInterface {
   protected $url;
 
   /**
-   * The Remote basic auth username.
+   * An associative array of the authorization plugin data.
    *
-   * @var string
-   */
-  protected $basic_auth_username;
-
-  /**
-   * The Remote basic auth password.
-   *
-   * @var string
+   * @var array
    */
-  protected $basic_auth_password;
+  protected $auth;
 
   /**
    * {@inheritdoc}
@@ -97,4 +90,41 @@ class Remote extends ConfigEntityBase implements RemoteInterface {
     }
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getAuthPlugin() {
+    $pluginData = $this->auth;
+    if (!empty($pluginData['pid'])) {
+      // DI not available in entities:
+      // https://www.drupal.org/project/drupal/issues/2142515.
+      /** @var \Drupal\entity_share_client\Plugin\ClientAuthorizationManager $manager */
+      $manager = \Drupal::service('plugin.manager.entity_share_auth');
+      $pluginId = $pluginData['pid'];
+      unset($pluginData['pid']);
+      return $manager->createInstance($pluginId, $pluginData);
+    }
+    return NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function mergePluginConfig(ClientAuthorizationInterface $plugin) {
+    $auth = ['pid' => $plugin->getPluginId()] +
+      $plugin->getConfiguration();
+    $this->auth = $auth;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getHttpClient(bool $json) {
+    $plugin = $this->getAuthPlugin();
+    if ($json) {
+      return $plugin->getJsonApiClient($this->url);
+    }
+    return $plugin->getClient($this->url);
+  }
+
 }
diff --git a/modules/entity_share_client/src/Entity/RemoteInterface.php b/modules/entity_share_client/src/Entity/RemoteInterface.php
index 77d1147..e667f5f 100644
--- a/modules/entity_share_client/src/Entity/RemoteInterface.php
+++ b/modules/entity_share_client/src/Entity/RemoteInterface.php
@@ -5,11 +5,40 @@ declare(strict_types = 1);
 namespace Drupal\entity_share_client\Entity;
 
 use Drupal\Core\Config\Entity\ConfigEntityInterface;
+use Drupal\entity_share_client\Plugin\ClientAuthorizationInterface;
 
 /**
  * Provides an interface for defining Remote entities.
  */
 interface RemoteInterface extends ConfigEntityInterface {
 
-  // Add get/set methods for your configuration properties here.
+  /**
+   * Copies plugin specific data into the Remote.
+   *
+   * @param \Drupal\entity_share_client\Plugin\ClientAuthorizationInterface $plugin
+   *   The authorization plugin to merge.
+   */
+  public function mergePluginConfig(ClientAuthorizationInterface $plugin);
+
+  /**
+   * Helper method to instantiate auth plugin from this entity.
+   *
+   * @return \Drupal\entity_share_client\Plugin\ClientAuthorizationInterface|null
+   *   The plugin if it is defined.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  public function getAuthPlugin();
+
+  /**
+   * Prepares a client object with options pulled from the auth plugin.
+   *
+   * @param bool $json
+   *   Is this client for JSON operations?
+   *
+   * @return \GuzzleHttp\Client
+   *   The configured client.
+   */
+  public function getHttpClient(bool $json);
+
 }
diff --git a/modules/entity_share_client/src/Form/RemoteForm.php b/modules/entity_share_client/src/Form/RemoteForm.php
index 397077b..9660750 100644
--- a/modules/entity_share_client/src/Form/RemoteForm.php
+++ b/modules/entity_share_client/src/Form/RemoteForm.php
@@ -7,6 +7,10 @@ namespace Drupal\entity_share_client\Form;
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Entity\EntityForm;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Form\SubformState;
+use Drupal\Core\Plugin\PluginFormInterface;
+use Drupal\entity_share_client\Plugin\ClientAuthorizationInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Class RemoteForm.
@@ -15,6 +19,29 @@ use Drupal\Core\Form\FormStateInterface;
  */
 class RemoteForm extends EntityForm {
 
+  /**
+   * Injected plugin service.
+   *
+   * @var \Drupal\entity_share_client\Plugin\ClientAuthorizationManager
+   */
+  protected $authPluginManager;
+
+  /**
+   * The currently configured auth plugin.
+   *
+   * @var \Drupal\entity_share_client\Plugin\ClientAuthorizationInterface
+   */
+  protected $authPlugin;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    $instance = parent::create($container);
+    $instance->authPluginManager = $container->get('plugin.manager.entity_share_auth');
+    return $instance;
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -36,6 +63,7 @@ class RemoteForm extends EntityForm {
       '#type' => 'machine_name',
       '#default_value' => $remote->id(),
       '#machine_name' => [
+        'source' => ['label'],
         'exists' => '\Drupal\entity_share_client\Entity\Remote::load',
       ],
       '#disabled' => !$remote->isNew(),
@@ -50,23 +78,7 @@ class RemoteForm extends EntityForm {
       '#required' => TRUE,
     ];
 
-    $form['basic_auth'] = [
-      '#type' => 'fieldset',
-      '#title' => $this->t('Basic Auth'),
-    ];
-
-    $form['basic_auth']['basic_auth_username'] = [
-      '#type' => 'textfield',
-      '#title' => $this->t('Username'),
-      '#default_value' => $remote->get('basic_auth_username'),
-      '#required' => TRUE,
-    ];
-
-    $form['basic_auth']['basic_auth_password'] = [
-      '#type' => 'password',
-      '#title' => $this->t('Password'),
-      '#required' => TRUE,
-    ];
+    $this->addAuthOptions($form, $form_state);
 
     return $form;
   }
@@ -79,6 +91,26 @@ class RemoteForm extends EntityForm {
     if (!UrlHelper::isValid($form_state->getValue('url'), TRUE)) {
       $form_state->setError($form['url'], $this->t('Invalid URL.'));
     }
+    $selectedPlugin = $this->getSelectedPlugin($form, $form_state);
+    if ($selectedPlugin instanceof PluginFormInterface) {
+      $subformState = SubformState::createForSubform($form['auth']['data'], $form, $form_state);
+      $selectedPlugin->validateConfigurationForm($form['auth']['data'], $subformState);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    parent::submitForm(
+      $form,
+      $form_state
+    );
+    $selectedPlugin = $this->getSelectedPlugin($form, $form_state);
+    $subformState = SubformState::createForSubform($form['auth']['data'], $form, $form_state);
+    // Store the remote entity in case the plugin submission needs its data.
+    $subformState->set('remote', $this->entity);
+    $selectedPlugin->submitConfigurationForm($form['auth']['data'], $subformState);
   }
 
   /**
@@ -87,6 +119,12 @@ class RemoteForm extends EntityForm {
   public function save(array $form, FormStateInterface $form_state) {
     /** @var \Drupal\entity_share_client\Entity\RemoteInterface $remote */
     $remote = $this->entity;
+
+    if (!empty($form['auth']['#plugins'])) {
+      $selectedPlugin = $this->getSelectedPlugin($form, $form_state);
+      $remote->mergePluginConfig($selectedPlugin);
+    }
+
     $status = $remote->save();
 
     switch ($status) {
@@ -104,4 +142,120 @@ class RemoteForm extends EntityForm {
     $form_state->setRedirectUrl($remote->toUrl('collection'));
   }
 
+  /**
+   * Helper function to build the authorization options in the form.
+   *
+   * @param array $form
+   *   The form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current form state.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  protected function addAuthOptions(array &$form, FormStateInterface $form_state) {
+    $options = [];
+    $plugins = [];
+    if ($this->getAuthPlugin()) {
+      $options[$this->authPlugin->getPluginId()] = $this->authPlugin->getLabel();
+      $plugins[$this->authPlugin->getPluginId()] = $this->authPlugin;
+    }
+    $availablePlugins = $this->authPluginManager->getAvailablePlugins();
+    foreach ($availablePlugins as $id => $plugin) {
+      if (empty($options[$id])) {
+        // This plugin type was not previously set as an option.
+        /** @var \Drupal\entity_share_client\Plugin\ClientAuthorizationInterface $plugin */
+        $options[$id] = $plugin->getLabel();
+        $plugins[$id] = $plugin;
+      }
+    }
+    // Do we have a value?
+    $selected = $form_state->getValue('pid');
+    if (!empty($selected)) {
+      $selectedPlugin = $plugins[$selected];
+    }
+    elseif (!empty($this->authPlugin)) {
+      // Is a plugin previously stored?
+      $selectedPlugin = $this->authPlugin;
+    }
+    else {
+      // Fallback: take the first option.
+      $selectedPlugin = reset($plugins);
+    }
+    $form['auth'] = [
+      '#type' => 'container',
+      '#plugins' => $plugins,
+      'pid' => [
+        '#type' => 'radios',
+        '#title' => $this->t('Authorization methods'),
+        '#options' => $options,
+        '#default_value' => $selectedPlugin->getPluginId(),
+        '#ajax' => [
+          'wrapper' => 'plugin-form-ajax-container',
+          'callback' => [get_class($this), 'ajaxPluginForm'],
+        ],
+      ],
+      'data' => [],
+    ];
+    $subformState = SubformState::createForSubform($form['auth']['data'], $form, $form_state);
+    $form['auth']['data'] = $selectedPlugin->buildConfigurationForm($form['auth']['data'], $subformState);
+    $form['auth']['data']['#tree'] = TRUE;
+    $form['auth']['data']['#prefix'] = '<div id="plugin-form-ajax-container">';
+    $form['auth']['data']['#suffix'] = '</div>';
+  }
+
+  /**
+   * Callback function to return the credentials portion of the form.
+   *
+   * @param array $form
+   *   The rebuilt form.
+   * @param \Drupal\Core\Form\FormStateInterface $formState
+   *   The current form state.
+   *
+   * @return array
+   *   A portion of the render array.
+   */
+  public static function ajaxPluginForm(array $form, FormStateInterface $formState) {
+    return $form['auth']['data'];
+  }
+
+  /**
+   * Helper method to instantiate plugin from this entity.
+   *
+   * @return bool
+   *   The Remote entity has a plugin.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  protected function getAuthPlugin() {
+    /** @var \Drupal\entity_share_client\Entity\RemoteInterface $remote */
+    $remote = $this->entity;
+    $plugin = $remote->getAuthPlugin();
+    if ($plugin instanceof ClientAuthorizationInterface) {
+      $this->authPlugin = $plugin;
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * Helper method to get selected plugin from the form.
+   *
+   * @param array $form
+   *   The form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current form state.
+   *
+   * @return \Drupal\entity_share_client\Plugin\ClientAuthorizationInterface
+   *   The selected plugin.
+   */
+  protected function getSelectedPlugin(
+    array &$form,
+    FormStateInterface $form_state) {
+    $authPluginId = $form_state->getValue('pid');
+    $plugins = $form['auth']['#plugins'];
+    /** @var \Drupal\entity_share_client\Plugin\ClientAuthorizationInterface $selectedPlugin */
+    $selectedPlugin = $plugins[$authPluginId];
+    return $selectedPlugin;
+  }
+
 }
diff --git a/modules/entity_share_client/src/Plugin/ClientAuthorization/BasicAuth.php b/modules/entity_share_client/src/Plugin/ClientAuthorization/BasicAuth.php
new file mode 100644
index 0000000..f8bc191
--- /dev/null
+++ b/modules/entity_share_client/src/Plugin/ClientAuthorization/BasicAuth.php
@@ -0,0 +1,95 @@
+<?php
+
+namespace Drupal\entity_share_client\Plugin\ClientAuthorization;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\entity_share_client\Plugin\ClientAuthorizationBase;
+
+/**
+ * Provides Basic Auth based client authorization.
+ *
+ * @ClientAuthorization(
+ *   id = "basic_auth",
+ *   label = @Translation("Basic Auth"), * )
+ */
+class BasicAuth extends ClientAuthorizationBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function checkIfAvailable() {
+    // Basic Auth is a core module which any server can enable.
+    return TRUE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getClient($url) {
+    $credentials = $this->keyService->getCredentials($this);
+    $http_client = $this->httpClientFactory->fromOptions([
+      'base_uri' => $url . '/',
+      'cookies' => TRUE,
+      'allow_redirects' => TRUE,
+    ]);
+
+    $http_client->post('/user/login', [
+      'form_params' => [
+        'name' => $credentials['username'],
+        'pass' => $credentials['password'],
+        'form_id' => 'user_login_form',
+      ],
+    ]);
+
+    return $http_client;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getJsonApiClient($url) {
+    $credentials = $this->keyService->getCredentials($this);
+    return $this->httpClientFactory->fromOptions([
+      'base_uri' => $url . '/',
+      'auth' => [
+        $credentials['username'],
+        $credentials['password'],
+      ],
+      'headers' => [
+        'Content-type' => 'application/vnd.api+json',
+      ],
+    ]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(
+    array $form,
+    FormStateInterface $form_state
+  ) {
+    $form = parent::buildConfigurationForm($form, $form_state);
+
+    $credentials = $this->keyService->getCredentials($this);
+    $form['entity_share']['username'] = [
+      '#type' => 'textfield',
+      '#required' => FALSE,
+      '#title' => $this->t('Username'),
+      '#default_value' => $credentials['username'] ?? '',
+    ];
+
+    $form['entity_share']['password'] = [
+      '#type' => 'textfield',
+      '#required' => FALSE,
+      '#title' => $this->t('Password'),
+      '#default_value' => $credentials['password'] ?? '',
+    ];
+    if ($this->keyService->additionalProviders()) {
+      $this->expandedProviderOptions($form);
+      $form['key']['id']['#key_filters'] = ['type' => 'entity_share_basic_auth'];
+      $form['key']['id']['#description'] = $this->t('Select the key you have configured to hold the Basic Auth credentials.');
+    }
+    return $form;
+  }
+
+}
diff --git a/modules/entity_share_client/src/Plugin/ClientAuthorization/Oauth.php b/modules/entity_share_client/src/Plugin/ClientAuthorization/Oauth.php
new file mode 100644
index 0000000..bb20472
--- /dev/null
+++ b/modules/entity_share_client/src/Plugin/ClientAuthorization/Oauth.php
@@ -0,0 +1,328 @@
+<?php
+
+namespace Drupal\entity_share_client\Plugin\ClientAuthorization;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\entity_share_client\Entity\Remote;
+use League\OAuth2\Client\Provider\GenericProvider;
+use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
+use League\OAuth2\Client\Token\AccessTokenInterface;
+use Drupal\entity_share_client\Plugin\ClientAuthorizationBase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides Oauth2 based client authorization.
+ *
+ * The entity share server needs to be configured with an Oauth scope or role
+ * with permission entity_share_server_access_channels.
+ *
+ * @ClientAuthorization(
+ *   id = "oauth",
+ *   label = @Translation("Oauth2"), * )
+ */
+class Oauth extends ClientAuthorizationBase {
+
+  /**
+   * Injected module service.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandler
+   */
+  protected $moduleHandler;
+
+  /**
+   * Injected messenger service.
+   *
+   * @var \Drupal\Core\Messenger\MessengerInterface
+   */
+  protected $messenger;
+
+  /**
+   * Injected service.
+   *
+   * @var \Psr\Log\LoggerInterface
+   */
+  protected $logger;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(
+    ContainerInterface $container,
+    array $configuration,
+    $plugin_id,
+    $plugin_definition
+  ) {
+    /** @var \Drupal\entity_share_client\Plugin\ClientAuthorization\Oauth $instance */
+    $instance = parent::create(
+      $container,
+      $configuration,
+      $plugin_id,
+      $plugin_definition
+    );
+    $instance->moduleHandler = $container->get('module_handler');
+    $instance->messenger = $container->get('messenger');
+    $instance->logger = $container->get('logger.channel.entity_share_client');
+    return $instance;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function checkIfAvailable() {
+    /*
+     * The oauth2_client client module currently places client_secret in
+     * code annotations, which doesn't seem secure.  But it also has a composer
+     * dependency on League\OAuth2\Client so for the moment we use that package
+     * directly.
+     */
+      return $this->moduleHandler->moduleExists('oauth2_client');
+  }
+
+  /**
+   * Obtains the stored or renewed access token based on expiration state.
+   *
+   * @param string $url
+   *   The url to the remote.
+   *
+   * @return string
+   *   The token value.
+   *
+   * @throws \Exception
+   */
+  protected function getAccessToken($url) {
+    $configuration = $this->getConfiguration();
+    $accessToken = $this->state->get($configuration['uuid'] . '-oauth');
+    $credentials = $this->keyService->getCredentials($this);
+    if ($accessToken instanceof AccessTokenInterface) {
+      if ($accessToken->hasExpired()) {
+        // Get the oauth client:
+        $oauthClient = new GenericProvider(
+          [
+            'clientId' => $credentials['client_id'],
+            'clientSecret' => $credentials['client_secret'],
+            'urlAuthorize' => $url . $credentials['authorization_path'],
+            'urlAccessToken' => $url . $credentials['token_path'],
+            'urlResourceOwnerDetails' => '',
+          ]
+        );
+        // Try to get an access token using the authorization code grant.
+        try {
+          $newAccessToken = $oauthClient->getAccessToken(
+            'refresh_token',
+            [
+              'refresh_token' => $accessToken->getRefreshToken(),
+            ]
+          );
+          $this->state->set($configuration['uuid'] . '-oauth', $newAccessToken);
+        }
+        catch (\Exception $e) {
+          $this->logger->critical(
+            'Entity Share new oauth token request failed with Exception: %exception_type and error: %error.',
+            [
+              '%exception_type' => get_class($e),
+              '%error' => $e->getMessage()
+            ]
+          );
+          throw $e;
+        }
+        return $newAccessToken->getToken();
+      }
+      return $accessToken->getToken();
+    }
+    throw new \UnexpectedValueException('Access Token object not found in storage');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getClient($url) {
+    $token = $this->getAccessToken($url);
+    return $this->httpClientFactory->fromOptions(
+      [
+        'base_uri' => $url . '/',
+        'allow_redirects' => TRUE,
+        'headers' => [
+          'Authorization' => 'Bearer ' . $token,
+        ],
+      ]
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getJsonApiClient($url) {
+    $token = $this->getAccessToken($url);
+    return $this->httpClientFactory->fromOptions(
+      [
+        'base_uri' => $url . '/',
+        'headers' => [
+          'Content-type' => 'application/vnd.api+json',
+          'Authorization' => 'Bearer ' . $token,
+        ],
+      ]
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(
+    array $form,
+    FormStateInterface $form_state
+  ) {
+    $form = parent::buildConfigurationForm($form, $form_state);
+    $form['entity_share']['#title'] = $this->t(
+      'Oauth using <em>Resource owner password credentials</em> grant'
+    );
+    $form['entity_share']['#description'] = $this->t(
+      'A token will be requested and saved in State storage when this form is submitted. The username and password entered here are not saved, but are only used to request the token.'
+    );
+    $form['entity_share']['username'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Username'),
+    ];
+
+    $form['entity_share']['password'] = [
+      '#type' => 'password',
+      '#title' => $this->t('Password'),
+    ];
+
+    $credentials = $this->keyService->getCredentials($this);
+    $form['entity_share']['client_id'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Client ID'),
+      '#default_value' => $credentials['client_id'] ?? '',
+    ];
+    $form['entity_share']['client_secret'] = [
+      '#type' => 'password',
+      '#title' => $this->t('Client secret'),
+    ];
+    $form['entity_share']['authorization_path'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Authorization path on the remote'),
+      '#default_value' => $credentials['authorization_path'] ?? '/oauth/token',
+    ];
+    $form['entity_share']['token_path'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Token path on the remote'),
+      '#default_value' => $credentials['token_path'] ?? '/oauth/token',
+    ];
+    if ($this->keyService->additionalProviders()) {
+      $this->expandedProviderOptions($form);
+      // Username and password also needed if Key module is involved.
+      $form['key']['#description'] = $this->t(
+        'A token will be requested and saved in State storage when this form is submitted. The username and password entered here are not saved, but are only used to request the token.'
+      );
+      $form['key']['username'] = [
+        '#type' => 'textfield',
+        '#title' => $this->t('Username'),
+      ];
+
+      $form['key']['password'] = [
+        '#type' => 'password',
+        '#title' => $this->t('Password'),
+      ];
+      $form['key']['id']['#key_filters'] = ['type' => 'entity_share_oauth'];
+      $form['key']['id']['#description'] = $this->t('Select the key you have configured to hold the Oauth credentials.');
+    }
+    return $form;
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitConfigurationForm(
+    array &$form,
+    FormStateInterface $form_state
+  ) {
+    $values = $form_state->getValues();
+    $configuration = $this->getConfiguration();
+    /** @var \Drupal\entity_share_client\Entity\Remote $remote */
+    $remote = $form_state->get('remote');
+    $resetConfiguration = $configuration;
+    $provider = $values['credential_provider'];
+    $credentials = $values[$provider];
+    $key = $configuration['uuid'];
+    if ($provider == 'key') {
+      $key = $credentials['id'];
+    }
+    $configuration['data'] = [
+      'credential_provider' => $provider,
+      'storage_key' => $key,
+    ];
+    $this->setConfiguration($configuration);
+    try {
+      // Try to obtain a token.
+      switch ($provider) {
+        case 'key':
+          $requestCredentials = $this->keyService->getCredentials($this);
+          $requestCredentials['username'] = $credentials['username'];
+          $requestCredentials['password'] = $credentials['password'];
+          $accessToken = $this->initalizeToken($remote, $requestCredentials);
+          $this->state->delete($configuration['uuid']);
+
+          break;
+
+        default:
+          $accessToken = $this->initalizeToken($remote, $credentials);
+          // Remove the username and password.
+          unset($credentials['username']);
+          unset($credentials['password']);
+          $this->state->set($configuration['uuid'], $credentials);
+      }
+
+      // Save the token.
+      $this->state->set($configuration['uuid'] . '-oauth', $accessToken);
+
+      $this->messenger->addStatus(
+        $this->t('OAuth token obtained from remote and stored.')
+      );
+    }
+    catch (IdentityProviderException $e) {
+      // Failed to get the access token.
+      // Reset original configuration.
+      $this->setConfiguration($resetConfiguration);
+      $this->messenger->addError(
+        $this->t(
+          'Unable to obtain an OAuth token.  Error message is: @message',
+          ['@message' => $e->getMessage()]
+        )
+      );
+    }
+  }
+
+  /**
+   * @param \Drupal\entity_share_client\Entity\Remote $remote
+   *   The remote for which authorization is needed.
+   * @param array $credentials
+   *   Trial credentials.
+   *
+   * @return AccessTokenInterface
+   *   A valid access token.
+   *
+   * @throws \League\OAuth2\Client\Provider\Exception\IdentityProviderException
+   */
+  protected function initalizeToken(Remote $remote, array $credentials) {
+    $oauthClient = new GenericProvider(
+      [
+        'clientId' => $credentials['client_id'],
+        'clientSecret' => $credentials['client_secret'],
+        'urlAuthorize' => $remote->get('url') . $credentials['authorization_path'],
+        'urlAccessToken' => $remote->get('url') . $credentials['token_path'],
+        'urlResourceOwnerDetails' => '',
+      ]
+    );
+    // Try to get an access token using the
+    // resource owner password credentials grant.
+    return $oauthClient->getAccessToken(
+      'password',
+      [
+        'username' => $credentials['username'],
+        'password' => $credentials['password'],
+      ]
+    );
+  }
+
+}
diff --git a/modules/entity_share_client/src/Plugin/ClientAuthorizationBase.php b/modules/entity_share_client/src/Plugin/ClientAuthorizationBase.php
new file mode 100644
index 0000000..3e51127
--- /dev/null
+++ b/modules/entity_share_client/src/Plugin/ClientAuthorizationBase.php
@@ -0,0 +1,275 @@
+<?php
+
+namespace Drupal\entity_share_client\Plugin;
+
+use Drupal\Component\Uuid\UuidInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Plugin\PluginBase;
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\State\StateInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\entity_share_client\Service\KeyProvider;
+use Drupal\Core\Http\ClientFactory;
+
+/**
+ * Base class for Client authorization plugins.
+ */
+abstract class ClientAuthorizationBase extends PluginBase implements ClientAuthorizationInterface, ContainerFactoryPluginInterface {
+
+  /**
+   * Injected key service.
+   *
+   * @var \Drupal\entity_share_client\Service\KeyProvider
+   */
+  protected $keyService;
+
+  /**
+   * Injected state service.
+   *
+   * @var \Drupal\Core\State\StateInterface
+   */
+  protected $state;
+
+  /**
+   * Injected UUID service.
+   *
+   * @var \Drupal\Component\Uuid\UuidInterface
+   */
+  protected $uuid;
+
+  /**
+   * Injected HTTP client factory.
+   *
+   * @var \Drupal\Core\Http\ClientFactory
+   */
+  protected $httpClientFactory;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct(
+    array $configuration,
+    $plugin_id,
+    $plugin_definition,
+    KeyProvider $keyProvider,
+    StateInterface $state,
+    UuidInterface $uuid,
+    ClientFactory $clientFactory
+  ) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->keyService = $keyProvider;
+    $this->state = $state;
+    $this->uuid = $uuid;
+    $this->httpClientFactory = $clientFactory;
+    $this->setConfiguration($configuration);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(
+    ContainerInterface $container,
+    array $configuration,
+    $plugin_id,
+    $plugin_definition
+  ) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('entity_share_client.key_provider'),
+      $container->get('state'),
+      $container->get('uuid'),
+      $container->get('http_client_factory')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [
+      'uuid' => $this->uuid->generate(),
+      'verified' => FALSE,
+      'data' => [],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfiguration() {
+    return $this->configuration;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setConfiguration(array $configuration) {
+    $this->configuration = NestedArray::mergeDeep(
+      $this->defaultConfiguration(),
+      $configuration
+    );
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCredentialProvider() {
+    $configuration = $this->getConfiguration();
+    return $configuration['data']['credential_provider'] ?? NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getStorageKey() {
+    $configuration = $this->getConfiguration();
+    return $configuration['data']['storage_key'] ?? NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(
+    array $form,
+    FormStateInterface $form_state
+  ) {
+    return $form + [
+      'credential_provider' => [
+        '#type' => 'hidden',
+        '#value' => 'entity_share',
+      ],
+      'entity_share' => [
+        '#type' => 'fieldset',
+        '#title' => $this->t('Stored in local State'),
+      ],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLabel() {
+    return $this->pluginDefinition['label'];
+  }
+
+  /**
+   * Helper method to build the credential provider elements of the form.
+   *
+   * @param array $form
+   *   The configuration form.
+   */
+  protected function expandedProviderOptions(array &$form) {
+    $provider = $this->getCredentialProvider();
+    // Provide selectors for the api key credential provider.
+    $form['credential_provider'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Credential provider'),
+      '#default_value' => empty($provider) ? 'entity_share' : $provider,
+      '#options' => [
+        'entity_share' => $this->t('Local Storage'),
+        'key' => $this->t('Key Module'),
+      ],
+      '#attributes' => [
+        'data-states-selector' => 'provider',
+      ],
+      '#weight' => -99,
+    ];
+    $form['entity_share']['#states'] = [
+      'required' => [
+        ':input[data-states-selector="provider"]' => ['value' => 'entity_share'],
+      ],
+      'visible' => [
+        ':input[data-states-selector="provider"]' => ['value' => 'entity_share'],
+      ],
+      'enabled' => [
+        ':input[data-states-selector="provider"]' => ['value' => 'entity_share'],
+      ],
+    ];
+    $key_id = $provider == 'key' ? $this->getStorageKey() : '';
+    $form['key'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t('Managed by Key module'),
+      '#states' => [
+        'required' => [
+          ':input[data-states-selector="provider"]' => ['value' => 'key'],
+        ],
+        'visible' => [
+          ':input[data-states-selector="provider"]' => ['value' => 'key'],
+        ],
+        'enabled' => [
+          ':input[data-states-selector="provider"]' => ['value' => 'key'],
+        ],
+      ],
+    ];
+    $form['key']['id'] = [
+      '#type' => 'key_select',
+      '#title' => $this->t('Select a Stored Key'),
+      '#default_value' => $key_id,
+      '#empty_option' => $this->t('- Please select -'),
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateConfigurationForm(
+    array &$form,
+    FormStateInterface $form_state
+  ) {
+    $values = $form_state->getValues();
+    if (empty($values['credential_provider'])) {
+      $form_state->setError(
+        $form['credential_provider'],
+        'A credential provider is required.'
+      );
+    }
+    else {
+      $provider = $values['credential_provider'];
+      foreach ($values[$provider] as $key => $value) {
+        if (empty($value)) {
+          $form_state->setError(
+            $form[$provider][$key],
+            'All credential values are required.'
+          );
+        }
+      }
+    }
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitConfigurationForm(
+    array &$form,
+    FormStateInterface $form_state
+  ) {
+    $key = NULL;
+    $values = $form_state->getValues();
+    $configuration = $this->getConfiguration();
+    $provider = $values['credential_provider'];
+    $credentials = $values[$provider];
+    switch ($provider) {
+      case 'entity_share':
+        $this->state->set($configuration['uuid'], $credentials);
+        $key = $configuration['uuid'];
+        break;
+
+      case 'key':
+        $this->state->delete($configuration['uuid']);
+        $key = $credentials['id'];
+        break;
+
+    }
+    $configuration['data'] = [
+      'credential_provider' => $provider,
+      'storage_key' => $key,
+    ];
+    $this->setConfiguration($configuration);
+  }
+
+}
diff --git a/modules/entity_share_client/src/Plugin/ClientAuthorizationInterface.php b/modules/entity_share_client/src/Plugin/ClientAuthorizationInterface.php
new file mode 100644
index 0000000..4c7ed5e
--- /dev/null
+++ b/modules/entity_share_client/src/Plugin/ClientAuthorizationInterface.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Drupal\entity_share_client\Plugin;
+
+use Drupal\Component\Plugin\ConfigurableInterface;
+use Drupal\Component\Plugin\PluginInspectionInterface;
+use Drupal\Core\Plugin\PluginFormInterface;
+
+/**
+ * Defines an interface for Client authorization plugins.
+ */
+interface ClientAuthorizationInterface extends PluginInspectionInterface, PluginFormInterface, ConfigurableInterface {
+
+  /**
+   * Gets the plugin label.
+   *
+   * @return string
+   *   The plugin label.
+   */
+  public function getLabel();
+
+  /**
+   * Returns true if the plugin method is supported.
+   *
+   * The method could be in core, or it could require a contrib module.
+   *
+   * @return bool
+   *   Is this plugin available?
+   */
+  public function checkIfAvailable();
+
+  /**
+   * Prepares a guzzle client for JSON operations with the supported auth.
+   *
+   * @param string $url
+   *   The remote url.
+   *
+   * @return \GuzzleHttp\Client
+   *   The HTTP client.
+   */
+  public function getJsonApiClient($url);
+
+  /**
+   * Prepares a guzzle client for http operations with the supported auth.
+   *
+   * @param string $url
+   *   The url to set in the client.
+   *
+   * @return \GuzzleHttp\Client
+   *   The HTTP client.
+   */
+  public function getClient($url);
+
+  /**
+   * Returns the plugin data if it is set, otherwise returns NULL.
+   *
+   * @return string|null
+   *   The data.
+   */
+  public function getCredentialProvider();
+
+  /**
+   * Returns the plugin data if it is set, otherwise returns NULL.
+   *
+   * @return mixed|null
+   *   The data.
+   */
+  public function getStorageKey();
+
+}
diff --git a/modules/entity_share_client/src/Plugin/ClientAuthorizationManager.php b/modules/entity_share_client/src/Plugin/ClientAuthorizationManager.php
new file mode 100644
index 0000000..4d3b7be
--- /dev/null
+++ b/modules/entity_share_client/src/Plugin/ClientAuthorizationManager.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Drupal\entity_share_client\Plugin;
+
+use Drupal\Core\Plugin\DefaultPluginManager;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+
+/**
+ * Provides the Client authorization plugin manager.
+ */
+class ClientAuthorizationManager extends DefaultPluginManager {
+
+  /**
+   * Constructs a new ClientAuthorizationManager object.
+   *
+   * @param \Traversable $namespaces
+   *   An object that implements \Traversable which contains the root paths
+   *   keyed by the corresponding namespace to look for plugin implementations.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   Cache backend instance to use.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler to invoke the alter hook with.
+   */
+  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
+    parent::__construct('Plugin/ClientAuthorization', $namespaces, $module_handler, 'Drupal\entity_share_client\Plugin\ClientAuthorizationInterface', 'Drupal\entity_share_client\Annotation\ClientAuthorization');
+
+    $this->alterInfo('entity_share_client_entity_share_auth_info');
+    $this->setCacheBackend($cache_backend, 'entity_share_client_entity_share_auth_plugins');
+  }
+
+  /**
+   * Builds an array of currently available plugin instances.
+   *
+   * @return \Drupal\entity_share_client\Plugin\ClientAuthorizationInterface[]
+   *   The array of plugins.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  public function getAvailablePlugins() {
+    $plugins = [];
+    $definitions = $this->getDefinitions();
+    foreach ($definitions as $definition) {
+      /** @var \Drupal\entity_share_client\Plugin\ClientAuthorizationInterface $plugin */
+      $plugin = $this->createInstance($definition['id']);
+      if ($plugin->checkIfAvailable()) {
+        $plugins[$plugin->getPluginId()] = $plugin;
+      }
+    }
+    return $plugins;
+  }
+
+}
diff --git a/modules/entity_share_client/src/Plugin/KeyType/EntityShareBasicAuth.php b/modules/entity_share_client/src/Plugin/KeyType/EntityShareBasicAuth.php
new file mode 100644
index 0000000..02017e4
--- /dev/null
+++ b/modules/entity_share_client/src/Plugin/KeyType/EntityShareBasicAuth.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Drupal\entity_share_client\Plugin\KeyType;
+
+use Drupal\key\Plugin\KeyType\AuthenticationMultivalueKeyType;
+
+/**
+ * Key module plugin to define a basic_auth credentials KeyType.
+ *
+ * @KeyType(
+ *   id = "entity_share_basic_auth",
+ *   label = @Translation("Entity Share Basic Auth"),
+ *   description = @Translation("A key type to store Basic Auth credentials for the Entity Share module. Store as JSON:<br><pre>{<br>&quot;username&quot;: &quot;username value&quot;,<br>&quot;password&quot;: &quot;password value&quot;<br>}</pre>"),
+ *   group = "authentication",
+ *   key_value = {
+ *     "plugin" = "textarea_field"
+ *   },
+ *   multivalue = {
+ *     "enabled" = true,
+ *     "fields" = {
+ *       "username" = @Translation("Username"),
+ *       "password" = @Translation("Password")
+ *     }
+ *   }
+ * )
+ */
+class EntityShareBasicAuth extends AuthenticationMultivalueKeyType {
+
+}
diff --git a/modules/entity_share_client/src/Plugin/KeyType/EntityShareOauth.php b/modules/entity_share_client/src/Plugin/KeyType/EntityShareOauth.php
new file mode 100644
index 0000000..8b08670
--- /dev/null
+++ b/modules/entity_share_client/src/Plugin/KeyType/EntityShareOauth.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace Drupal\entity_share_client\Plugin\KeyType;
+
+use Drupal\key\Plugin\KeyType\AuthenticationMultivalueKeyType;
+
+/**
+ * Key module plugin to define an oauth credentials KeyType.
+ *
+ * @KeyType(
+ *   id = "entity_share_oauth",
+ *   label = @Translation("Entity Share Oauth"),
+ *   description = @Translation("A key type to store oauth credentials for the Entity Share module. Store as JSON:<br><pre>{<br>&quot;client_id&quot;: &quot;client_id value&quot;,<br>&quot;client_secret&quot;: &quot;client_secret value&quot;<br>,<br>&quot;authorization_path&quot;: &quot;authorization_path value&quot;<br>,<br>&quot;token_path&quot;: &quot;token_path value&quot;<br>}</pre>"),
+ *   group = "authentication",
+ *   key_value = {
+ *     "plugin" = "textarea_field"
+ *   },
+ *   multivalue = {
+ *     "enabled" = true,
+ *     "fields" = {
+ *       "client_id" = @Translation("Client ID"),
+ *       "client_secret" = @Translation("Client Secret"),
+ *       "authorization_path" = @Translation("Authorization Path"),
+ *       "token_path" = @Translation("Token Path")
+ *     }
+ *   }
+ * )
+ */
+class EntityShareOauth extends AuthenticationMultivalueKeyType {
+
+}
diff --git a/modules/entity_share_client/src/Service/KeyProvider.php b/modules/entity_share_client/src/Service/KeyProvider.php
new file mode 100644
index 0000000..b0bf7c5
--- /dev/null
+++ b/modules/entity_share_client/src/Service/KeyProvider.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace Drupal\entity_share_client\Service;
+
+use Drupal\key\Entity\Key;
+use Drupal\Core\State\StateInterface;
+use Drupal\entity_share_client\Plugin\ClientAuthorizationInterface;
+use Drupal\key\KeyRepositoryInterface;
+
+/**
+ * Class KeyProvider.
+ *
+ * @package Drupal\entity_share_client\Service
+ */
+class KeyProvider {
+
+  /**
+   * Key module service conditionally injected.
+   *
+   * @var \Drupal\key\KeyRepositoryInterface
+   */
+  protected $keyRepository;
+
+  /**
+   * Injected service.
+   *
+   * @var \Drupal\Core\State\StateInterface
+   */
+  protected $state;
+
+  /**
+   * KeyService constructor.
+   *
+   * @param \Drupal\Core\State\StateInterface $state
+   *   Injected service.
+   */
+  public function __construct(StateInterface $state) {
+    $this->state = $state;
+  }
+
+  /**
+   * Provides a means to our services.yml file to conditionally inject service.
+   *
+   * @param \Drupal\key\KeyRepositoryInterface $repository
+   *   The injected service, if it exists.
+   *
+   * @see maw_luminate.services.yml
+   */
+  public function setKeyRepository(KeyRepositoryInterface $repository) {
+    $this->keyRepository = $repository;
+  }
+
+  /**
+   * Detects if key module service was injected.
+   *
+   * @return bool
+   *   True if the KeyRepository is present.
+   */
+  public function additionalProviders() {
+    return $this->keyRepository instanceof KeyRepositoryInterface;
+  }
+
+  /**
+   * Get the provided credentials.
+   *
+   * @param \Drupal\entity_share_client\Plugin\ClientAuthorizationInterface $plugin
+   *   An authorization plugin.
+   *
+   * @return array|string
+   *   The value of the configured key.
+   */
+  public function getCredentials(ClientAuthorizationInterface $plugin) {
+    $provider = $plugin->getCredentialProvider();
+    $credentials = [];
+    if (empty($provider)) {
+      return $credentials;
+    }
+    switch ($provider) {
+      case 'key':
+        $keyEntity = $this->keyRepository->getKey($plugin->getStorageKey());
+        if ($keyEntity instanceof Key) {
+          // A key was found in the repository.
+          $credentials = $keyEntity->getKeyValues();
+        }
+        break;
+
+      default:
+        $credentials = $this->state->get($plugin->getStorageKey());
+    }
+
+    return $credentials;
+  }
+
+}
diff --git a/modules/entity_share_client/src/Service/RemoteManager.php b/modules/entity_share_client/src/Service/RemoteManager.php
index 405b7aa..74a445e 100644
--- a/modules/entity_share_client/src/Service/RemoteManager.php
+++ b/modules/entity_share_client/src/Service/RemoteManager.php
@@ -5,7 +5,6 @@ declare(strict_types = 1);
 namespace Drupal\entity_share_client\Service;
 
 use Drupal\Component\Serialization\Json;
-use Drupal\Core\Http\ClientFactory;
 use Drupal\entity_share_client\Entity\RemoteInterface;
 
 /**
@@ -16,11 +15,18 @@ use Drupal\entity_share_client\Entity\RemoteInterface;
 class RemoteManager implements RemoteManagerInterface {
 
   /**
-   * The HTTP client factory.
+   * A constant to document the call for a standard client.
    *
-   * @var \Drupal\Core\Http\ClientFactory
+   * @var bool
    */
-  protected $httpClientFactory;
+  const STANDARD_CLIENT = FALSE;
+
+  /**
+   * A constant to document the call for a JSON:API client.
+   *
+   * @var bool
+   */
+  const JSON_API_CLIENT = TRUE;
 
   /**
    * The request service.
@@ -32,16 +38,12 @@ class RemoteManager implements RemoteManagerInterface {
   /**
    * RemoteManager constructor.
    *
-   * @param \Drupal\Core\Http\ClientFactory $http_client_factory
-   *   The HTTP client factory.
    * @param \Drupal\entity_share_client\Service\RequestServiceInterface $request_service
    *   The request service.
    */
   public function __construct(
-    ClientFactory $http_client_factory,
     RequestServiceInterface $request_service
   ) {
-    $this->httpClientFactory = $http_client_factory;
     $this->requestService = $request_service;
   }
 
@@ -49,37 +51,14 @@ class RemoteManager implements RemoteManagerInterface {
    * {@inheritdoc}
    */
   public function prepareClient(RemoteInterface $remote) {
-    $http_client = $this->httpClientFactory->fromOptions([
-      'base_uri' => $remote->get('url') . '/',
-      'cookies' => TRUE,
-      'allow_redirects' => TRUE,
-    ]);
-
-    $http_client->post('user/login', [
-      'form_params' => [
-        'name' => $remote->get('basic_auth_username'),
-        'pass' => $remote->get('basic_auth_password'),
-        'form_id' => 'user_login_form',
-      ],
-    ]);
-
-    return $http_client;
+    return $remote->getHttpClient(self::STANDARD_CLIENT);
   }
 
   /**
    * {@inheritdoc}
    */
   public function prepareJsonApiClient(RemoteInterface $remote) {
-    return $this->httpClientFactory->fromOptions([
-      'base_uri' => $remote->get('url') . '/',
-      'auth' => [
-        $remote->get('basic_auth_username'),
-        $remote->get('basic_auth_password'),
-      ],
-      'headers' => [
-        'Content-type' => 'application/vnd.api+json',
-      ],
-    ]);
+    return $remote->getHttpClient(self::JSON_API_CLIENT);
   }
 
   /**
@@ -88,10 +67,12 @@ class RemoteManager implements RemoteManagerInterface {
   public function getChannelsInfos(RemoteInterface $remote) {
     $http_client = $this->prepareJsonApiClient($remote);
 
-    $response = $this->requestService->request($http_client, 'GET', 'entity_share');
-    $json = Json::decode((string) $response->getBody());
+    if ($response = $this->requestService->request($http_client, 'GET', 'entity_share')) {
+      $json = Json::decode((string) $response->getBody());
 
-    return $json['data']['channels'];
+      return $json['data']['channels'];
+    }
+    return [];
   }
 
 }
