diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Shortcut/ShortcutHalJsonAnonTest.php b/core/modules/hal/tests/src/Functional/EntityResource/Shortcut/ShortcutHalJsonAnonTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..c9e8dc00c5eebbd5f4b60dd1d75a97a5e49c444d
--- /dev/null
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Shortcut/ShortcutHalJsonAnonTest.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace Drupal\Tests\hal\Functional\EntityResource\Shortcut;
+
+use Drupal\Core\Cache\Cache;
+use Drupal\Tests\hal\Functional\EntityResource\HalEntityNormalizationTrait;
+use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
+use Drupal\Tests\rest\Functional\EntityResource\Shortcut\ShortcutResourceTestBase;
+
+/**
+ * @group hal
+ */
+class ShortcutHalJsonAnonTest extends ShortcutResourceTestBase {
+
+  use HalEntityNormalizationTrait;
+  use AnonResourceTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['hal'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $format = 'hal_json';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $mimeType = 'application/hal+json';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getExpectedNormalizedEntity() {
+    $default_normalization = parent::getExpectedNormalizedEntity();
+
+    $normalization = $this->applyHalFieldNormalization($default_normalization);
+
+    return $normalization + [
+      '_links' => [
+        'self' => [
+          'href' => $this->baseUrl . '/admin/config/user-interface/shortcut/link/1?_format=hal_json',
+        ],
+        'type' => [
+          'href' => $this->baseUrl . '/rest/type/shortcut/default',
+        ],
+      ],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getNormalizedPostEntity() {
+    return parent::getNormalizedPostEntity() + [
+      '_links' => [
+        'type' => [
+          'href' => $this->baseUrl . '/rest/type/shortcut/default',
+        ],
+      ],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getExpectedCacheContexts() {
+    // The 'url.site' cache context is added for '_links' in the response.
+    return Cache::mergeContexts(parent::getExpectedCacheContexts(), ['url.site']);
+  }
+
+}
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Shortcut/ShortcutHalJsonBasicAuthTest.php b/core/modules/hal/tests/src/Functional/EntityResource/Shortcut/ShortcutHalJsonBasicAuthTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..a5be6dbc88399238e12b395704ac13bebbe9ffad
--- /dev/null
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Shortcut/ShortcutHalJsonBasicAuthTest.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Drupal\Tests\hal\Functional\EntityResource\Shortcut;
+
+use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
+
+/**
+ * @group hal
+ */
+class ShortcutHalJsonBasicAuthTest extends ShortcutHalJsonAnonTest {
+
+  use BasicAuthResourceTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['basic_auth'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $auth = 'basic_auth';
+
+}
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Shortcut/ShortcutHalJsonCookieTest.php b/core/modules/hal/tests/src/Functional/EntityResource/Shortcut/ShortcutHalJsonCookieTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..a48a61f9e62c98d75df441d268804703c7267da9
--- /dev/null
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Shortcut/ShortcutHalJsonCookieTest.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Drupal\Tests\hal\Functional\EntityResource\Shortcut;
+
+use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
+
+/**
+ * @group hal
+ */
+class ShortcutHalJsonCookieTest extends ShortcutHalJsonAnonTest {
+
+  use CookieResourceTestTrait;
+  /**
+   * {@inheritdoc}
+   */
+  protected static $auth = 'cookie';
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Shortcut/ShortcutJsonAnonTest.php b/core/modules/rest/tests/src/Functional/EntityResource/Shortcut/ShortcutJsonAnonTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..8f317b3c05c838e6a76433582418e23aa7956e0e
--- /dev/null
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Shortcut/ShortcutJsonAnonTest.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Drupal\Tests\rest\Functional\EntityResource\Shortcut;
+
+use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
+
+/**
+ * @group rest
+ */
+class ShortcutJsonAnonTest extends ShortcutResourceTestBase {
+
+  use AnonResourceTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $format = 'json';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $mimeType = 'application/json';
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Shortcut/ShortcutJsonBasicAuthTest.php b/core/modules/rest/tests/src/Functional/EntityResource/Shortcut/ShortcutJsonBasicAuthTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..31f1117a92174b8ea253f219a0dc856a1b3ab7cc
--- /dev/null
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Shortcut/ShortcutJsonBasicAuthTest.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Drupal\Tests\rest\Functional\EntityResource\Shortcut;
+
+use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
+
+/**
+ * @group rest
+ */
+class ShortcutJsonBasicAuthTest extends ShortcutResourceTestBase {
+
+  use BasicAuthResourceTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['basic_auth'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $format = 'json';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $mimeType = 'application/json';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $auth = 'basic_auth';
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Shortcut/ShortcutJsonCookieTest.php b/core/modules/rest/tests/src/Functional/EntityResource/Shortcut/ShortcutJsonCookieTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..3a043c879aa8ac3f2c40677faa7e751a89916dc8
--- /dev/null
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Shortcut/ShortcutJsonCookieTest.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Drupal\Tests\rest\Functional\EntityResource\Shortcut;
+
+use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
+
+/**
+ * @group rest
+ */
+class ShortcutJsonCookieTest extends ShortcutResourceTestBase {
+
+  use CookieResourceTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $format = 'json';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $mimeType = 'application/json';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $auth = 'cookie';
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Shortcut/ShortcutResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Shortcut/ShortcutResourceTestBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..fb8ebaad35257b5acdbf8f1b9b83294929487222
--- /dev/null
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Shortcut/ShortcutResourceTestBase.php
@@ -0,0 +1,159 @@
+<?php
+
+namespace Drupal\Tests\rest\Functional\EntityResource\Shortcut;
+
+use Drupal\shortcut\Entity\Shortcut;
+use Drupal\shortcut\Entity\ShortcutSet;
+use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
+
+/**
+ * ResourceTestBase for Shortcut entity.
+ */
+abstract class ShortcutResourceTestBase extends EntityResourceTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['comment', 'shortcut'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $entityTypeId = 'shortcut';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $patchProtectedFieldNames = [];
+
+  /**
+   * The Shortcut entity.
+   *
+   * @var \Drupal\shortcut\ShortcutInterface
+   */
+  protected $entity;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUpAuthorization($method) {
+    switch ($method) {
+      case 'GET':
+      case 'POST':
+      case 'PATCH':
+      case 'DELETE':
+        $this->grantPermissionsToTestedRole(['access shortcuts', 'customize shortcut links']);
+        break;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function createEntity() {
+    // Create shortcut.
+    $shortcut = Shortcut::create([
+      'shortcut_set' => 'default',
+      'title' => t('Comments'),
+      'weight' => -20,
+      'link' => [
+        'uri' => 'internal:/admin/content/comment',
+      ],
+    ]);
+    $shortcut->save();
+
+    return $shortcut;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getExpectedNormalizedEntity() {
+    return [
+      'uuid' => [
+        [
+          'value' => $this->entity->uuid(),
+        ],
+      ],
+      'id' => [
+        [
+          'value' => (int) $this->entity->id(),
+        ],
+      ],
+      'title' => [
+        [
+          'value' => 'Comments',
+        ],
+      ],
+      'shortcut_set' => [
+        [
+          'target_id' => 'default',
+          'target_type' => 'shortcut_set',
+          'target_uuid' => ShortcutSet::load('default')->uuid(),
+        ],
+      ],
+      'link' => [
+        [
+          'uri' => 'internal:/admin/content/comment',
+          'title' => NULL,
+          'options' => [],
+        ],
+      ],
+      'weight' => [
+        [
+          'value' => -20,
+        ],
+      ],
+      'langcode' => [
+        [
+          'value' => 'en',
+        ],
+      ],
+      'default_langcode' => [
+        [
+          'value' => TRUE,
+        ],
+      ],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getNormalizedPostEntity() {
+    return [
+      'title' => [
+        [
+          'value' => 'Comments',
+        ],
+      ],
+      'link' => [
+        [
+          'uri' => 'internal:/',
+        ],
+      ],
+      'shortcut_set' => 'default',
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getExpectedUnauthorizedAccessMessage($method) {
+    if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
+      return parent::getExpectedUnauthorizedAccessMessage($method);
+    }
+
+    switch ($method) {
+      case 'GET':
+      case 'POST':
+      case 'PATCH':
+      case 'DELETE':
+        return "The shortcut set must be the currently displayed set for the user and the user must have 'access shortcuts' AND 'customize shortcut links' permissions.";
+
+      default:
+        return parent::getExpectedUnauthorizedAccessMessage($method);
+    }
+  }
+
+}
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index 156b4914c55768575d1c1a5bd5ed472c949c0565..8ace7bdb10b0d76d813268faf9d1e7510df1efa3 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -65,7 +65,11 @@ function shortcut_set_edit_access(ShortcutSetInterface $shortcut_set = NULL) {
   // Sufficiently-privileged users can edit their currently displayed shortcut
   // set, but not other sets. They must also be able to access shortcuts.
   $may_edit_current_shortcut_set = $account->hasPermission('customize shortcut links') && (!isset($shortcut_set) || $shortcut_set == shortcut_current_displayed_set()) && $account->hasPermission('access shortcuts');
-  return AccessResult::allowedIf($may_edit_current_shortcut_set)->cachePerPermissions();
+  $result = AccessResult::allowedIf($may_edit_current_shortcut_set)->cachePerPermissions();
+  if (!$result->isAllowed()) {
+    $result->setReason("The shortcut set must be the currently displayed set for the user and the user must have 'access shortcuts' AND 'customize shortcut links' permissions.");
+  }
+  return $result;
 }
 
 /**