diff --git a/clients.info b/clients.info
index ec9b8c2..9cc78bd 100755
--- a/clients.info
+++ b/clients.info
@@ -3,6 +3,7 @@ description = Basic service management and API for Web Services clients
 core = 7.x
 package = Clients
 dependencies[] = entity
+dependencies[] = ctools
 files[] = includes/clients.connection.admin.inc
 files[] = includes/clients.entity.inc
 files[] = includes/clients.controller.inc
diff --git a/clients.module b/clients.module
index 55f69cb..5da0d06 100755
--- a/clients.module
+++ b/clients.module
@@ -266,7 +266,7 @@ function clients_entity_info() {
   $return = array(
     'clients_connection' => array(
       'label' => t('Clients Connection'),
-      'controller class' => 'ClientsHandlerEntityController',
+      'controller class' => 'ClientsConnectionHandlerEntityController',
       'base table' => 'clients_connection',
       'uri callback' => 'clients_connection_uri',
       'fieldable' => FALSE,
@@ -431,6 +431,27 @@ function clients_resource_uri($resource) {
   );
 }
 
+// ================================================ CTools plugins
+
+/**
+ * Implements hook_ctools_plugin_type().
+ */
+function clients_ctools_plugin_type() {
+  $plugins['clients_credentials_storage'] = array(
+  );
+
+  return $plugins;
+}
+
+/**
+ * Implements hook_ctools_plugin_directory().
+ */
+function clients_ctools_plugin_directory($owner, $plugin_type) {
+  if ($owner == 'clients') {
+    return "plugins/$plugin_type";
+  }
+}
+
 // ================================================ Misc helpers
 
 /**
diff --git a/connections/clients_drupal/clients_drupal.inc b/connections/clients_drupal/clients_drupal.inc
index d9b2dea..46aaf70 100755
--- a/connections/clients_drupal/clients_drupal.inc
+++ b/connections/clients_drupal/clients_drupal.inc
@@ -18,10 +18,26 @@ class clients_connection_drupal_services extends clients_connection_base {
     parent::__construct($values, $entityType);
 
     // Decrypt the password.
-    $this->configuration['password'] = clients_drupal_decrypt($this->configuration['password']);
+    // TODO: move this to the loading of credentials.
+    // Though nothing uses this any more!!!
+    //$this->configuration['password'] = clients_drupal_decrypt($this->configuration['password']);
   }
 
   // ============================================ Base: Admin UI.
+
+  /**
+   * Declare an array of properties which should be treated as credentials.
+   *
+   * This lets the credentials storage plugin know which configuration
+   * properties to take care of.
+   *
+   * @return
+   *  A flat array of property names.
+   */
+  function credentialsProperties() {
+    return array('username', 'password');
+  }
+
   /**
    * Submit handler for saving/updating connections of this class.
    *
@@ -73,14 +89,8 @@ class clients_connection_drupal_services_7_3 extends clients_connection_drupal_s
   function connectionSettingsFormAlter(&$form, &$form_state) {
     $form['endpoint']['#description'] = t('Remote service URL e.g. http://mysite.com/path/to/xmlrpc');
 
-    $form['configuration'] += array(
-      '#type' => 'fieldset',
-      '#title' => t('Configuration'),
-      '#collapsible' => FALSE,
-      '#tree' => TRUE,
-    );
-
-    $form['configuration']['username'] = array(
+    // There is no configuration other than the credentials.
+    $form['credentials']['username'] = array(
       '#type' => 'textfield',
       '#title' => t('Service username'),
       '#size' => 30,
@@ -90,11 +100,11 @@ class clients_connection_drupal_services_7_3 extends clients_connection_drupal_s
       '#required' => TRUE,
     );
 
-    $password_exists = isset($this->configuration['password']);
+    $password_exists = isset($this->credentials['password']);
     $password_desc = $password_exists
       ? t('This should be same as the password on the server you are connecting to. Leave blank unless you need to change this.')
       : t('This should be same as the password on the server you are connecting to.');
-    $form['configuration']['password'] = array(
+    $form['credentials']['password'] = array(
       '#type' => 'password',
       '#title' => t('Service password'),
       '#size' => 30,
@@ -135,8 +145,9 @@ class clients_connection_drupal_services_7_3 extends clients_connection_drupal_s
     }
 
     // Log in and get the user's session ID.
-    $username = $this->configuration['username'];
-    $password = $this->configuration['password'];
+    $this->credentialsLoad();
+    $username = $this->credentials['username'];
+    $password = $this->credentials['password'];
 
     $login = xmlrpc($this->endpoint, array(
       'user.login' => array(
@@ -212,7 +223,8 @@ class clients_connection_drupal_services_6_2 extends clients_connection_drupal_s
       '#description' => t('This should be same as the \'Key\' field used by the Services authentication key on the server you are connecting to.'),
       '#required' => TRUE,
     );
-    $form['configuration']['username'] = array(
+
+    $form['credentials']['username'] = array(
       '#type' => 'textfield',
       '#title' => t('Service username'),
       '#size' => 30,
@@ -222,11 +234,11 @@ class clients_connection_drupal_services_6_2 extends clients_connection_drupal_s
       '#required' => TRUE,
     );
 
-    $password_exists = isset($this->configuration['password']);
+    $password_exists = isset($this->credentials['password']);
     $password_desc = $password_exists
       ? t('This should be same as the password on the server you are connecting to. Leave blank unless you need to change this.')
       : t('This should be same as the password on the server you are connecting to.');
-    $form['configuration']['password'] = array(
+    $form['credentials']['password'] = array(
       '#type' => 'password',
       '#title' => t('Service password'),
       '#size' => 30,
@@ -274,8 +286,9 @@ class clients_connection_drupal_services_6_2 extends clients_connection_drupal_s
     //dsm($key_args);
 
     // Build the array of connection arguments we need to log in.
-    $username = $this->configuration['username'];
-    $password = $this->configuration['password'];
+    $this->credentialsLoad();
+    $username = $this->credentials['username'];
+    $password = $this->credentials['password'];
 
     // Call the xmlrpc method with our array of arguments. This accounts for
     // whether we use a key or not, and the extra parameters to pass to the method.
diff --git a/connections/clients_drupal_rest/clients_drupal_rest.inc b/connections/clients_drupal_rest/clients_drupal_rest.inc
index d0b8e90..8091f8d 100644
--- a/connections/clients_drupal_rest/clients_drupal_rest.inc
+++ b/connections/clients_drupal_rest/clients_drupal_rest.inc
@@ -12,6 +12,19 @@ class clients_connection_drupal_services_rest_7 extends clients_connection_base
   // ============================================ Connection form methods.
 
   /**
+   * Declare an array of properties which should be treated as credentials.
+   *
+   * This lets the credentials storage plugin know which configuration
+   * properties to take care of.
+   *
+   * @return
+   *  A flat array of property names.
+   */
+  function credentialsProperties() {
+    return array('username', 'password');
+  }
+
+  /**
    * Extra form elements specific to a class's edit form.
    *
    * @param $form_state
@@ -23,14 +36,8 @@ class clients_connection_drupal_services_rest_7 extends clients_connection_base
   function connectionSettingsFormAlter(&$form, &$form_state) {
     $form['endpoint']['#description'] = t('Remote service URL e.g. http://mysite.com/service-endpoint');
 
-    $form['configuration'] += array(
-      '#type' => 'fieldset',
-      '#title' => t('Configuration'),
-      '#collapsible' => FALSE,
-      '#tree' => TRUE,
-    );
-
-    $form['configuration']['username'] = array(
+    // There is no configuration other than the credentials.
+    $form['credentials']['username'] = array(
       '#type' => 'textfield',
       '#title' => t('Service username'),
       '#size' => 30,
@@ -40,11 +47,11 @@ class clients_connection_drupal_services_rest_7 extends clients_connection_base
       '#required' => TRUE,
     );
 
-    $password_exists = isset($this->configuration['password']);
+    $password_exists = isset($this->credentials['password']);
     $password_description = $password_exists
       ? t('This should be same as the password on the server you are connecting to. Leave blank unless you need to change this.')
       : t('This should be same as the password on the server you are connecting to.');
-    $form['configuration']['password'] = array(
+    $form['credentials']['password'] = array(
       '#type' => 'password',
       '#title' => t('Service password'),
       '#size' => 30,
@@ -107,9 +114,10 @@ class clients_connection_drupal_services_rest_7 extends clients_connection_base
    */
   function userLogin() {
     // Based on example code at http://drupal.org/node/910598.
+    $this->credentialsLoad();
     $data = array(
-      'username' => $this->configuration['username'],
-      'password' => $this->configuration['password'],
+      'username' => $this->credentials['username'],
+      'password' => $this->credentials['password'],
     );
     $data = http_build_query($data, '', '&');
     $headers = array();
diff --git a/includes/clients.connection.admin.inc b/includes/clients.connection.admin.inc
index ca79dde..c2a65b2 100755
--- a/includes/clients.connection.admin.inc
+++ b/includes/clients.connection.admin.inc
@@ -146,6 +146,39 @@ function clients_connection_form($form, &$form_state, $connection, $op = 'edit')
     '#tree' => TRUE,
   );
 
+  // Container for credentials form elements.
+  $form['credentials'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Credentials'),
+    '#tree' => TRUE,
+  );
+
+  $form['#connection_credentials_storage'] = isset($connection->configuration['credentials_storage'])
+    ? $connection->configuration['credentials_storage']
+    : NULL;
+
+  $form['credentials']['credentials_storage'] = array(
+    '#type' => 'radios',
+    '#title' => t('Credentials storage'),
+    '#required' => TRUE,
+    '#default_value' => $form['#connection_credentials_storage'],
+  );
+
+  // Get an array of the available label plugins.
+  ctools_include('plugins');
+  $credentials_plugins = ctools_get_plugins('clients', 'clients_credentials_storage');
+  $options = array();
+  foreach ($credentials_plugins as $credentials_plugin => $credentials_plugin_info) {
+    $options[$credentials_plugin] = $credentials_plugin_info['label'];
+    $form['credentials']['credentials_storage'][$credentials_plugin]['#description'] = $credentials_plugin_info['description'];
+  }
+  $form['credentials']['credentials_storage']['#options'] = $options;
+
+  // Get the current (or default) storage plugin, and get the credentials from
+  // it.
+  $credentials_storage_plugin = $connection->get_credentials_storage_plugin();
+  $credentials_storage_plugin->credentialsLoad($connection);
+
   $form['#connection_type'] = $type;
   $form['#connection_class'] = $class;
 
@@ -159,6 +192,13 @@ function clients_connection_form($form, &$form_state, $connection, $op = 'edit')
       $form['configuration'][$configuration_key]['#default_value'] = $connection->configuration[$configuration_key];
     }
   }
