diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php
index 9117891..013dec1 100644
--- a/core/lib/Drupal/Core/CoreServiceProvider.php
+++ b/core/lib/Drupal/Core/CoreServiceProvider.php
@@ -4,6 +4,7 @@
 
 use Drupal\Core\Cache\Context\CacheContextsPass;
 use Drupal\Core\Cache\ListCacheBinsPass;
+use Drupal\Core\DependencyInjection\Compiler\AuthenticationProviderPass;
 use Drupal\Core\DependencyInjection\Compiler\BackendCompilerPass;
 use Drupal\Core\DependencyInjection\Compiler\GuzzleMiddlewarePass;
 use Drupal\Core\DependencyInjection\Compiler\ContextProvidersPass;
@@ -90,6 +91,7 @@ public function register(ContainerBuilder $container) {
     $container->addCompilerPass(new ListCacheBinsPass());
     $container->addCompilerPass(new CacheContextsPass());
     $container->addCompilerPass(new ContextProvidersPass());
+    $container->addCompilerPass(new AuthenticationProviderPass());
 
     // Register plugin managers.
     $container->addCompilerPass(new PluginManagerPass());
diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/AuthenticationProviderPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/AuthenticationProviderPass.php
new file mode 100644
index 0000000..d1a8407
--- /dev/null
+++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/AuthenticationProviderPass.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Drupal\Core\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+class AuthenticationProviderPass implements CompilerPassInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function process(ContainerBuilder $container) {
+    $authentication_providers = [];
+    foreach ($container->findTaggedServiceIds('authentication_provider') as $service_id => $attributes) {
+      $authentication_provider = $attributes[0]['provider_id'];
+      if ($provider_tag = $container->getDefinition($service_id)->getTag('_provider')) {
+        $authentication_providers[$authentication_provider] = $provider_tag[0]['provider'];
+      }
+    }
+    $container->setParameter('authentication_providers', $authentication_providers);
+  }
+
+}
diff --git a/core/modules/page_cache/src/Tests/PageCacheTest.php b/core/modules/page_cache/src/Tests/PageCacheTest.php
index 9194e65..64e4ee9 100644
--- a/core/modules/page_cache/src/Tests/PageCacheTest.php
+++ b/core/modules/page_cache/src/Tests/PageCacheTest.php
@@ -131,7 +131,7 @@ function testQueryParameterFormatRequests() {
     $this->assertRaw('{"content":"oh hai this is json"}', 'The correct Json response was returned.');
 
     // Enable REST support for nodes and hal+json.
-    \Drupal::service('module_installer')->install(['node', 'rest', 'hal']);
+    \Drupal::service('module_installer')->install(['node', 'rest', 'hal', 'basic_auth']);
     $this->drupalCreateContentType(['type' => 'article']);
     $node = $this->drupalCreateNode(['type' => 'article']);
     $node_uri = $node->urlInfo();
diff --git a/core/modules/rest/config/install/rest.settings.yml b/core/modules/rest/config/install/rest.settings.yml
index d3aa82b..2d8185e 100644
--- a/core/modules/rest/config/install/rest.settings.yml
+++ b/core/modules/rest/config/install/rest.settings.yml
@@ -1,50 +1,3 @@
-# Enable all methods on nodes.
-# You must install Hal and Basic_auth modules for this to work. Also, if you are
-# going to use Basic_auth in a production environment then you should consider
-# setting up SSL.
-# There are alternatives to Basic_auth in contrib such as OAuth module.
-resources:
-  entity:node:
-    GET:
-      supported_formats:
-        - hal_json
-      supported_auth:
-        - basic_auth
-    POST:
-      supported_formats:
-        - hal_json
-      supported_auth:
-        - basic_auth
-    PATCH:
-      supported_formats:
-        - hal_json
-      supported_auth:
-        - basic_auth
-    DELETE:
-      supported_formats:
-        - hal_json
-      supported_auth:
-        - basic_auth
-
-# Multiple formats and multiple authentication providers can be defined for a
-# resource:
-#
-# resources:
-#   entity:node:
-#     GET:
-#       supported_formats:
-#         - json
-#         - hal_json
-#         - xml
-#       supported_auth:
-#         - cookie
-#         - basic_auth
-#
-# hal_json is the only format supported for POST and PATCH methods.
-#
-# The full documentation is located at
-# https://www.drupal.org/documentation/modules/rest.
-
 # Set the domain for REST type and relation links.
 # If left blank, the site's domain will be used.
 link_domain: ~
diff --git a/core/modules/rest/config/optional/rest.resource.entity__node.yml b/core/modules/rest/config/optional/rest.resource.entity__node.yml
new file mode 100644
index 0000000..a73d44d
--- /dev/null
+++ b/core/modules/rest/config/optional/rest.resource.entity__node.yml
@@ -0,0 +1,18 @@
+id: entity__node
+plugin_id: 'entity:node'
+granularity: resource
+configuration:
+  methods:
+    - GET
+    - POST
+    - PATCH
+    - DELETE
+  formats:
+    - hal_json
+  authentication:
+    - basic_auth
+dependencies:
+  module:
+    - node
+    - basic_auth
+    - hal
diff --git a/core/modules/rest/config/schema/rest.schema.yml b/core/modules/rest/config/schema/rest.schema.yml
index 8014b31..080c9c5 100644
--- a/core/modules/rest/config/schema/rest.schema.yml
+++ b/core/modules/rest/config/schema/rest.schema.yml
@@ -1,20 +1,14 @@
 # Schema for the configuration files of the REST module.
-
 rest.settings:
   type: config_object
   label: 'REST settings'
   mapping:
-    resources:
-      type: sequence
-      label: 'Resources'
-      sequence:
-        type: rest_resource
-        label: 'Resource'
     link_domain:
       type: string
       label: 'Domain of the relation'
 
-rest_resource:
+# Method-level granularity of REST resource configuration.
+rest_resource.method:
   type: mapping
   mapping:
     GET:
@@ -30,6 +24,28 @@ rest_resource:
       type: rest_request
       label: 'DELETE method settings'
 
+rest_resource.resource:
+  type: mapping
+  mapping:
+    methods:
+      type: sequence
+      label: 'Supported methods'
+      sequence:
+        type: string
+        label: 'HTTP method'
+    formats:
+      type: sequence
+      label: 'Supported formats'
+      sequence:
+        type: string
+        label: 'Format'
+    authentication:
+      type: sequence
+      label: 'Supported authentication providers'
+      sequence:
+        type: string
+        label: 'Authentication provider'
+
 rest_request:
   type: mapping
   mapping:
@@ -45,3 +61,20 @@ rest_request:
       sequence:
         type: string
         label: 'Authentication'
+
+rest.resource.*:
+  type: config_entity
+  label: 'REST resource config'
+  mapping:
+    id:
+      type: string
+      label: 'REST resource config ID'
+    plugin_id:
+      type: string
+      label: 'REST resource plugin id'
+    granularity:
+      type: string
+      label: 'REST resource configuration granularity'
+    configuration:
+      type: rest_resource.[%parent.granularity]
+      label: 'REST resource configuration'
diff --git a/core/modules/rest/rest.install b/core/modules/rest/rest.install
index 4bca69b..414c4fa 100644
--- a/core/modules/rest/rest.install
+++ b/core/modules/rest/rest.install
@@ -5,6 +5,8 @@
  * Install, update and uninstall functions for the rest module.
  */
 
+use Drupal\rest\Entity\RestResourceConfig;
+
 /**
  * Implements hook_requirements().
  */
@@ -21,3 +23,65 @@ function rest_requirements($phase) {
   }
   return $requirements;
 }
+
+/**
+ * @defgroup updates-8.1.x-to-8.2.x Updates from 8.1.x to 8.2.x
+ * @{
+ * Update functions from 8.1.x to 8.2.x.
+ */
+
+/**
+ * Install the REST config entity type and convert old settings-based config.
+ */
+function rest_update_8201() {
+  \Drupal::entityDefinitionUpdateManager()->installEntityType(\Drupal::entityTypeManager()->getDefinition('rest_resource_config'));
+  foreach (\Drupal::config('rest.settings')->get('resources') as $key => $resource) {
+    $resource = RestResourceConfig::create([
+      'id' => str_replace(':', '__', $key),
+      'granularity' => 'method',
+      'configuration' => $resource,
+    ]);
+    $resource->save();
+  }
+  \Drupal::configFactory()->getEditable('rest.settings')
+    ->clear('resources')
+    ->save();
+}
+
+/**
+ * Simplify method-granularity REST resources to resource-granularity.
+ */
+function rest_update_8202() {
+  /** @var \Drupal\Core\Entity\EntityStorageInterface $resource_config_storage */
+  $resource_config_storage = \Drupal::entityTypeManager()->getStorage('rest_resource_config');
+  $resource_config_entities = $resource_config_storage->loadMultiple();
+  /** @var \Drupal\rest\RestResourceConfigInterface $resource_config_entity */
+  foreach ($resource_config_entities as $resource_config_entity) {
+    if ($resource_config_entity->get('granularity') === 'method') {
+
+      $format_and_auth_configuration = [];
+      foreach ($resource_config_entity->getMethods()as $method) {
+        $format_and_auth_configuration['format'][$method] = implode(',', $resource_config_entity->getFormats($method));
+        $format_and_auth_configuration['auth'][$method] = implode(',', $resource_config_entity->getAuthenticationProviders($method));
+      }
+
+      // If each method has the same formats and the same authentication
+      // providers configured, convert it to 'granularity: resource', which has
+      // a simpler/less verbose configuration.
+      if (count(array_unique($format_and_auth_configuration['format'])) === 1 && count(array_unique($format_and_auth_configuration['auth'])) === 1) {
+        $resource_config_entity->set('configuration', [
+          'methods' => $resource_config_entity->getMethods(),
+          'formats' => $resource_config_entity->getFormats('GET'),
+          'authentication' => $resource_config_entity->getAuthenticationProviders('GET')
+        ]);
+        $resource_config_entity->set('granularity', 'resource');
+        $resource_config_entity->save();
+      }
+    }
+  }
+}
+
+
+/**
+ * @} End of "defgroup updates-8.1.x-to-8.2.x".
+ */
diff --git a/core/modules/rest/rest.permissions.yml b/core/modules/rest/rest.permissions.yml
index 2ab7154..171a284 100644
--- a/core/modules/rest/rest.permissions.yml
+++ b/core/modules/rest/rest.permissions.yml
@@ -1,2 +1,5 @@
 permission_callbacks:
   - Drupal\rest\RestPermissions::permissions
+
+administer rest resources:
+  title: 'Administer REST resource configuration'
diff --git a/core/modules/rest/rest.services.yml b/core/modules/rest/rest.services.yml
index 6b613e3..bdc3e17 100644
--- a/core/modules/rest/rest.services.yml
+++ b/core/modules/rest/rest.services.yml
@@ -24,9 +24,12 @@ services:
     arguments: ['@cache.default', '@entity.manager', '@module_handler', '@config.factory', '@request_stack']
   rest.resource_routes:
     class: Drupal\rest\Routing\ResourceRoutes
