 .../EventSubscriber/AuthenticationSubscriber.php   | 16 +++++++
 .../RouteAccessResponseSubscriber.php              |  2 +-
 .../src/Plugin/rest/resource/EntityResource.php    | 55 +++++++---------------
 core/modules/rest/src/Routing/ResourceRoutes.php   |  3 +-
 4 files changed, 35 insertions(+), 41 deletions(-)

diff --git a/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php
index be3f55e..df5de13 100644
--- a/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php
@@ -125,6 +125,21 @@ public function onExceptionSendChallenge(GetResponseForExceptionEvent $event) {
   }
 
   /**
+   * Detect disallowed authentication methods on access denied exceptions.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
+   */
+  public function onExceptionAccessDenied(GetResponseForExceptionEvent $event) {
+    if ($event->getRequestType() === HttpKernelInterface::MASTER_REQUEST) {
+      $request = $event->getRequest();
+      $exception = $event->getException();
+      if ($exception instanceof AccessDeniedHttpException && $this->authenticationProvider->applies($request) && !$this->filter->appliesToRoutedRequest($request, TRUE)) {
+        $event->setException(new AccessDeniedHttpException('The used authentication method is not allowed on this route.'));
+      }
+    }
+  }
+
+  /**
    * {@inheritdoc}
    */
   public static function getSubscribedEvents() {
@@ -137,6 +152,7 @@ public static function getSubscribedEvents() {
     // Access check must be performed after routing.
     $events[KernelEvents::REQUEST][] = ['onKernelRequestFilterProvider', 31];
     $events[KernelEvents::EXCEPTION][] = ['onExceptionSendChallenge', 75];
+    $events[KernelEvents::EXCEPTION][] = ['onExceptionAccessDenied', 75];
     return $events;
   }
 
diff --git a/core/lib/Drupal/Core/EventSubscriber/RouteAccessResponseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/RouteAccessResponseSubscriber.php
index b2f1312..83d277f 100644
--- a/core/lib/Drupal/Core/EventSubscriber/RouteAccessResponseSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/RouteAccessResponseSubscriber.php
@@ -50,7 +50,7 @@ public function onRespond(FilterResponseEvent $event) {
   public static function getSubscribedEvents() {
     // Priority 10, so that it runs before FinishResponseSubscriber, which will
     // expose the cacheability metadata in the form of headers.
-    $events[KernelEvents::RESPONSE][] = ['onRespond', 10];
+    $events[KernelEvents::RESPONSE][] = ['onRespond', 110];
     return $events;
   }
 
diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
index 5d9849d..a945b83 100644
--- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
@@ -120,14 +120,8 @@ public static function create(ContainerInterface $container, array $configuratio
    * @throws \Symfony\Component\HttpKernel\Exception\HttpException
    */
   public function get(EntityInterface $entity) {
-    $entity_access = $entity->access('view', NULL, TRUE);
-    if (!$entity_access->isAllowed()) {
-      throw new AccessDeniedHttpException($entity_access->getReason() ?: $this->generateFallbackAccessDeniedMessage($entity, 'view'));
-    }
-
     $response = new ResourceResponse($entity, 200);
     $response->addCacheableDependency($entity);
-    $response->addCacheableDependency($entity_access);
 
     if ($entity instanceof FieldableEntityInterface) {
       foreach ($entity as $field_name => $field) {
@@ -162,10 +156,6 @@ public function post(EntityInterface $entity = NULL) {
       throw new BadRequestHttpException('No entity content received.');
     }
 
-    $entity_access = $entity->access('create', NULL, TRUE);
-    if (!$entity_access->isAllowed()) {
-      throw new AccessDeniedHttpException($entity_access->getReason() ?: $this->generateFallbackAccessDeniedMessage($entity, 'create'));
-    }
     $definition = $this->getPluginDefinition();
     // Verify that the deserialized entity is of the type that we expect to
     // prevent security issues.
@@ -257,10 +247,6 @@ public function patch(EntityInterface $original_entity, EntityInterface $entity
     if ($entity->getEntityTypeId() != $definition['entity_type']) {
       throw new BadRequestHttpException('Invalid entity type');
     }
-    $entity_access = $original_entity->access('update', NULL, TRUE);
-    if (!$entity_access->isAllowed()) {
-      throw new AccessDeniedHttpException($entity_access->getReason() ?: $this->generateFallbackAccessDeniedMessage($entity, 'update'));
-    }
 
     // Overwrite the received properties.
     $entity_keys = $entity->getEntityType()->getKeys();
@@ -322,10 +308,6 @@ public function patch(EntityInterface $original_entity, EntityInterface $entity
    * @throws \Symfony\Component\HttpKernel\Exception\HttpException
    */
   public function delete(EntityInterface $entity) {
-    $entity_access = $entity->access('delete', NULL, TRUE);
-    if (!$entity_access->isAllowed()) {
-      throw new AccessDeniedHttpException($entity_access->getReason() ?: $this->generateFallbackAccessDeniedMessage($entity, 'delete'));
-    }
     try {
       $entity->delete();
       $this->logger->notice('Deleted entity %type with ID %id.', ['%type' => $entity->getEntityTypeId(), '%id' => $entity->id()]);
@@ -339,26 +321,6 @@ public function delete(EntityInterface $entity) {
   }
 
   /**
-   * Generates a fallback access denied message, when no specific reason is set.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity object.
-   * @param string $operation
-   *   The disallowed entity operation.
-   *
-   * @return string
-   *   The proper message to display in the AccessDeniedHttpException.
-   */
-  protected function generateFallbackAccessDeniedMessage(EntityInterface $entity, $operation) {
-    $message = "You are not authorized to {$operation} this {$entity->getEntityTypeId()} entity";
-
-    if ($entity->bundle() !== $entity->getEntityTypeId()) {
-      $message .= " of bundle {$entity->bundle()}";
-    }
-    return "{$message}.";
-  }
-
-  /**
    * {@inheritdoc}
    */
   public function permissions() {
@@ -378,6 +340,23 @@ public function permissions() {
    */
   protected function getBaseRoute($canonical_path, $method) {
     $route = parent::getBaseRoute($canonical_path, $method);
+
+    switch ($method) {
+      case 'GET':
+        $route->setRequirement('_entity_access', $this->entityType->id() . '.view');
+        break;
+      case 'POST':
+        $route->setRequirement('_entity_create_access', $this->entityType->id());
+        break;
+      case 'PATCH':
+        $route->setRequirement('_entity_access', $this->entityType->id() . '.update');
+        break;
+      case 'DELETE':
+        $route->setRequirement('_entity_access', $this->entityType->id() . '.delete');
+        break;
+    }
+
+    if ($method)
     $definition = $this->getPluginDefinition();
 
     $parameters = $route->getOption('parameters') ?: [];
diff --git a/core/modules/rest/src/Routing/ResourceRoutes.php b/core/modules/rest/src/Routing/ResourceRoutes.php
index 5ba4c5d..a34ec00 100644
--- a/core/modules/rest/src/Routing/ResourceRoutes.php
+++ b/core/modules/rest/src/Routing/ResourceRoutes.php
@@ -94,8 +94,6 @@ protected function getRoutesForResourceConfig(RestResourceConfigInterface $rest_
       $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('_csrf_request_header_token', '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', [':id' => $rest_resource_config->id()]);
@@ -120,6 +118,7 @@ protected function getRoutesForResourceConfig(RestResourceConfigInterface $rest_
         //   allow request bodies to be sent
         // - set the allowed authentication providers
         if (in_array($method, ['POST', 'PATCH', 'PUT'], TRUE)) {
+          $route->setRequirement('_csrf_request_header_token', 'TRUE');
           // Restrict the incoming HTTP Content-type header to the allowed
           // formats.
           $route->addRequirements(['_content_type_format' => implode('|', $rest_resource_config->getFormats($method))]);