+  // Fill in the default values for the credentials.
+  $credentials_properties = $connection->credentialsProperties();
+  foreach ($credentials_properties as $property_name) {
+    if (isset($connection->credentials[$property_name])) {
+      $form['credentials'][$property_name]['#default_value'] = $connection->credentials[$property_name];
+    }
+  }
 
   $form['actions']['submit'] = array(
     '#type' => 'submit',
@@ -191,12 +231,38 @@ function clients_connection_form_submit($form, &$form_state) {
     return;
   }
 
+  // Build the entity from the form values.
+  // Note that this will place a 'credentials' propery on it, from the form
+  // fieldset, which will not be saved because there is no schema for it.
   $connection = entity_ui_form_submit_build_entity($form, $form_state);
 
+  ctools_include('plugins');
+
+  // Set the credentials storage plugin id on the connection.
+  $connection->configuration['credentials_storage'] = $form_state['values']['credentials']['credentials_storage'];
+
   // Call connectionSettingsForm_submit() on the connection handler.
   // This allows treatment of form values particular to the connection type.
+  // The base class also handles restoring the password from credentials storage
+  // to the connection if the form element was left blank.
   $connection->connectionSettingsForm_submit($form, $form_state);
 
+  // Call the credentials storage plugin to handle saving the credentials.
+  $credentials_storage_plugin = $connection->get_credentials_storage_plugin();
+  $credentials_storage_plugin->credentialsSave($connection);
+
+  // If the credentials storage method has changed, delete the old storage.
+  // We do this last, as the copying of the password relies on the old
+  // credentials still existing.
+  if (isset($form['#connection_credentials_storage'])
+      && $form['#connection_credentials_storage'] != $form_state['values']['credentials']['credentials_storage']) {
+    // Delete the credentials from the old storage.
+    // Get the old plugin for $form['#connection_credentials_storage'].
+    $old_credentials_storage_plugin = $connection->get_credentials_storage_plugin($form['#connection_credentials_storage']);
+    // Let the old plugin delete its storage.
+    $old_credentials_storage_plugin->credentialsDelete($connection);
+  }
+
   // Save and go back.
   $connection->save();
 
diff --git a/includes/clients.controller.inc b/includes/clients.controller.inc
index a344307..01198f9 100755
--- a/includes/clients.controller.inc
+++ b/includes/clients.controller.inc
@@ -101,3 +101,30 @@ class ClientsHandlerEntityController extends EntityAPIControllerExportable {
   }
 
 }