-    arguments: ['@plugin.manager.rest', '@config.factory', '@logger.channel.rest']
+    arguments: ['@plugin.manager.rest', '@entity_type.manager', '@logger.channel.rest']
     tags:
       - { name: 'event_subscriber' }
   logger.channel.rest:
     parent: logger.channel_base
     arguments: ['rest']
+  rest_resource.dependencies:
+    class: \Drupal\rest\Entity\ConfigDependencies
+    arguments: ['%serializer.format_providers%', '%authentication_providers%']
diff --git a/core/modules/rest/src/Entity/ConfigDependencies.php b/core/modules/rest/src/Entity/ConfigDependencies.php
new file mode 100644
index 0000000..5e46fe1
--- /dev/null
+++ b/core/modules/rest/src/Entity/ConfigDependencies.php
@@ -0,0 +1,278 @@
+<?php
+
+namespace Drupal\rest\Entity;
+
+use Drupal\Component\Plugin\DependentPluginInterface;
+use Drupal\rest\RestResourceConfigInterface;
+
+/**
+ * Calculates rest resource config dependencies.
+ */
+class ConfigDependencies {
+
+  /**
+   * The serialization format providers, keyed by format.
+   *
+   * @var string[]
+   */
+  protected $formatProviders;
+
+  /**
+   * The authentication providers, keyed by ID.
+   *
+   * @var string[]
+   */
+  protected $authProviders;
+
+  /**
+   * Creates a new ConfigDependencies instance.
+   *
+   * @param string[] $format_providers
+   *   The serialization format providers, keyed by format.
+   * @param string[] $auth_providers
+   *   The authentication providers, keyed by ID.
+   */
+  public function __construct(array $format_providers, array $auth_providers) {
+    $this->formatProviders = $format_providers;
+    $this->authProviders = $auth_providers;
+  }
+
+  /**
+   * Calculates dependencies of a specific rest resource configuration.
+   *
+   * @param \Drupal\rest\RestResourceConfigInterface $rest_config
+   *   The rest configuration.
+   *
+   * @return string[][]
+   *   Dependencies keyed by dependency type.
+   *
+   * @see \Drupal\Core\Config\Entity\ConfigEntityInterface::calculateDependencies()
+   */
+  public function calculateDependencies(RestResourceConfigInterface $rest_config) {
+    $granularity = $rest_config->get('granularity');
+
+    // Dependency calculation is the same for either granularity, the most
+    // notable difference is that for the 'resource' granularity, the same
+    // authentication providers and formats are supported for every method.
+    if ($granularity === 'method') {
+      $methods = array_keys($rest_config->get('configuration'));
+    }
+    else {
+      $methods = ['GET'];
+    }
+
+    // The dependency lists for authentication providers and formats
+    // generated on container build.
+    $dependencies = [];
+    foreach ($methods as $request_method) {
+      // 'granularity' is not a request method
+      if ($request_method === 'granularity') {
+        continue;
+      }
+
+      // Add dependencies based on the supported authentication providers.
+      foreach ($rest_config->getAuthenticationProviders($request_method) as $auth) {
+        if (isset($this->authProviders[$auth])) {
+          $module_name = $this->authProviders[$auth];
+          $dependencies['module'][] = $module_name;
+        }
+      }
+      // Add dependencies based on the supported authentication formats.
+      foreach ($rest_config->getFormats($request_method) as $format) {
+        if (isset($this->formatProviders[$format])) {
+          $module_name = $this->formatProviders[$format];
+          $dependencies['module'][] = $module_name;
+        }
+      }
+    }
+
+    if (($resource_plugin = $rest_config->getResourcePlugin()) && $resource_plugin instanceof DependentPluginInterface) {
+      $dependencies = array_merge($dependencies, $resource_plugin->calculateDependencies());
+    }
+
+    return $dependencies;
+  }
+
+  /**
+   * Informs the entity that entities it depends on will be deleted.
+   *
+   * @param \Drupal\rest\RestResourceConfigInterface $rest_config
+   *   The rest configuration.
+   * @param array $dependencies
+   *   An array of dependencies that will be deleted keyed by dependency type.
+   *   Dependency types are, for example, entity, module and theme.
+   *
+   * @return bool
+   *   TRUE if the entity has been changed as a result, FALSE if not.
+   *
+   * @see \Drupal\Core\Config\Entity\ConfigEntityInterface::onDependencyRemoval()
+   */
+  public function onDependencyRemoval(RestResourceConfigInterface $rest_config, array $dependencies) {
+    $granularity = $rest_config->get('granularity');
+    if ($granularity === 'method') {
+      return $this->onDependencyRemovalForMethodGranularity($rest_config, $dependencies);
+    }
+    else {
+      return $this->onDependencyRemovalForResourceGranularity($rest_config, $dependencies);
+    }
+  }
+
+  /**
+   * Informs the entity that entities it depends on will be deleted.
+   *
+   * @param \Drupal\rest\RestResourceConfigInterface $rest_config
+   *   The rest configuration.
+   * @param array $dependencies
+   *   An array of dependencies that will be deleted keyed by dependency type.
+   *   Dependency types are, for example, entity, module and theme.
+   *
+   * @return bool
+   *   TRUE if the entity has been changed as a result, FALSE if not.
+   */
+  public function onDependencyRemovalForMethodGranularity(RestResourceConfigInterface $rest_config, array $dependencies) {
+    $changed = FALSE;
+    // Only module-related dependencies can be fixed. All other types of
+    // dependencies cannot, because they were not generated based on supported
+    // authentication providers or formats.
+    if (isset($dependencies['module'])) {
+      // Try to fix dependencies.
+      $removed_auth = [];
+      $removed_formats = [];
+      foreach ($dependencies['module'] as $dep_module) {
+        // Check if the removed dependency module contained an authentication
+        // provider.
+        foreach ($this->authProviders as $auth => $auth_module) {
+          if ($dep_module != $auth_module) {
+            continue;
+          }
+          $removed_auth[] = $auth;
+        }
+        // Check if the removed dependency module contained a format.
+        foreach ($this->formatProviders as $format => $format_module) {
+          if ($dep_module != $format_module) {
+            continue;
+          }
+          $removed_formats[] = $format;
+        }
+      }
+      $configuration = $rest_config->get('configuration');
+      if (!empty($removed_auth) || !empty($removed_formats)) {
+        // Try to fix dependency problems by removing affected
+        // authentication providers and formats.
+        foreach (array_keys($rest_config->get('configuration')) as $request_method) {
+          foreach ($removed_formats as $format) {
+            if (in_array($format, $rest_config->getFormats($request_method))) {
+              $rest_config = array_filter($rest_config[$request_method]['supported_formats'], function ($val) use ($format) {
+                return ($val != $format);
+              });
+            }
+          }
+          foreach ($removed_auth as $auth) {
+            if (in_array($auth, $rest_config->getAuthenticationProviders($request_method))) {
+              $rest_config = array_filter($rest_config[$request_method]['supported_auth'], function ($val) use ($auth) {
+                return ($val != $auth);
+              });
+            }
+          }
+          if (empty($rest_config->getAuthenticationProviders($request_method)) ){
+            // Remove the key if there are no more authentication providers
+            // supported by this request method.
+            unset($configuration[$request_method]['supported_auth']);
+          }
+          if (empty($rest_config->getFormats($request_method))) {
+            // Remove the key if there are no more formats supported by this
+            // request method.
+            unset($configuration[$request_method]['supported_formats']);
+          }
+          if (empty($configuration[$request_method])) {
+            // Remove the request method altogether if it no longer has any
+            // supported authentication providers or formats.
+            unset($configuration[$request_method]);
+          }
+        }
+      }
+      if (!empty($configuration)) {
+        // Only mark the dependencies problems as fixed if there is any
+        // configuration left.
+        $changed = TRUE;
+      }
+    }
+    // If the dependency problems are not marked as fixed at this point they
+    // should be related to the resource plugin and the config entity should
+    // be deleted.
+    return $changed;
+  }
+
+  /**
+   * Informs the entity that entities it depends on will be deleted.
+   *
+   * @param \Drupal\rest\RestResourceConfigInterface $rest_config
+   *   The rest configuration.
+   * @param array $dependencies
+   *   An array of dependencies that will be deleted keyed by dependency type.
+   *   Dependency types are, for example, entity, module and theme.
+   *
+   * @return bool
+   *   TRUE if the entity has been changed as a result, FALSE if not.
+   */
+  public function onDependencyRemovalForResourceGranularity(RestResourceConfigInterface $rest_config, array $dependencies) {
+    $changed = FALSE;
+    // Only module-related dependencies can be fixed. All other types of
+    // dependencies cannot, because they were not generated based on supported
+    // authentication providers or formats.
+    if (isset($dependencies['module'])) {
+      // Try to fix dependencies.
+      $removed_auth = [];
+      $removed_formats = [];
+      foreach ($dependencies['module'] as $dep_module) {
+        // Check if the removed dependency module contained an authentication
+        // provider.
+        foreach ($this->authProviders as $auth => $auth_module) {
+          if ($dep_module != $auth_module) {
+            continue;
+          }
+          $removed_auth[] = $auth;
+        }
+        // Check if the removed dependency module contained a format.
+        foreach ($this->formatProviders as $format => $format_module) {
+          if ($dep_module != $format_module) {
+            continue;
+          }
+          $removed_formats[] = $format;
+        }
+      }
+      $configuration = $rest_config->get('configuration');
+      if (!empty($removed_auth) || !empty($removed_formats)) {
+        foreach ($removed_formats as $format) {
+          if (in_array($format, $configuration['formats'])) {
+            $configuration = array_filter($configuration['formats'], function ($val) use ($format) {
+              return $val !== $format;
+            });
+          }
+        }
+        foreach ($removed_auth as $auth) {
+          if (in_array($auth, $configuration['authentication'])) {
+            $configuration = array_filter($configuration['authentication'], function ($val) use ($auth) {
+              return $val !== $auth;
+            });
+          }
+        }
+        // Only mark the dependencies problems as fixed if there is still >=1
+        // format available and >=1 authentication provider available.
+        if (!empty($configuration['formats']) && !empty($configuration['authentication'])) {
+          $changed = TRUE;
+        }
+      }
+      else {
+        // Dependencies were removed, but they did not affect the formats or
+        // authentication providers used by this entity.
+        $changed = TRUE;
+      }
+    }
+    // If the dependency problems are not marked as fixed at this point they
+    // should be related to the resource plugin and the config entity should
+    // be deleted.
+    return $changed;
+  }
+
+}
diff --git a/core/modules/rest/src/Entity/RestResourceConfig.php b/core/modules/rest/src/Entity/RestResourceConfig.php
new file mode 100644
index 0000000..c68e0af
--- /dev/null
+++ b/core/modules/rest/src/Entity/RestResourceConfig.php
@@ -0,0 +1,266 @@
+<?php
+
+namespace Drupal\rest\Entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\Core\Entity\Entity;
+use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection;
+use Drupal\rest\RestResourceConfigInterface;
+
+/**
+ * Defines a RestResourceConfig configuration entity class.
+ *
+ * @ConfigEntityType(
+ *   id = "rest_resource_config",
+ *   label = @Translation("REST resource config"),
+ *   config_prefix = "resource",
+ *   admin_permission = "administer rest resources",
+ *   label_callback = "getLabelFromPlugin",
+ *   entity_keys = {
+ *     "id" = "id"
+ *   },
+ *   config_export = {
+ *     "id",
+ *     "plugin_id",
+ *     "granularity",
+ *     "configuration"
+ *   }
+ * )
+ */
+class RestResourceConfig extends ConfigEntityBase implements RestResourceConfigInterface {
+
+  /**
+   * The REST resource config id.
+   *
+   * @var string
+   */
+  protected $id;
+
+  /**
+   * The REST resource plugin id.
+   *
+   * @var string
+   */
+  protected $plugin_id;
+
+  /**
+   * The REST resource configuration granularity.
+   *
+   * Currently either 'method' or 'resource'.
+   *
+   * @var string
+   */
+  protected $granularity;
+
+  /**
+   * The REST resource configuration.
+   *
+   * @var array
+   */
+  protected $configuration;
+
+  /**
+   * The rest resource plugin manager.
+   *
+   * @var \Drupal\Component\Plugin\PluginManagerInterface
+   */
+  protected $pluginManager;
+
+  protected $methods = [];
+  protected $auth = [];
+  protected $formats = [];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct(array $values, $entity_type) {
+    parent::__construct($values, $entity_type);
+    // The config entity id looks like the plugin id but uses __ instead of :
+    // because : is not valid for config entities.
+    if (!isset($this->plugin_id) && isset($this->id)) {
+      // Generate plugin_id on first entity creation.
+      $this->plugin_id = str_replace('__', ':', $this->id);
+    }
+  }
+
+  /**
+   * The label callback for this configuration entity.
+   *
+   * @return string The label.
+   */
+  protected function getLabelFromPlugin() {
+    $plugin_definition = $this->getResourcePluginManager()
+      ->getDefinition(['id' => $this->plugin_id]);
+    return $plugin_definition['label'];
+  }
+
+  /**
+   * Returns the resource plugin manager.
+   *
+   * @return \Drupal\Component\Plugin\PluginManagerInterface
+   */
+  protected function getResourcePluginManager() {
+    if (!isset($this->pluginManager)) {
+      $this->pluginManager = \Drupal::service('plugin.manager.rest');
+    }
+    return $this->pluginManager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getResourcePlugin() {
+    return $this->getPluginCollections()['resource']->get($this->plugin_id);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMethods() {
+    if ($this->granularity === 'method') {
+      return $this->getMethodsForMethodGranularity();
+    }
+    else {
+      return $this->configuration['methods'];
+    }
+  }
+
+  /**
+   * Retrieves a list of supported HTTP methods for this resource.
+   *
+   * @return string[]
+   *   A list of supported HTTP methods.
+   */
+  protected function getMethodsForMethodGranularity() {
+    $methods = array_keys($this->configuration);
+    return array_map([$this, 'normalizeRestMethod'], $methods);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAuthenticationProviders($method) {
+    if ($this->granularity === 'method') {
+      return $this->getAuthenticationProvidersForMethodGranularity($method);
+    }
+    else {
+      return $this->configuration['authentication'];
+    }
+  }
+
+  /**
+   * Retrieves a list of supported authentication providers.
+   *
+   * @param string $method
+   *   The request method e.g GET or POST.
+   *
+   * @return string[]
+   *   A list of supported authentication provider IDs.
+   */
+  public function getAuthenticationProvidersForMethodGranularity($method) {
+    $method = $this->normalizeRestMethod($method);
+    if (in_array($method, $this->getMethods()) && isset($this->configuration[$method]['supported_auth'])) {
+      return $this->configuration[$method]['supported_auth'];
+    }
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormats($method) {
+    if ($this->granularity === 'method') {
+      return $this->getFormatsForMethodGranularity($method);
+    }
+    else {
+      return $this->configuration['formats'];
+    }
+  }
+
+  /**
+   * Retrieves a list of supported response formats.
+   *
+   * @param string $method
+   *   The request method e.g GET or POST.
+   *
+   * @return string[]
+   *   A list of supported format IDs.
+   */
+  protected function getFormatsForMethodGranularity($method) {
+    $method = $this->normalizeRestMethod($method);
+    if (in_array($method, $this->getMethods()) && isset($this->configuration[$method]['supported_formats'])) {
+      return $this->configuration[$method]['supported_formats'];
+    }
+    return [];
+  }
+
+  /**
+   * Returns the plugin collections used by this entity.
+   *
+   * @return \Drupal\Component\Plugin\LazyPluginCollection[]
+   *   An array of plugin collections, keyed by the property name they use to
+   *   store their configuration.
+   */
+  public function getPluginCollections() {
+    return [
+      'resource' => new DefaultSingleLazyPluginCollection($this->getResourcePluginManager(), $this->plugin_id, [])
+    ];
+  }
+
+  /**
+   * (@inheritdoc)
+   */
+  public function calculateDependencies() {
+    parent::calculateDependencies();
+
+    foreach ($this->getRestResourceDependencies()->calculateDependencies($this) as $type => $dependencies) {
+      foreach ($dependencies as $dependency) {
+        $this->addDependency($type, $dependency);
+      }
+    }
+    return $this->dependencies;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onDependencyRemoval(array $dependencies) {
+    $parent = parent::onDependencyRemoval($dependencies);
+
+    // If the dependency problems are not marked as fixed at this point they
+    // should be related to the resource plugin and the config entity should
+    // be deleted.
+    $changed = $this->getRestResourceDependencies()->onDependencyRemoval($this, $dependencies);
+    return $parent || $changed;
+  }
+
+  /**
+   * Returns the REST resource dependencies.
+   *
+   * @return \Drupal\rest\Entity\ConfigDependencies
+   */
+  protected function getRestResourceDependencies() {
+    return \Drupal::service('rest_resource.dependencies');
+  }
+
+  /**
+   * Normalizes the method to upper case and check validity.
+   *
+   * @param string $method
+   *   The request method.
+   *
+   * @return string
+   *   The normalised request method.
+   *
+   * @throws \InvalidArgumentException
+   *   If the method is not supported.
+   */
+  protected function normalizeRestMethod($method) {
+    $valid_methods = ['GET', 'POST', 'PATCH', 'DELETE'];
+    $normalised_method = strtoupper($method);
+    if (!in_array($normalised_method, $valid_methods)) {
+      throw new \InvalidArgumentException('The method is not supported.');
+    }
+    return $normalised_method;
+  }
+}
diff --git a/core/modules/rest/src/Plugin/ResourceBase.php b/core/modules/rest/src/Plugin/ResourceBase.php
index 33cb3aa..549ac54 100644
--- a/core/modules/rest/src/Plugin/ResourceBase.php
+++ b/core/modules/rest/src/Plugin/ResourceBase.php
@@ -194,8 +194,6 @@ protected function getBaseRoute($canonical_path, $method) {
 
     $route = new Route($canonical_path, array(
       '_controller' => 'Drupal\rest\RequestHandler::handle',
-      // Pass the resource plugin ID along as default property.
-      '_plugin' => $this->pluginId,
     ), array(
       '_permission' => "restful $lower_method $this->pluginId",
     ),
diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
index 63857e4..7ce0118 100644
--- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
@@ -2,10 +2,14 @@
 
 namespace Drupal\rest\Plugin\rest\resource;
 
+use Drupal\Component\Plugin\DependentPluginInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageException;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\rest\Plugin\ResourceBase;
 use Drupal\rest\ResourceResponse;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 use Drupal\rest\ModifiedResourceResponse;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
@@ -27,7 +31,49 @@
  *   }
  * )
  */
-class EntityResource extends ResourceBase {
+class EntityResource extends ResourceBase implements DependentPluginInterface {
+
+  /**
+   * The entity type targeted by this resource.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeInterface
+   */
+  protected $entityType;
+
+  /**
+   * Constructs a Drupal\rest\Plugin\rest\resource\EntityResource object.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager
+   * @param array $serializer_formats
+   *   The available serialization formats.
+   * @param \Psr\Log\LoggerInterface $logger
+   *   A logger instance.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager,  $serializer_formats, LoggerInterface $logger) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $serializer_formats, $logger);
+    $this->entityType = $entity_type_manager->getDefinition($plugin_definition['entity_type']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('entity_type.manager'),
+      $container->getParameter('serializer.formats'),
+      $container->get('logger.factory')->get('rest')
+    );
+  }
 
   /**
    * Responds to entity GET requests.
@@ -256,4 +302,13 @@ protected function getBaseRoute($canonical_path, $method) {
     return $route;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function calculateDependencies() {
+    if (isset($this->entityType)) {
+      return ['module' => [$this->entityType->getProvider()]];
+    }
+  }
+
 }
diff --git a/core/modules/rest/src/RequestHandler.php b/core/modules/rest/src/RequestHandler.php
index 2aa3673..212ae06 100644
--- a/core/modules/rest/src/RequestHandler.php
+++ b/core/modules/rest/src/RequestHandler.php
@@ -2,11 +2,14 @@
 
 namespace Drupal\rest;
 
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Cache\CacheableResponseInterface;
 use Drupal\Core\Render\RenderContext;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Symfony\Component\DependencyInjection\ContainerAwareInterface;
 use Symfony\Component\DependencyInjection\ContainerAwareTrait;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpKernel\Exception\HttpException;
@@ -17,11 +20,35 @@
 /**
  * Acts as intermediate request forwarder for resource plugins.
  */
-class RequestHandler implements ContainerAwareInterface {
+class RequestHandler implements ContainerAwareInterface, ContainerInjectionInterface {
 
   use ContainerAwareTrait;
 
   /**
+   * The resource configuration storage.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface
+   */
+  protected $resourceStorage;
+
+  /**
+   * Creates a new RequestHandler instance.
+   *
+   * @param \Drupal\Core\Entity\EntityStorageInterface $entity_storage
+   *   The resource configuration storage.
+   */
+  public function __construct(EntityStorageInterface $entity_storage) {
+    $this->resourceStorage = $entity_storage;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static($container->get('entity_type.manager')->getStorage('rest_resource_config'));
+  }
+
+  /**
    * Handles a web API request.
    *
    * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
@@ -33,13 +60,12 @@ class RequestHandler implements ContainerAwareInterface {
    *   The response object.
    */
   public function handle(RouteMatchInterface $route_match, Request $request) {
-
-    $plugin = $route_match->getRouteObject()->getDefault('_plugin');
     $method = strtolower($request->getMethod());
 
-    $resource = $this->container
-      ->get('plugin.manager.rest')
-      ->createInstance($plugin);
+    $resource_config_id = $route_match->getRouteObject()->getDefault('_rest_resource_config');
+    /** @var \Drupal\rest\RestResourceConfigInterface $resource_config */
+    $resource_config = $this->resourceStorage->load($resource_config_id);
+    $resource = $resource_config->getResourcePlugin();
 
     // Deserialize incoming data if available.
     $serializer = $this->container->get('serializer');
@@ -52,9 +78,8 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
       // formats are configured allow all and hope that the serializer knows the
       // format. If the serializer cannot handle it an exception will be thrown
       // that bubbles up to the client.
-      $config = $this->container->get('config.factory')->get('rest.settings')->get('resources');
-      $method_settings = $config[$plugin][$request->getMethod()];
-      if (empty($method_settings['supported_formats']) || in_array($format, $method_settings['supported_formats'])) {
+      $request_method = $request->getMethod();
+      if (in_array($format, $resource_config->getFormats($request_method))) {
         $definition = $resource->getPluginDefinition();
         $class = $definition['serialization_class'];
         try {
@@ -100,7 +125,7 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
     }
 
     return $response instanceof ResourceResponseInterface ?
-      $this->renderResponse($request, $response, $serializer, $format) :
+      $this->renderResponse($request, $response, $serializer, $format, $resource_config) :
       $response;
   }
 
@@ -131,6 +156,8 @@ public function csrfToken() {
    *   The serializer to use.
    * @param string $format
    *   The response format.
+   * @param \Drupal\rest\RestResourceConfigInterface $resource_config
+   *   The resource config.
    *
    * @return \Drupal\rest\ResourceResponse
    *   The altered response.
@@ -138,7 +165,7 @@ public function csrfToken() {
    * @todo Add test coverage for language negotiation contexts in
    *   https://www.drupal.org/node/2135829.
    */
-  protected function renderResponse(Request $request, ResourceResponseInterface $response, SerializerInterface $serializer, $format) {
+  protected function renderResponse(Request $request, ResourceResponseInterface $response, SerializerInterface $serializer, $format, RestResourceConfigInterface $resource_config) {
     $data = $response->getResponseData();
 
     if ($response instanceof CacheableResponseInterface) {
@@ -152,9 +179,8 @@ protected function renderResponse(Request $request, ResourceResponseInterface $r
         $response->addCacheableDependency($context->pop());
       }
 
-      // Add rest settings config's cache tags.
-      $response->addCacheableDependency($this->container->get('config.factory')
-        ->get('rest.settings'));
+      // Add rest config's cache tags.
+      $response->addCacheableDependency($resource_config);
     }
     else {
       $output = $serializer->serialize($data, $format);
diff --git a/core/modules/rest/src/RestPermissions.php b/core/modules/rest/src/RestPermissions.php
index 57e95e4..473c192 100644
--- a/core/modules/rest/src/RestPermissions.php
+++ b/core/modules/rest/src/RestPermissions.php
@@ -2,8 +2,8 @@
 
 namespace Drupal\rest;
 
-use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\rest\Plugin\Type\ResourcePluginManager;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -20,30 +20,30 @@ class RestPermissions implements ContainerInjectionInterface {
   protected $restPluginManager;
 
   /**
-   * The config factory.
+   * The REST resource config storage.
    *
-   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   * @var \Drupal\Core\Entity\EntityManagerInterface
    */
-  protected $configFactory;
+  protected $resourceConfigStorage;
 
   /**
    * Constructs a new RestPermissions instance.
    *
    * @param \Drupal\rest\Plugin\Type\ResourcePluginManager $rest_plugin_manager
    *   The rest resource plugin manager.
-   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
-   *   The config factory.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
    */
-  public function __construct(ResourcePluginManager $rest_plugin_manager, ConfigFactoryInterface $config_factory) {
+  public function __construct(ResourcePluginManager $rest_plugin_manager, EntityTypeManagerInterface $entity_type_manager) {
     $this->restPluginManager = $rest_plugin_manager;
-    $this->configFactory = $config_factory;
+    $this->resourceConfigStorage = $entity_type_manager->getStorage('rest_resource_config');
   }
 
   /**
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container) {
-    return new static($container->get('plugin.manager.rest'), $container->get('config.factory'));
+    return new static($container->get('plugin.manager.rest'), $container->get('entity_type.manager'));
   }
 
   /**
@@ -53,12 +53,11 @@ public static function create(ContainerInterface $container) {
    */
   public function permissions() {
     $permissions = [];
-    $resources = $this->configFactory->get('rest.settings')->get('resources');
-    if ($resources && $enabled = array_intersect_key($this->restPluginManager->getDefinitions(), $resources)) {
-      foreach ($enabled as $id => $resource) {
-        $plugin = $this->restPluginManager->createInstance($id);
-        $permissions = array_merge($permissions, $plugin->permissions());
-      }
+    /** @var \Drupal\rest\RestResourceConfigInterface[] $resource_configs */
+    $resource_configs = $this->resourceConfigStorage->loadMultiple();
+    foreach ($resource_configs as $resource_config) {
+      $plugin = $resource_config->getResourcePlugin();
+      $permissions = array_merge($permissions, $plugin->permissions());
     }
     return $permissions;
   }
diff --git a/core/modules/rest/src/RestResourceConfigInterface.php b/core/modules/rest/src/RestResourceConfigInterface.php
new file mode 100644
index 0000000..eac95ac
--- /dev/null
+++ b/core/modules/rest/src/RestResourceConfigInterface.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace Drupal\rest;
+
+use Drupal\Core\Config\Entity\ConfigEntityInterface;
+use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
+
+/**
+ * Defines a configuration entity to store enabled REST resources.
+ */
+interface RestResourceConfigInterface extends ConfigEntityInterface, EntityWithPluginCollectionInterface {
+
+  /**
+   * Retrieves the REST resource plugin.
+   *
+   * @return \Drupal\rest\Plugin\ResourceInterface
+   *   The resource plugin
+   */
+  public function getResourcePlugin();
+
+  /**
+   * Retrieves a list of supported HTTP methods.
+   *
+   * @return string[]
+   *   A list of supported HTTP methods.
+   */
+  public function getMethods();
+
+  /**
+   * Retrieves a list of supported authentication providers.
+   *
+   * @param string $method
+   *   The request method e.g GET or POST.
+   *
+   * @return string[]
+   *   A list of supported authentication provider IDs.
+   */
+  public function getAuthenticationProviders($method);
+
+  /**
+   * Retrieves a list of supported response formats.
+   *
+   * @param string $method
+   *   The request method e.g GET or POST.
+   *
+   * @return string[]
+   *   A list of supported format IDs.
+   */
+  public function getFormats($method);
+
+}
diff --git a/core/modules/rest/src/Routing/ResourceRoutes.php b/core/modules/rest/src/Routing/ResourceRoutes.php
index e88f18b..5d3ae30 100644
--- a/core/modules/rest/src/Routing/ResourceRoutes.php
+++ b/core/modules/rest/src/Routing/ResourceRoutes.php
@@ -2,9 +2,10 @@
 
 namespace Drupal\rest\Routing;
 
-use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Routing\RouteSubscriberBase;
 use Drupal\rest\Plugin\Type\ResourcePluginManager;
+use Drupal\rest\RestResourceConfigInterface;
 use Psr\Log\LoggerInterface;
 use Symfony\Component\Routing\RouteCollection;
 
@@ -21,11 +22,11 @@ class ResourceRoutes extends RouteSubscriberBase {
   protected $manager;
 
   /**
-   * The Drupal configuration factory.
+   * The REST resource config storage.
    *
-   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   * @var \Drupal\Core\Entity\EntityManagerInterface
    */
-  protected $config;
+  protected $resourceConfigStorage;
 
   /**
    * A logger instance.
@@ -39,14 +40,14 @@ class ResourceRoutes extends RouteSubscriberBase {
    *
    * @param \Drupal\rest\Plugin\Type\ResourcePluginManager $manager
    *   The resource plugin manager.
-   * @param \Drupal\Core\Config\ConfigFactoryInterface $config
-   *   The configuration factory holding resource settings.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager
    * @param \Psr\Log\LoggerInterface $logger
    *   A logger instance.
    */
-  public function __construct(ResourcePluginManager $manager, ConfigFactoryInterface $config, LoggerInterface $logger) {
+  public function __construct(ResourcePluginManager $manager, EntityTypeManagerInterface $entity_type_manager, LoggerInterface $logger) {
     $this->manager = $manager;
-    $this->config = $config;
+    $this->resourceConfigStorage = $entity_type_manager->getStorage('rest_resource_config');
     $this->logger = $logger;
   }
 
@@ -58,56 +59,68 @@ public function __construct(ResourcePluginManager $manager, ConfigFactoryInterfa
    * @return array
    */
   protected function alterRoutes(RouteCollection $collection) {
-    $routes = array();
-
-    // Silently ignore resources that are in the settings but are not defined on
-    // the plugin manager currently. That avoids exceptions when REST module is
-    // enabled before another module that provides the resource plugin specified
-    // in the settings.
-    // @todo Remove in https://www.drupal.org/node/2308745
-    $resources = $this->config->get('rest.settings')->get('resources') ?: array();
-    $enabled_resources = array_intersect_key($resources, $this->manager->getDefinitions());
-    if (count($resources) != count($enabled_resources)) {
-      trigger_error('rest.settings lists resources relying on the following missing plugins: ' . implode(', ', array_keys(array_diff_key($resources, $enabled_resources))));
+    // Iterate over all enabled REST resource configs.
+    /** @var \Drupal\rest\RestResourceConfigInterface[] $resource_configs */
+    $resource_configs = $this->resourceConfigStorage->loadMultiple();
+    // Iterate over all enabled resource plugins.
+    foreach ($resource_configs as $resource_config) {
+      $resource_routes = $this->getRoutesForResourceConfig($resource_config);
+      $collection->addCollection($resource_routes);
     }
+  }
 
-    // Iterate over all enabled resource plugins.
-    foreach ($enabled_resources as $id => $enabled_methods) {
-      $plugin = $this->manager->createInstance($id);
-      foreach ($plugin->routes() as $name => $route) {
-        // @todo: Are multiple methods possible here?
-        $methods = $route->getMethods();
-        // Only expose routes where the method is enabled in the configuration.
-        if ($methods && ($method = $methods[0]) && $method && isset($enabled_methods[$method])) {
-          $route->setRequirement('_access_rest_csrf', 'TRUE');
-
-          // Check that authentication providers are defined.
-          if (empty($enabled_methods[$method]['supported_auth']) || !is_array($enabled_methods[$method]['supported_auth'])) {
-            $this->logger->error('At least one authentication provider must be defined for resource @id', array(':id' => $id));
-            continue;
-          }
-
-          // Check that formats are defined.
-          if (empty($enabled_methods[$method]['supported_formats']) || !is_array($enabled_methods[$method]['supported_formats'])) {
-            $this->logger->error('At least one format must be defined for resource @id', array(':id' => $id));
-            continue;
-          }
-
-          // If the route has a format requirement, then verify that the
-          // resource has it.
-          $format_requirement = $route->getRequirement('_format');
-          if ($format_requirement && !in_array($format_requirement, $enabled_methods[$method]['supported_formats'])) {
-            continue;
-          }
-
-          // The configuration seems legit at this point, so we set the
-          // authentication provider and add the route.
-          $route->setOption('_auth', $enabled_methods[$method]['supported_auth']);
-          $routes["rest.$name"] = $route;
-          $collection->add("rest.$name", $route);
+  /**
+   * Provides all routes for a given REST resource config.
+   *
+   * This method determines where a resource is reachable, what path
+   * replacements are used, the required HTTP method for the operation etc.
+   *
+   * @param \Drupal\rest\RestResourceConfigInterface $rest_resource_config
+   *   The rest resource config.
+   *
+   * @return \Symfony\Component\Routing\RouteCollection
+   *   The route collection.
+   */
+  protected function getRoutesForResourceConfig(RestResourceConfigInterface $rest_resource_config) {
+    $plugin = $rest_resource_config->getResourcePlugin();
+    $collection = new RouteCollection();
+
+    foreach ($plugin->routes() as $name => $route) {
+      /** @var \Symfony\Component\Routing\Route $route */
+      // @todo: Are multiple methods possible here?
+      $methods = $route->getMethods();
+      // Only expose routes where the method is enabled in the configuration.
+      if ($methods && ($method = $methods[0]) && $supported_formats = $rest_resource_config->getFormats($method)) {
+        $route->setRequirement('_access_rest_csrf', 'TRUE');
+
+        // Check that authentication providers are defined.
+        if (empty($rest_resource_config->getAuthenticationProviders($method))) {
+          $this->logger->error('At least one authentication provider must be defined for resource @id', array(':id' => $rest_resource_config->id()));
+          continue;
+        }
+
+        // Check that formats are defined.
+        if (empty($rest_resource_config->getFormats($method))) {
+          $this->logger->error('At least one format must be defined for resource @id', array(':id' => $rest_resource_config->id()));
+          continue;
         }
+
+        // If the route has a format requirement, then verify that the
+        // resource has it.
+        $format_requirement = $route->getRequirement('_format');
+        if ($format_requirement && !in_array($format_requirement, $rest_resource_config->getFormats($method))) {
+          continue;
+        }
+
+        // The configuration seems legit at this point, so we set the
+        // authentication provider and add the route.
+        $route->setOption('_auth', $rest_resource_config->getAuthenticationProviders($method));
+        $route->setDefault('_rest_resource_config', $rest_resource_config->id());
+        $collection->add("rest.$name", $route);
       }
+
     }
+    return $collection;
   }
 
 }
diff --git a/core/modules/rest/src/Tests/AuthTest.php b/core/modules/rest/src/Tests/AuthTest.php
index 08db9ab..e9fb7d7 100644
--- a/core/modules/rest/src/Tests/AuthTest.php
+++ b/core/modules/rest/src/Tests/AuthTest.php
@@ -16,7 +16,7 @@ class AuthTest extends RESTTestBase {
    *
    * @var array
    */
-  public static $modules = array('basic_auth', 'hal', 'rest', 'entity_test', 'comment');
+  public static $modules = array('basic_auth', 'hal', 'rest', 'entity_test');
 
   /**
    * Tests reading from an authenticated resource.
diff --git a/core/modules/rest/src/Tests/CreateTest.php b/core/modules/rest/src/Tests/CreateTest.php
index 035480b..28c8af6 100644
--- a/core/modules/rest/src/Tests/CreateTest.php
+++ b/core/modules/rest/src/Tests/CreateTest.php
@@ -23,7 +23,7 @@ class CreateTest extends RESTTestBase {
    *
    * @var array
    */
-  public static $modules = array('hal', 'rest', 'entity_test', 'comment');
+  public static $modules = array('hal', 'rest', 'entity_test', 'comment', 'node');
 
   /**
    * The 'serializer' service.
diff --git a/core/modules/rest/src/Tests/DeleteTest.php b/core/modules/rest/src/Tests/DeleteTest.php
index ccba38e..4cc8016 100644
--- a/core/modules/rest/src/Tests/DeleteTest.php
+++ b/core/modules/rest/src/Tests/DeleteTest.php
@@ -16,7 +16,7 @@ class DeleteTest extends RESTTestBase {
    *
    * @var array
    */
-  public static $modules = array('hal', 'rest', 'entity_test');
+  public static $modules = array('hal', 'rest', 'entity_test', 'node');
 
   /**
    * Tests several valid and invalid delete requests on all entity types.
diff --git a/core/modules/rest/src/Tests/NodeTest.php b/core/modules/rest/src/Tests/NodeTest.php
index 7f0ed81..9eedd2c 100644
--- a/core/modules/rest/src/Tests/NodeTest.php
+++ b/core/modules/rest/src/Tests/NodeTest.php
@@ -19,7 +19,7 @@ class NodeTest extends RESTTestBase {
    *
    * @var array
    */
-  public static $modules = array('hal', 'rest', 'comment');
+  public static $modules = array('hal', 'rest', 'comment', 'node');
 
   /**
    * Enables node specific REST API configuration and authentication.
diff --git a/core/modules/rest/src/Tests/PageCacheTest.php b/core/modules/rest/src/Tests/PageCacheTest.php
index 0ce66da..b2342b9 100644
--- a/core/modules/rest/src/Tests/PageCacheTest.php
+++ b/core/modules/rest/src/Tests/PageCacheTest.php
@@ -76,7 +76,7 @@ public function testConfigChangePageCache() {
     $this->httpRequest($url, 'GET', NULL, $this->defaultMimeType);
     $this->assertResponse(200, 'HTTP response code is correct.');
     $this->assertHeader('x-drupal-cache', 'MISS');
-    $this->assertCacheTag('config:rest.settings');
+    $this->assertCacheTag('config:rest.resource.entity__entity_test');
     $this->assertCacheTag('entity_test:1');
     $this->assertCacheTag('entity_test_access:field_test_text');
 
@@ -84,17 +84,17 @@ public function testConfigChangePageCache() {
     $this->httpRequest($url, 'GET', NULL, $this->defaultMimeType);
     $this->assertResponse(200, 'HTTP response code is correct.');
     $this->assertHeader('x-drupal-cache', 'HIT');
-    $this->assertCacheTag('config:rest.settings');
+    $this->assertCacheTag('config:rest.resource.entity__entity_test');
     $this->assertCacheTag('entity_test:1');
     $this->assertCacheTag('entity_test_access:field_test_text');
 
-    // Trigger a config save which should clear the page cache, so we should get
-    // a cache miss now for the same request.
-    $this->config('rest.settings')->save();
+    // Trigger a resource config save which should clear the page cache, so we
+    // should get a cache miss now for the same request.
+    $this->resourceConfigStorage->load('entity__entity_test')->save();
     $this->httpRequest($url, 'GET', NULL, $this->defaultMimeType);
     $this->assertResponse(200, 'HTTP response code is correct.');
     $this->assertHeader('x-drupal-cache', 'MISS');
-    $this->assertCacheTag('config:rest.settings');
+    $this->assertCacheTag('config:rest.resource.entity__entity_test');
     $this->assertCacheTag('entity_test:1');
     $this->assertCacheTag('entity_test_access:field_test_text');
 
diff --git a/core/modules/rest/src/Tests/RESTTestBase.php b/core/modules/rest/src/Tests/RESTTestBase.php
index 092af7c..ff18ba6 100644
--- a/core/modules/rest/src/Tests/RESTTestBase.php
+++ b/core/modules/rest/src/Tests/RESTTestBase.php
@@ -11,6 +11,13 @@
 abstract class RESTTestBase extends WebTestBase {
 
   /**
+   * The REST resource config storage.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface
+   */
+  protected $resourceConfigStorage;
+
+  /**
    * The default serialization format to use for testing REST operations.
    *
    * @var string
@@ -51,15 +58,18 @@
    *
    * @var array
    */
-  public static $modules = array('rest', 'entity_test', 'node');
+  public static $modules = array('rest', 'entity_test');
 
   protected function setUp() {
     parent::setUp();
     $this->defaultFormat = 'hal_json';
     $this->defaultMimeType = 'application/hal+json';
     $this->defaultAuth = array('cookie');
+    $this->resourceConfigStorage = $this->container->get('entity_type.manager')->getStorage('rest_resource_config');
     // Create a test content type for node testing.
-    $this->drupalCreateContentType(array('name' => 'resttest', 'type' => 'resttest'));
+    if (in_array('node', static::$modules)) {
+      $this->drupalCreateContentType(array('name' => 'resttest', 'type' => 'resttest'));
+    }
   }
 
   /**
@@ -255,29 +265,43 @@ protected function entityValues($entity_type) {
    * @param array $auth
    *   (Optional) The list of valid authentication methods.
    */
-  protected function enableService($resource_type, $method = 'GET', $format = NULL, $auth = NULL) {
-    // Enable REST API for this entity type.
-    $config = $this->config('rest.settings');
-    $settings = array();
-
+  protected function enableService($resource_type, $method = 'GET', $format = NULL, array $auth = []) {
     if ($resource_type) {
+      // Enable REST API for this entity type.
+      $resource_config_id = str_replace(':', '__', $resource_type);
+      // get entity by id
+      /** @var \Drupal\rest\RestResourceConfigInterface $resource_config */
+      $resource_config = $this->resourceConfigStorage->load($resource_config_id);
+      $resource_config = $resource_config ?: $this->resourceConfigStorage->create(['id' => $resource_config_id, 'granularity' => 'method', 'configuration' => []]);
+      $configuration = $resource_config->get('configuration');
+
       if (is_array($format)) {
-        $settings[$resource_type][$method]['supported_formats'] = $format;
+        for ($i = 0; $i < count($format); $i++) {
+          $configuration[$method]['supported_formats'][] = $format[$i];
+        }
       }
       else {
         if ($format == NULL) {
           $format = $this->defaultFormat;
         }
-        $settings[$resource_type][$method]['supported_formats'][] = $format;
+        $configuration[$method]['supported_formats'][] = $format;
       }
 
-      if ($auth == NULL) {
+      if (!is_array($auth) || empty($auth)) {
         $auth = $this->defaultAuth;
       }
-      $settings[$resource_type][$method]['supported_auth'] = $auth;
+      foreach ($auth as $auth_provider) {
+        $configuration[$method]['supported_auth'][] = $auth_provider;
+      }
+
+      $resource_config->set('configuration', $configuration);
+      $resource_config->save();
+    }
+    else {
+      foreach ($this->resourceConfigStorage->loadMultiple() as $resource_config) {
+        $resource_config->delete();
+      }
     }
-    $config->set('resources', $settings);
-    $config->save();
     $this->rebuildCache();
   }
 
diff --git a/core/modules/rest/src/Tests/ReadTest.php b/core/modules/rest/src/Tests/ReadTest.php
index 31b5db8..4ee05cd 100644
--- a/core/modules/rest/src/Tests/ReadTest.php
+++ b/core/modules/rest/src/Tests/ReadTest.php
@@ -17,7 +17,7 @@ class ReadTest extends RESTTestBase {
    *
    * @var array
    */
-  public static $modules = array('hal', 'rest', 'entity_test');
+  public static $modules = array('hal', 'rest', 'entity_test', 'node');
 
   /**
    * Tests several valid and invalid read requests on all entity types.
diff --git a/core/modules/rest/src/Tests/ResourceTest.php b/core/modules/rest/src/Tests/ResourceTest.php
index df99cc6..509ca62 100644
--- a/core/modules/rest/src/Tests/ResourceTest.php
+++ b/core/modules/rest/src/Tests/ResourceTest.php
@@ -2,7 +2,6 @@
 
 namespace Drupal\rest\Tests;
 
-use Drupal\Component\Plugin\Exception\PluginNotFoundException;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\user\Entity\Role;
 
@@ -32,9 +31,7 @@ class ResourceTest extends RESTTestBase {
    */
   protected function setUp() {
     parent::setUp();
-    $this->config = $this->config('rest.settings');
-
-    // Create an entity programmatically.
+    // Create an entity programmatic.
     $this->entity = $this->entityCreate('entity_test');
     $this->entity->save();
 
@@ -47,20 +44,17 @@ protected function setUp() {
    * Tests that a resource without formats cannot be enabled.
    */
   public function testFormats() {
-    $settings = array(
-      'entity:entity_test' => array(
-        'GET' => array(
-          'supported_auth' => array(
+    $this->resourceConfigStorage->create([
+      'id' => 'entity__entity_test',
+      'granularity' => 'method',
+      'configuration' => [
+        'GET' => [
+          'supported_auth' => [
             'basic_auth',
-          ),
-        ),
-      ),
-    );
-
-    // Attempt to enable the resource.
-    $this->config->set('resources', $settings);
-    $this->config->save();
-    $this->rebuildCache();
+          ],
+        ],
+      ],
+    ])->save();
 
     // Verify that accessing the resource returns 406.
     $response = $this->httpRequest($this->entity->urlInfo()->setRouteParameter('_format', $this->defaultFormat), 'GET');
@@ -77,20 +71,17 @@ public function testFormats() {
    * Tests that a resource without authentication cannot be enabled.
    */
   public function testAuthentication() {
-    $settings = array(
-      'entity:entity_test' => array(
-        'GET' => array(
-          'supported_formats' => array(
+    $this->resourceConfigStorage->create([
+      'id' => 'entity__entity_test',
+      'granularity' => 'method',
+      'configuration' => [
+        'GET' => [
+          'supported_formats' => [
             'hal_json',
-          ),
-        ),
-      ),
-    );
-
-    // Attempt to enable the resource.
-    $this->config->set('resources', $settings);
-    $this->config->save();
-    $this->rebuildCache();
+          ],
+        ],
+      ],
+    ])->save();
 
     // Verify that accessing the resource returns 401.
     $response = $this->httpRequest($this->entity->urlInfo()->setRouteParameter('_format', $this->defaultFormat), 'GET');
@@ -118,30 +109,4 @@ public function testUriPaths() {
     }
   }
 
-  /**
-   * Tests that a resource with a missing plugin does not cause an exception.
-   */
-  public function testMissingPlugin() {
-    $settings = array(
-      'entity:nonexisting' => array(
-        'GET' => array(
-          'supported_formats' => array(
-            'hal_json',
-          ),
-        ),
-      ),
-    );
-
-    try {
-      // Attempt to enable the resource.
-      $this->config->set('resources', $settings);
-      $this->config->save();
-      $this->rebuildCache();
-      $this->pass('rest.settings referencing a missing REST resource plugin does not cause an exception.');
-    }
-    catch (PluginNotFoundException $e) {
-      $this->fail('rest.settings referencing a missing REST resource plugin caused an exception.');
-    }
-  }
-
 }
diff --git a/core/modules/rest/src/Tests/Update/ResourceGranularityRestResourcesConfigEntitiesUpdateTest.php b/core/modules/rest/src/Tests/Update/ResourceGranularityRestResourcesConfigEntitiesUpdateTest.php
new file mode 100644
index 0000000..41e90be
--- /dev/null
+++ b/core/modules/rest/src/Tests/Update/ResourceGranularityRestResourcesConfigEntitiesUpdateTest.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Drupal\rest\Tests\Update;
+
+use Drupal\system\Tests\Update\UpdatePathTestBase;
+
+/**
+ * Tests rest_resource_config entities are simplified when possible.
+ *
+ * @see https://www.drupal.org/node/2721595
+ *
+ * @group rest
+ */
+class ResourceGranularityRestResourcesConfigEntitiesUpdateTest extends UpdatePathTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['rest', 'serialization'];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setDatabaseDumpFiles() {
+    $this->databaseDumpFiles = [
+      __DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
+      __DIR__ . '/../../../../rest/tests/fixtures/update/drupal-8.rest-rest_update_8202.php',
+    ];
+  }
+
+  /**
+   * Tests rest_update_8202().
+   */
+  public function testResourcesConvertedToConfigEntities() {
+    /** @var \Drupal\Core\Entity\EntityStorageInterface $resource_config_storage */
+    $resource_config_storage = $this->container->get('entity_type.manager')->getStorage('rest_resource_config');
+
+    // Make sure we have the expected values before the update.
+    $resource_config_entities = $resource_config_storage->loadMultiple();
+    $this->assertIdentical(['entity__comment', 'entity__node', 'entity__user'], array_keys($resource_config_entities));
+    $this->assertIdentical('method', $resource_config_entities['entity__node']->get('granularity'));
+    $this->assertIdentical('method', $resource_config_entities['entity__comment']->get('granularity'));
+    $this->assertIdentical('method', $resource_config_entities['entity__user']->get('granularity'));
+
+    // Read the existing 'entity:comment' and 'entity:user' resource
+    // configuration so we can verify it after the update.
+    $comment_resource_configuration = $resource_config_entities['entity__comment']->get('configuration');
+    $user_resource_configuration = $resource_config_entities['entity__user']->get('configuration');
+
+    $this->runUpdates();
+
+    // Make sure we have the expected values after the update.
+    $resource_config_entities = $resource_config_storage->loadMultiple();
+    $this->assertIdentical(['entity__comment', 'entity__node', 'entity__user'], array_keys($resource_config_entities));
+    // 'entity:node' should be updated.
+    $this->assertIdentical('resource', $resource_config_entities['entity__node']->get('granularity'));
+    $this->assertidentical($resource_config_entities['entity__node']->get('configuration'), [
+      'methods' => ['GET', 'POST', 'PATCH', 'DELETE'],
+      'formats' => ['hal_json'],
+      'authentication' => ['basic_auth'],
+    ]);
+    // 'entity:comment' should be unchanged.
+    $this->assertIdentical('method', $resource_config_entities['entity__comment']->get('granularity'));
+    $this->assertIdentical($comment_resource_configuration, $resource_config_entities['entity__comment']->get('configuration'));
+    // 'entity:user' should be unchanged.
+    $this->assertIdentical('method', $resource_config_entities['entity__user']->get('granularity'));
+    $this->assertIdentical($user_resource_configuration, $resource_config_entities['entity__user']->get('configuration'));
+  }
+
+}
diff --git a/core/modules/rest/src/Tests/Update/RestConfigurationEntitiesUpdateTest.php b/core/modules/rest/src/Tests/Update/RestConfigurationEntitiesUpdateTest.php
new file mode 100644
index 0000000..3c861d7
--- /dev/null
+++ b/core/modules/rest/src/Tests/Update/RestConfigurationEntitiesUpdateTest.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Drupal\rest\Tests\Update;
+
+use Drupal\system\Tests\Update\UpdatePathTestBase;
+
+/**
+ * Tests that rest.settings is converted to rest_resource_config entities.
+ *
+ * @see https://www.drupal.org/node/2308745
+ *
+ * @group rest
+ */
+class RestConfigurationEntitiesUpdateTest extends UpdatePathTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['rest', 'serialization'];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setDatabaseDumpFiles() {
+    $this->databaseDumpFiles = [
+      __DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
+      __DIR__ . '/../../../../rest/tests/fixtures/update/drupal-8.rest-rest_update_8201.php',
+    ];
+  }
+
+  /**
+   * Tests rest_update_8201().
+   */
+  public function testResourcesConvertedToConfigEntities() {
+    /** @var \Drupal\Core\Entity\EntityStorageInterface $resource_config_storage */
+    $resource_config_storage = $this->container->get('entity_type.manager')->getStorage('rest_resource_config');
+
+    // Make sure we have the expected values before the update.
+    $rest_settings = $this->config('rest.settings');
+    $this->assertTrue(array_key_exists('resources', $rest_settings->getRawData()));
+    $this->assertTrue(array_key_exists('entity:node', $rest_settings->getRawData()['resources']));
+    $resource_config_entities = $resource_config_storage->loadMultiple();
+    $this->assertIdentical([], array_keys($resource_config_entities));
+
+    // Read the existing 'entity:node' resource configuration so we can verify
+    // it after the update.
+    $node_configuration = $rest_settings->getRawData()['resources']['entity:node'];
+
+    $this->runUpdates();
+
+    // Make sure we have the expected values after the update.
+    $rest_settings = $this->config('rest.settings');
+    $this->assertFalse(array_key_exists('resources', $rest_settings->getRawData()));
+    $resource_config_entities = $resource_config_storage->loadMultiple();
+    $this->assertIdentical(['entity__node'], array_keys($resource_config_entities));
+    $node_resource_config_entity = $resource_config_entities['entity__node'];
+    $this->assertIdentical('method', $node_resource_config_entity->get('granularity'));
+    $this->assertIdentical($node_configuration,$node_resource_config_entity->get('configuration'));
+  }
+
+}
diff --git a/core/modules/rest/src/Tests/UpdateTest.php b/core/modules/rest/src/Tests/UpdateTest.php
index 98df739..1d99cf2 100644
--- a/core/modules/rest/src/Tests/UpdateTest.php
+++ b/core/modules/rest/src/Tests/UpdateTest.php
@@ -23,7 +23,7 @@ class UpdateTest extends RESTTestBase {
    *
    * @var array
    */
-  public static $modules = ['hal', 'rest', 'entity_test', 'comment'];
+  public static $modules = ['hal', 'rest', 'entity_test', 'node', 'comment'];
 
   /**
    * {@inheritdoc}
diff --git a/core/modules/rest/tests/fixtures/update/drupal-8.rest-rest_update_8201.php b/core/modules/rest/tests/fixtures/update/drupal-8.rest-rest_update_8201.php
new file mode 100644
index 0000000..f035e94
--- /dev/null
+++ b/core/modules/rest/tests/fixtures/update/drupal-8.rest-rest_update_8201.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * @file
+ * Contains database additions to drupal-8.bare.standard.php.gz for testing the
+ * upgrade path of rest_update_8201().
+ */
+
+use Drupal\Core\Database\Database;
+
+$connection = Database::getConnection();
+
+// Set the schema version.
+$connection->insert('key_value')
+  ->fields([
+    'collection' => 'system.schema',
+    'name' => 'rest',
+    'value' => 'i:8000;',
+  ])
+  ->fields([
+    'collection' => 'system.schema',
+    'name' => 'serialization',
+    'value' => 'i:8000;',
+  ])
+  ->execute();
+
+// Update core.extension.
+$extensions = $connection->select('config')
+  ->fields('config', ['data'])
+  ->condition('collection', '')
+  ->condition('name', 'core.extension')
+  ->execute()
+  ->fetchField();
+$extensions = unserialize($extensions);
+$extensions['module']['rest'] = 8000;
+$extensions['module']['serialization'] = 8000;
+$connection->update('config')
+  ->fields([
+    'data' => serialize($extensions),
+  ])
+  ->condition('collection', '')
+  ->condition('name', 'core.extension')
+  ->execute();
+
+// Install the rest configuration.
+$config = [
+  'resources' => [
+    'entity:node' => [
+      'GET' => [
+        'supported_formats' => ['json'],
+        'supported_auth' => [],
+      ],
+    ],
+  ],
+  'link_domain' => '~',
+];
+$data = $connection->insert('config')
+  ->fields([
+    'name' => 'rest.settings',
+    'data' => serialize($config),
+    'collection' => ''
+  ])
+  ->execute();
diff --git a/core/modules/rest/tests/fixtures/update/drupal-8.rest-rest_update_8202.php b/core/modules/rest/tests/fixtures/update/drupal-8.rest-rest_update_8202.php
new file mode 100644
index 0000000000000000000000000000000000000000..868db72091a2055e8cd23050a09d9d37ce817fde
GIT binary patch
literal 4293
zcmcInZExE)5bkIH3PLC#Cy1TcN#Eqv+mN)_fUO&v?!%B20wd9m5K1&CDotASzwbF7
zrAVm*Y*_Q5sZ1X4<+<mM#~*&&Ty93A{oUP>*cCsm3a#kl*i=pyRV{Mq<b|x2ka=FX
z!eGf6k=te?_2lLBLRvMgovd<c^C?tK*Pq48Sm9LdifS#~rPtSN)>h_9Y^1vsW+g1P
z(`EzhDt$RWn2&d+FuOk*H86GJwVxlGa$_o0x${$H3#p6Gk{meyTb6n;6twF6BskiA
zEJmZJnW-w3(JAp-v=T|ORt^D%o$(GN_xHuAa^$V3vrAP<ajk5PbJM<($r6w#>&Eew
zx=pX8ZdAN85@NDk6-wvz_zXXUh%=)#n~lZmrHJd>+NpBNn&Ukds${8no4{ySS*br&
zBtT}i@H-#BgBf^!hvL7pr*2f%IMqV*mrt0;43e&HoT>ss-FFR`d!SF2wIU$KK%`m~
z>mgX};+{B*iDq#K60%G+I@STXC*p9tx9>5;&d{SE*E!D_QZBoEOKGBLo(^1YD61YD
zHSX;U<<8>L<c(JG$FtaH>?4Bp8R~26OSMWpZR4IeA_(N0EV&4_BNXW5!68bt3Wr`2
zpB@XkVG&K1rLt@FE&Y2b8Kw-?Te#b4u2x0G?PH1(auR9HA5N$54)|jlo2eAa!JR3q
zO5N>dd(rhO=a1xQo<v<6L3cRzI?msCcfv3J@d5gx<nUGUrA}tgljzCr6E>J`EWEyn
z7IpG6iCS;y=j@pmLl~~e(oCXZM2m~$fVPhOmNw?9*`(+ab+nMlEcsISZR|Epkw=TK
z_l>EWjj@h|k6K}bK%zx5gW^|7w9(DFs8U{=ld)M6p{-VpmKG?J=pij1CJ~3wSWi7~
zZVq9@<`bAR9B(?C2gw)mh~u)V%9Xu~i_FhpAE!6e6w%8ncW;}+4k>EGeomN%D0&|r
z&Fg}C2cdb8MD!n^zi6sl1Ilg<l3G^l%;bLc-cQOFh$S2C(!SV3>mB$tFuuzJ2TiEL
zM>w;Me6&Ys69m>=7r=*>poXOB6TT61w5*ZwIyrpN@sA{0;=RW={&`+=4!mZjov{V-
zEA=red3R^?68EbH^hA$2fr<k%%M{x>LIi!T2a@xDKYKCQa@OcySP$8Rf3-7Ku068%
zRj_ZZ*MTj*c23(v#1|%o%uCP`)F_L(P3jyz`E_E8_18W9@qv4QNPr0yPNf4TGu(ks
zRPuKi>!P~yNCeU%5-8I0LTQjv>kFA(c~p@ST=c)u##?L3KYjVoPACPYTRJe}K?h}_
zF>ISvwD7C!a9H)`!UoEX#4#kLn3agYBtK^@Rx#64YU2I<8T;Ms_l{BlL*q9m*aL@(
zuQg3OWQyAkvoAP>?uXxfMw~i}S?ohNNHbLXO-g>37R?GhkeJ0Ot^EOy6)NKWo6`?n
z_Hg@CJN5{3h|;c`5-9eQf>VgVZ2ZJXw54PbW%}8w;S}{Q(iOTuXhED!G(0PHf0x?~
z?`Ec{e8U_xuf`TV7EmG>o3%BF5kI$dM=Y{pFN9tK#zc$n3L{zNGU|FVto{f5aE17y
z1^N!Ni;o0Ya*+$F5D`Kom;?FzRGKzL>d6TNTuV;wIGHI+Q>D1)6q-J+T8CUJC}&m$
zEgCh<?Vhe;vfj^hpx&K~lWV`<U3!NIlSz|MFNC?l#h#LWz7aVKkOlwn`WV>ZnrL4k
zL<=SrH*JuOIolH3UoP(!U)wo%IBnneVK7{T`MfvC_;H;3!~>&DZwBke&ik`d`*vU~
zhO_Xu4~PCMA$<Eeu(O0G%L(6pp(F#d@Q}iT#7{#HK1jBsGHo-{!jV?Ed0W{nQz*T}
zk|bAjL67O_n1Ve_5+rY&rYFC?Pg5}!@jhjKyZ&_kVm^EJ>e=+R)bY;FPCvlo2A5Ph
zjBihTD)5M`j1x7UQ1sd?rK<(rc<BCJpQ^tn4pb<}|6@WCO#ELoWP>*>ZtBcWevb+Y
Sp6d275h48#!ZBI)>g*rm>s&Yh

literal 0
HcmV?d00001

diff --git a/core/modules/rest/tests/fixtures/update/rest.resource.entity__comment_2721595.yml b/core/modules/rest/tests/fixtures/update/rest.resource.entity__comment_2721595.yml
new file mode 100644
index 0000000..7508bd6
--- /dev/null
+++ b/core/modules/rest/tests/fixtures/update/rest.resource.entity__comment_2721595.yml
@@ -0,0 +1,30 @@
+id: entity__comment
+plugin_id: 'entity:comment'
+granularity: method
+configuration:
+  GET:
+    supported_formats:
+      - hal_json
+      - xml
+    supported_auth:
+      - basic_auth
+  POST:
+    supported_formats:
+      - hal_json
+    supported_auth:
+      - basic_auth
+  PATCH:
+    supported_formats:
+      - hal_json
+    supported_auth:
+      - basic_auth
+  DELETE:
+    supported_formats:
+      - hal_json
+    supported_auth:
+      - basic_auth
+dependencies:
+  module:
+    - node
+    - basic_auth
+    - hal
diff --git a/core/modules/rest/tests/fixtures/update/rest.resource.entity__node_2721595.yml b/core/modules/rest/tests/fixtures/update/rest.resource.entity__node_2721595.yml
new file mode 100644
index 0000000..c7da93f
--- /dev/null
+++ b/core/modules/rest/tests/fixtures/update/rest.resource.entity__node_2721595.yml
@@ -0,0 +1,29 @@
+id: entity__node
+plugin_id: 'entity:node'
+granularity: method
+configuration:
+  GET:
+    supported_formats:
+      - hal_json
+    supported_auth:
+      - basic_auth
+  POST:
+    supported_formats:
+      - hal_json
+    supported_auth:
+      - basic_auth
+  PATCH:
+    supported_formats:
+      - hal_json
+    supported_auth:
+      - basic_auth
+  DELETE:
+    supported_formats:
+      - hal_json
+    supported_auth:
+      - basic_auth
+dependencies:
+  module:
+    - node
+    - basic_auth
+    - hal
diff --git a/core/modules/rest/tests/fixtures/update/rest.resource.entity__user_2721595.yml b/core/modules/rest/tests/fixtures/update/rest.resource.entity__user_2721595.yml
new file mode 100644
index 0000000..7715178
--- /dev/null
+++ b/core/modules/rest/tests/fixtures/update/rest.resource.entity__user_2721595.yml
@@ -0,0 +1,30 @@
+id: entity__user
+plugin_id: 'entity:user'
+granularity: method
+configuration:
+  GET:
+    supported_formats:
+      - hal_json
+    supported_auth:
+      - basic_auth
+      - oauth
+  POST:
+    supported_formats:
+      - hal_json
+    supported_auth:
+      - basic_auth
+  PATCH:
+    supported_formats:
+      - hal_json
+    supported_auth:
+      - basic_auth
+  DELETE:
+    supported_formats:
+      - hal_json
+    supported_auth:
+      - basic_auth
+dependencies:
+  module:
+    - node
+    - basic_auth
+    - hal
diff --git a/core/modules/rest/tests/src/Kernel/RequestHandlerTest.php b/core/modules/rest/tests/src/Kernel/RequestHandlerTest.php
index 36a482c..e09b175 100644
--- a/core/modules/rest/tests/src/Kernel/RequestHandlerTest.php
+++ b/core/modules/rest/tests/src/Kernel/RequestHandlerTest.php
@@ -1,18 +1,14 @@
 <?php
 
-/**
- * @file
- * Contains \Drupal\Tests\rest\Kernel\RequestHandlerTest.
- */
-
 namespace Drupal\Tests\rest\Kernel;
 
+use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Routing\RouteMatch;
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\rest\Plugin\ResourceBase;
-use Drupal\rest\Plugin\Type\ResourcePluginManager;
 use Drupal\rest\RequestHandler;
 use Drupal\rest\ResourceResponse;
+use Drupal\rest\RestResourceConfigInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\Routing\Route;
 
@@ -32,11 +28,19 @@ class RequestHandlerTest extends KernelTestBase {
   public static $modules = ['serialization', 'rest'];
 
   /**
+   * The entity storage.
+   *
+   * @var \Prophecy\Prophecy\ObjectProphecy
+   */
+  protected $entityStorage;
+
+  /**
    * {@inheritdoc}
    */
   public function setUp() {
     parent::setUp();
-    $this->requestHandler = new RequestHandler();
+    $this->entityStorage = $this->prophesize(EntityStorageInterface::class);
+    $this->requestHandler = new RequestHandler($this->entityStorage->reveal());
     $this->requestHandler->setContainer($this->container);
   }
 
@@ -47,17 +51,19 @@ public function setUp() {
    */
   public function testBaseHandler() {
     $request = new Request();
-    $route_match = new RouteMatch('test', new Route('/rest/test', ['_plugin' => 'restplugin', '_format' => 'json']));
+    $route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => 'restplugin', '_format' => 'json']));
 
     $resource = $this->prophesize(StubRequestHandlerResourcePlugin::class);
     $resource->get(NULL, $request)
       ->shouldBeCalled();
 
-    // Setup stub plugin manager that will return our plugin.
-    $stub = $this->prophesize(ResourcePluginManager::class);
-    $stub->createInstance('restplugin')
-      ->willReturn($resource->reveal());
-    $this->container->set('plugin.manager.rest', $stub->reveal());
+    // Setup the configuration.
+    $config = $this->prophesize(RestResourceConfigInterface::class);
+    $config->getResourcePlugin()->willReturn($resource->reveal());
+    $config->getCacheContexts()->willReturn([]);
+    $config->getCacheTags()->willReturn([]);
+    $config->getCacheMaxAge()->willReturn(12);
+    $this->entityStorage->load('restplugin')->willReturn($config->reveal());
 
     // Response returns NULL this time because response from plugin is not
     // a ResourceResponse so it is passed through directly.
@@ -89,15 +95,17 @@ public function testBaseHandler() {
    */
   public function testSerialization($data) {
     $request = new Request();
-    $route_match = new RouteMatch('test', new Route('/rest/test', ['_plugin' => 'restplugin', '_format' => 'json']));
+    $route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => 'restplugin', '_format' => 'json']));
 
     $resource = $this->prophesize(StubRequestHandlerResourcePlugin::class);
 
-    // Setup stub plugin manager that will return our plugin.
-    $stub = $this->prophesize(ResourcePluginManager::class);
-    $stub->createInstance('restplugin')
-      ->willReturn($resource->reveal());
-    $this->container->set('plugin.manager.rest', $stub->reveal());
+    // Setup the configuration.
+    $config = $this->prophesize(RestResourceConfigInterface::class);
+    $config->getResourcePlugin()->willReturn($resource->reveal());
+    $config->getCacheContexts()->willReturn([]);
+    $config->getCacheTags()->willReturn([]);
+    $config->getCacheMaxAge()->willReturn(12);
+    $this->entityStorage->load('restplugin')->willReturn($config->reveal());
 
     $response = new ResourceResponse($data);
     $resource->get(NULL, $request)
diff --git a/core/modules/serialization/src/EventSubscriber/DefaultExceptionSubscriber.php b/core/modules/serialization/src/EventSubscriber/DefaultExceptionSubscriber.php
new file mode 100644
index 0000000..b4d59de
--- /dev/null
+++ b/core/modules/serialization/src/EventSubscriber/DefaultExceptionSubscriber.php
@@ -0,0 +1,134 @@
+<?php
+
+namespace Drupal\serialization\EventSubscriber;
+
+use Drupal\Core\EventSubscriber\HttpExceptionSubscriberBase;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
+use Symfony\Component\Serializer\SerializerInterface;
+
+/**
+ * Exception subscriber for handling default  error responses in serialization formats.
+ */
+class DefaultExceptionSubscriber extends HttpExceptionSubscriberBase {
+
+  /**
+   * The serializer.
+   *
+   * @var \Symfony\Component\Serializer\Serializer
+   */
+  protected $serializer;
+
+  /**
+   * The available serialization formats.
+   *
+   * @var array
+   */
+  protected $serializerFormats = [];
+
+  /**
+   * DefaultExceptionSubscriber constructor.
+   *
+   * @param \Symfony\Component\Serializer\SerializerInterface $serializer
+   *   The serializer service.
+   * @param array $serializer_formats
+   *   The available serialization formats.
+   */
+  public function __construct(SerializerInterface $serializer, array $serializer_formats) {
+    $this->serializer = $serializer;
+    $this->serializerFormats = $serializer_formats;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getHandledFormats() {
+    return $this->serializerFormats;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static function getPriority() {
+    // This will fire after the most common HTML handler, since HTML requests
+    // are still more common than HTTP requests.
+    return -75;
+  }
+
+  /**
+   * Handles a 400 error for HTTP.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
+   *   The event to process.
+   */
+  public function on400(GetResponseForExceptionEvent $event) {
+    $this->setEventResponse($event, Response::HTTP_BAD_REQUEST);
+  }
+
+  /**
+   * Handles a 403 error for HTTP.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
+   *   The event to process.
+   */
+  public function on403(GetResponseForExceptionEvent $event) {
+    $this->setEventResponse($event, Response::HTTP_FORBIDDEN);
+  }
+
+  /**
+   * Handles a 404 error for HTTP.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
+   *   The event to process.
+   */
+  public function on404(GetResponseForExceptionEvent $event) {
+    $this->setEventResponse($event, Response::HTTP_NOT_FOUND);
+  }
+
+  /**
+   * Handles a 405 error for HTTP.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
+   *   The event to process.
+   */
+  public function on405(GetResponseForExceptionEvent $event) {
+    $this->setEventResponse($event, Response::HTTP_METHOD_NOT_ALLOWED);
+  }
+
+  /**
+   * Handles a 406 error for HTTP.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
+   *   The event to process.
+   */
+  public function on406(GetResponseForExceptionEvent $event) {
+    $this->setEventResponse($event, Response::HTTP_NOT_ACCEPTABLE);
+  }
+
+  /**
+   * Handles a 422 error for HTTP.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
+   *   The event to process.
+   */
+  public function on422(GetResponseForExceptionEvent $event) {
+    $this->setEventResponse($event, Response::HTTP_UNPROCESSABLE_ENTITY);
+  }
+
+  /**
+   * Sets the Response for the exception event.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
+   *   The current exception event.
+   * @param int $status
+   *   The HTTP status code to set for the response.
+   */
+  protected function setEventResponse(GetResponseForExceptionEvent $event, $status) {
+    $format = $event->getRequest()->getRequestFormat();
+    $content = ['message' => $event->getException()->getMessage()];
+    $encoded_content = $this->serializer->serialize($content, $format);
+    $response = new Response($encoded_content, $status);
+    $event->setResponse($response);
+  }
+
+}
diff --git a/core/modules/system/src/Tests/System/ResponseGeneratorTest.php b/core/modules/system/src/Tests/System/ResponseGeneratorTest.php
index 9d6cf4f..d0d22a2 100644
--- a/core/modules/system/src/Tests/System/ResponseGeneratorTest.php
+++ b/core/modules/system/src/Tests/System/ResponseGeneratorTest.php
@@ -16,7 +16,7 @@ class ResponseGeneratorTest extends RESTTestBase {
    *
    * @var array
    */
-  public static $modules = array('hal', 'rest', 'node');
+  public static $modules = array('hal', 'rest', 'node', 'basic_auth');
 
   /**
    * {@inheritdoc}
diff --git a/core/modules/system/src/Tests/Update/UpdatePathTestBase.php b/core/modules/system/src/Tests/Update/UpdatePathTestBase.php
index 19e1fcf..82169ad 100644
--- a/core/modules/system/src/Tests/Update/UpdatePathTestBase.php
+++ b/core/modules/system/src/Tests/Update/UpdatePathTestBase.php
@@ -265,7 +265,15 @@ protected function runUpdates() {
     }
 
     // Ensure that the update hooks updated all entity schema.
-    $this->assertFalse(\Drupal::service('entity.definition_update_manager')->needsUpdates(), 'After all updates ran, entity schema is up to date.');
+    $needs_updates = \Drupal::entityDefinitionUpdateManager()->needsUpdates();
+    $this->assertFalse($needs_updates, 'After all updates ran, entity schema is up to date.');
+    if ($needs_updates) {
+      foreach (\Drupal::entityDefinitionUpdateManager()->getChangeSummary() as $entity_type_id => $summary) {
+        foreach ($summary as $message) {
+          $this->fail($message);
+        }
+      }
+    }
   }
 
   /**
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index cdaeba6..ba1172e 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -1131,7 +1131,11 @@ function system_update_8004() {
   $manager = \Drupal::entityDefinitionUpdateManager();
   foreach (array_keys(\Drupal::entityManager()
     ->getDefinitions()) as $entity_type_id) {
-    $manager->updateEntityType($manager->getEntityType($entity_type_id));
+    // Only update the entity type if it already exists. This condition is
+    // needed in case new entity types are introduced after this update.
+    if ($entity_type = $manager->getEntityType($entity_type_id)) {
+      $manager->updateEntityType($entity_type);
+    }
   }
 }
 
diff --git a/core/tests/Drupal/Tests/Core/DependencyInjection/Compiler/AuthenticationProviderPassTest.php b/core/tests/Drupal/Tests/Core/DependencyInjection/Compiler/AuthenticationProviderPassTest.php
new file mode 100644
index 0000000..6f99d09
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/DependencyInjection/Compiler/AuthenticationProviderPassTest.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Drupal\Tests\Core\DependencyInjection\Compiler;
+
+use Drupal\Core\DependencyInjection\Compiler\AuthenticationProviderPass;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\Serializer\Serializer;
+
+/**
+ * @coversDefaultClass \Drupal\Core\DependencyInjection\Compiler\AuthenticationProviderPass
+ * @group DependencyInjection
+ */
+class AuthenticationProviderPassTest extends \PHPUnit_Framework_TestCase {
+
+  /**
+   * @covers ::process
+   */
+  public function testEncoders() {
+    $container = new ContainerBuilder();
+    $container->setDefinition('serializer', new Definition(Serializer::class, [[], []]));
+
+    $definition = new Definition('TestClass');
+    $definition->addTag('authentication_provider', ['provider_id' => 'bunny_auth']);
+    $definition->addTag('_provider', ['provider' => 'test_provider_a']);
+    $container->setDefinition('test_provider_a.authentication.bunny_auth', $definition);
+
+    $definition = new Definition('TestClass');
+    $definition->addTag('authentication_provider', ['provider_id' => 'llama_auth', 'priority' => 100]);
+    $definition->addTag('_provider', ['provider' => 'test_provider_a']);
+    $container->setDefinition('test_provider_a.authentication.llama_auth', $definition);
+
+    $definition = new Definition('TestClass');
+    $definition->addTag('authentication_provider', ['provider_id' => 'camel_auth', 'priority' => -100]);
+    $definition->addTag('_provider', ['provider' => 'test_provider_b']);
+    $container->setDefinition('test_provider_b.authentication.camel_auth', $definition);
+
+    $compiler_pass = new AuthenticationProviderPass();
+    $compiler_pass->process($container);
+
+    $this->assertEquals(['bunny_auth' => 'test_provider_a', 'llama_auth' => 'test_provider_a', 'camel_auth' => 'test_provider_b'], $container->getParameter('authentication_providers'));
+  }
+
+}