+
+/**
+ * Controller class for Connections.
+ */
+class ClientsConnectionHandlerEntityController extends ClientsHandlerEntityController {
+
+  /**
+   * Implements EntityAPIControllerInterface.
+   *
+   * Takes care of deleting stored credentials when connections are deleted.
+   *
+   * @param $transaction
+   *   Optionally a DatabaseTransaction object to use. Allows overrides to pass
+   *   in their transaction object.
+   */
+  public function delete($ids, DatabaseTransaction $transaction = NULL) {
+    $connections = entity_load('clients_connection', $ids);
+
+    foreach ($connections as $connection) {
+      $credentials_storage_plugin = $connection->get_credentials_storage_plugin();
+      $credentials_storage_plugin->credentialsDelete($connection);
+    }
+
+    parent::delete($ids, $transaction);
+  }
+
+}
diff --git a/includes/clients.entity.inc b/includes/clients.entity.inc
index 21f05c9..7bcf703 100755
--- a/includes/clients.entity.inc
+++ b/includes/clients.entity.inc
@@ -63,9 +63,52 @@ abstract class clients_connection_base extends ClientsHandlerEntity {
    */
   public $configuration;
 
+  /**
+   * Declare an array of properties which should be treated as credentials.
+   *
+   * This lets the credentials storage plugin know which configuration
+   * properties to take care of.
+   *
+   * @return
+   *  A flat array of property names.
+   */
+  function credentialsProperties() {
+    return array();
+  }
+
   // ============================================ Connection UI
 
   /**
+   * Get the credentials storage plugin for this connection.
+   *
+   * @param $plugin_id
+   *  (Optional) The id of the plugin to retrieve. Specifying this allows the
+   *  loading of the old plugin during connection form save.
+   *
+   * @return
+   *  The instantiated plugin handler.
+   */
+  function get_credentials_storage_plugin($plugin_id = NULL) {
+    if (!isset($plugin_id)) {
+      if (isset($this->configuration['credentials_storage'])) {
+        $plugin_id = $this->configuration['credentials_storage'];
+      }
+      else {
+        // Fallback to the connection configuration plugin by default. This also
+        // ensures backward compatibility.
+        $plugin_id = 'connection_configuration';
+      }
+    }
+
+    // Get the credentials storage plugin.
+    ctools_include('plugins');
+    $storage_plugin = ctools_get_plugins('clients', 'clients_credentials_storage', $plugin_id);
+    $class = ctools_plugin_get_class($storage_plugin, 'handler');
+
+    return new $class();
+  }
+
+  /**
    * Format the connection's endpoint as a link.
    *
    * @param $url
@@ -108,7 +151,7 @@ abstract class clients_connection_base extends ClientsHandlerEntity {
    */
   function connectionSettingsForm_submit($form, &$form_state) {
     // Check there is a password form element.
-    if (isset($form['configuration']['password'])) {
+    if (isset($form['credentials']['password'])) {
       $old_connection = $form['#connection'];
 
       // Check whether we're editing or adding a new connection.
@@ -118,8 +161,11 @@ abstract class clients_connection_base extends ClientsHandlerEntity {
       }
       else {
         // Set password to original if blank.
-        if (empty($form_state['values']['configuration']['password'])) {
-          $this->configuration['password'] = $old_connection->configuration['password'];
+        if (empty($form_state['values']['credentials']['password'])) {
+          // Load the old credentials into the old connection, then copy the
+          // password into the new one.
+          $old_connection->credentialsLoad();
+          $this->credentials['password'] = $old_connection->credentials['password'];
         }
         // Encryption disabled for now. TODO
         //$form_state['values']['configuration']['password'] = clients_drupal_encrypt($form_state['values']['configuration']['password']);
@@ -131,6 +177,20 @@ abstract class clients_connection_base extends ClientsHandlerEntity {
   }
 
   // ============================================ Connection API.
+
+  /**
+   * Load the credentials into the connection.
+   *
+   * The credentials are loaded to a $connection->credentials array.
+   *
+   * Note that we don't call this in __construct() because then they would be
+   * exported, which defeats the purpose.
+   */
+  function credentialsLoad() {
+    $credentials_storage_plugin = $this->get_credentials_storage_plugin();
+    $credentials_storage_plugin->credentialsLoad($this);
+  }
+
   // TODO: move these to an XMLRPC client interface.
 
   /**
diff --git a/plugins/clients_credentials_storage/connection_configuration.inc b/plugins/clients_credentials_storage/connection_configuration.inc
new file mode 100644
index 0000000..768f7a9
--- /dev/null
+++ b/plugins/clients_credentials_storage/connection_configuration.inc
@@ -0,0 +1,80 @@
+<?php
+
+/**
+ * Credentials storage plugin for Clients.
+ */
+
+$plugin = array(
+  'label' => t('Configuration storage'),
+  'description' => t('Store the credentials in the connection configuration, so they can be exported as part of the connection.'),
+  'handler' => array(
+    'class' => 'clients_credentials_storage_configuration',
+  ),
+);
+
+/**
+ * Plugin handler class.
+ *
+ * Store credentials in the Connection's configuration array (i.e., the same as
+ * the old way!). This means that the credentials are contained within the
+ * exported connection, which may or may not be desirable depending on your
+ * situation.
+ */
+class clients_credentials_storage_configuration {
+
+  /**
+   * Load credentials, if any exist, into the connection.
+   *
+   * @param $connection
+   *  The connection to load credentials from.
+   */
+  public function credentialsLoad($connection) {
+    // Get the list of properties which are credentials.
+    $credentials_properties = $connection->credentialsProperties();
+    // Copy them to the credentials array, where connection classes expect to
+    // find them.
+    foreach ($credentials_properties as $property_name) {
+      $connection->credentials[$property_name] = $connection->configuration[$property_name];
+    }
+  }
+
+  /**
+   * Save credentials.
+   *
+   * @param $connection
+   *  The connection to save credentials for. These should be present in the
+   *  $connection->credentials array, where the form submit process will have
+   *  placed them.
+   */
+  public function credentialsSave($connection) {
+    // Get the list of properties which are credentials.
+    $credentials_properties = $connection->credentialsProperties();
+    foreach ($credentials_properties as $property_name) {
+      $connection->configuration[$property_name] = $connection->credentials[$property_name];
+    }
+
+    // No need to save: the connection is about to be saved anyway.
+  }
+
+  /**
+   * Erase credentials.
+   *
+   * This should be called when a connection is deleted, or when a connection
+   * changes its credentials storage mechanism.
+   *
+   * @param $connection
+   *  The connection to delete credentials for.
+   */
+  public function credentialsDelete($connection) {
+    // Get the list of properties which are credentials.
+    $credentials_properties = $connection->credentialsProperties();
+
+    foreach ($credentials_properties as $property_name) {
+      unset($connection->configuration[$property_name]);
+    }
+
+    // No need to save: if deleting, the connection is deleted anyway, and if
+    // switching storage methods, it's about to be saved also.
+  }
+
+}
diff --git a/plugins/clients_credentials_storage/variables.inc b/plugins/clients_credentials_storage/variables.inc
new file mode 100644
index 0000000..c7a4222
--- /dev/null
+++ b/plugins/clients_credentials_storage/variables.inc
@@ -0,0 +1,86 @@
+<?php
+
+/**
+ * Credentials storage plugin for Clients.
+ */
+
+$plugin = array(
+  'label' => t('Variable storage'),
+  'description' => t('Store the credentials in a system variable. This keeps them unique to this site.'),
+  'handler' => array(
+    'class' => 'clients_credentials_storage_variable',
+  ),
+);
+
+/**
+ * Plugin handler class.
+ *
+ * Store credentials in the Connection's configuration array (i.e., the same as
+ * the old way!). This means that the credentials are contained within the
+ * exported connection, which may or may not be desirable depending on your
+ * situation.
+ */
+class clients_credentials_storage_variable {
+
+  /**
+   * Helper to get the variable name.
+   *
+   * @param $connection
+   *  The connection to use.
+   */
+  function variableName($connection) {
+    // TODO: This can cause problems if the connection name is too long:
+    // character limit for a system variable name is 128, but so is that for
+    // a connection name!
+    return 'clients_connection_credentials_' . $connection->name;
+  }
+
+  /**
+   * Load credentials, if any exist, into the connection.
+   *
+   * @param $connection
+   *  The connection to load credentials from.
+   */
+  public function credentialsLoad($connection) {
+    $credentials = variable_get($this->variableName($connection), array());
+
+    foreach ($credentials as $property_name => $value) {
+      $connection->credentials[$property_name] = $value;
+    }
+  }
+
+  /**
+   * Save credentials.
+   *
+   * @param $connection
+   *  The connection to save credentials for. These should be present in the
+   *  $connection->credentials array, where the form submit process will have
+   *  placed them.
+   */
+  public function credentialsSave($connection) {
+    // Get the list of properties which are credentials.
+    $credentials = array();
+    $credentials_properties = $connection->credentialsProperties();
+
+    // Build an array of credentials to save.
+    foreach ($credentials_properties as $property_name) {
+      $credentials[$property_name] = $connection->credentials[$property_name];
+    }
+
+    variable_set($this->variableName($connection), $credentials);
+  }
+
+  /**
+   * Erase credentials.
+   *
+   * This should be called when a connection is deleted, or when a connection
+   * changes its credentials storage mechanism.
+   *
+   * @param $connection
+   *  The connection to delete credentials for.
+   */
+  public function credentialsDelete($connection) {
+    variable_del($this->variableName($connection));
+  }
+
+}
