diff --git a/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php b/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php
index bf415af..b4c77ee 100644
--- a/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php
+++ b/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Entity\Element;
 
 use Drupal\Component\Utility\Tags;
+use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element\Textfield;
 use Drupal\user\EntityOwnerInterface;
@@ -15,6 +16,9 @@
 /**
  * Provides an entity autocomplete form element.
  *
+ * The #default_value accepted by this element is either an entity object or an
+ * array of entity objects.
+ *
  * @FormElement("entity_autocomplete")
  */
 class EntityAutocomplete extends Textfield {
@@ -35,18 +39,43 @@ public function getInfo() {
     // This should only be set to FALSE if proper validation by the selection
     // handler is performed at another level on the extracted form values.
     $info['#validate_reference'] = TRUE;
+    // IMPORTANT! This should only be set to FALSE if the #default_value
+    // property is processed at another level (e.g. by a Field API widget) and
+    // it's value is properly checked for access.
+    $info['#process_default_value'] = TRUE;
 
     $info['#element_validate'] = array(array($class, 'validateEntityAutocomplete'));
     array_unshift($info['#process'], array($class, 'processEntityAutocomplete'));
 
-    // @todo Consider providing better DX for #default_value? Maybe we impose an
-    // array('label' => .., 'value' => ..) structure instead of manually
-    // composing the textfield string?. See https://www.drupal.org/node/2418249.
-
     return $info;
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
+    // Process the #default_value property.
+    if ($input === FALSE && isset($element['#default_value']) && $element['#process_default_value']) {
+      if (is_array($element['#default_value']) && $element['#tags'] !== TRUE) {
+        throw new \InvalidArgumentException('The #default_value property is an array but the form element does not allow multiple values.');
+      }
+      elseif (!is_array($element['#default_value'])) {
+        // Convert the default value into an array for easier processing in
+        // static::getEntityLabels().
+        $element['#default_value'] = array($element['#default_value']);
+      }
+
+      if ($element['#default_value'] && !(reset($element['#default_value']) instanceof EntityInterface)) {
+        throw new \InvalidArgumentException('The #default_value property has to be an entity object or an array of entity objects.');
+      }
+
+      // Extract the labels from the passed-in entity objects, taking access
+      // checks into account.
+      return static::getEntityLabels($element['#default_value']);
+    }
+  }
+
+  /**
    * Adds entity autocomplete functionality to a form element.
    *
    * @param array $element
@@ -160,6 +189,35 @@ public static function validateEntityAutocomplete(array &$element, FormStateInte
   }
 
   /**
+   * Converts an array of entity objects into a string of entity labels.
+   *
+   * This method is also responsible for checking the 'view' access on the
+   * passed-in entities.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface[] $entities
+   *   An array of entity objects.
+   *
+   * @return string
+   *   A string of entity labels separated by commas.
+   */
+  public static function getEntityLabels(array $entities) {
+    $entity_labels = array();
+    foreach ($entities as $entity) {
+      $label = $entity->label();
+
+      // Take into account "autocreated" entities.
+      if (!$entity->isNew()) {
+        $label .= ' (' . $entity->id() . ')';
+      }
+
+      // Labels containing commas or quotes must be wrapped in quotes.
+      $entity_labels[] = Tags::encode($label);
+    }
+
+    return implode(', ', $entity_labels);
+  }
+
+  /**
    * Extracts the entity ID from the autocompletion result.
    *
    * @param string $input
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/EntityReferenceAutocompleteTagsWidget.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/EntityReferenceAutocompleteTagsWidget.php
index 2b2f003..64b46c2 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/EntityReferenceAutocompleteTagsWidget.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/EntityReferenceAutocompleteTagsWidget.php
@@ -32,6 +32,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
     $element = parent::formElement($items, $delta, $element, $form, $form_state);
 
     $element['target_id']['#tags'] = TRUE;
+    $element['target_id']['#default_value'] = $items->referencedEntities();
 
     return $element;
   }
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/EntityReferenceAutocompleteWidget.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/EntityReferenceAutocompleteWidget.php
index 602eaf4..2d99619 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/EntityReferenceAutocompleteWidget.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/EntityReferenceAutocompleteWidget.php
@@ -7,8 +7,6 @@
 
 namespace Drupal\Core\Field\Plugin\Field\FieldWidget;
 
-use Drupal\Component\Utility\Tags;
-use Drupal\Core\Field\EntityReferenceFieldItemListInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\WidgetBase;
 use Drupal\Core\Form\FormStateInterface;
@@ -94,6 +92,7 @@ public function settingsSummary() {
    */
   public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
     $entity = $items->getEntity();
+    $referenced_entities = $items->referencedEntities();
 
     $element += array(
       '#type' => 'entity_autocomplete',
@@ -104,7 +103,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
       // the 'ValidReference' constraint.
       '#validate_reference' => FALSE,
       '#maxlength' => 1024,
-      '#default_value' => implode(', ', $this->getLabels($items, $delta)),
+      '#default_value' => isset($referenced_entities[$delta]) ? $referenced_entities[$delta] : NULL,
       '#size' => $this->getSetting('size'),
       '#placeholder' => $this->getSetting('placeholder'),
     );
@@ -143,35 +142,6 @@ public function massageFormValues(array $values, array $form, FormStateInterface
   }
 
   /**
-   * Gets the entity labels.
-   */
-  protected function getLabels(EntityReferenceFieldItemListInterface $items, $delta) {
-    if ($items->isEmpty()) {
-      return array();
-    }
-
-    $entity_labels = array();
-    $handles_multiple_values = $this->handlesMultipleValues();
-    foreach ($items->referencedEntities() as $referenced_delta => $referenced_entity) {
-      // The autocomplete widget outputs one entity label per form element.
-      if (!$handles_multiple_values && $referenced_delta != $delta) {
-        continue;
-      }
-
-      $key = $referenced_entity->label();
-
-      // Take into account "autocreate" items.
-      if (!$referenced_entity->isNew()) {
-        $key .= ' (' . $referenced_entity->id() . ')';
-      }
-
-      // Labels containing commas or quotes must be wrapped in quotes.
-      $entity_labels[] = Tags::encode($key);
-    }
-    return $entity_labels;
-  }
-
-  /**
    * Returns the name of the bundle which will be used for autocreated entities.
    *
    * @return string
diff --git a/core/modules/entity_reference/src/Tests/EntityReferenceIntegrationTest.php b/core/modules/entity_reference/src/Tests/EntityReferenceIntegrationTest.php
index 6c5f8fc..cabaf22 100644
--- a/core/modules/entity_reference/src/Tests/EntityReferenceIntegrationTest.php
+++ b/core/modules/entity_reference/src/Tests/EntityReferenceIntegrationTest.php
@@ -55,7 +55,7 @@ protected function setUp() {
     parent::setUp();
 
     // Create a test user.
-    $web_user = $this->drupalCreateUser(array('administer entity_test content', 'administer entity_test fields'));
+    $web_user = $this->drupalCreateUser(array('administer entity_test content', 'administer entity_test fields', 'view test entity'));
     $this->drupalLogin($web_user);
   }
 
diff --git a/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php b/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
index 169204a..09dfd62 100644
--- a/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
+++ b/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\link\Plugin\Field\FieldWidget;
 
-use Drupal\Component\Utility\Unicode;
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Entity\Element\EntityAutocomplete;
 use Drupal\Core\Field\FieldItemListInterface;
@@ -17,7 +16,6 @@
 use Drupal\link\LinkItemInterface;
 use Symfony\Component\Routing\Exception\RouteNotFoundException;
 use Symfony\Component\Validator\ConstraintViolation;
-use Symfony\Component\Validator\ConstraintViolationInterface;
 use Symfony\Component\Validator\ConstraintViolationListInterface;
 
 /**
@@ -81,17 +79,10 @@ protected static function getUriAsDisplayableString($uri) {
     }
     elseif ($scheme === 'entity') {
       list($entity_type, $entity_id) = explode('/', substr($uri, 7), 2);
-      // Show the 'entity:' URI as the entity autocomplete would, but only if:
-      // - the entity could be loaded, and;
-      // - the current user is allowed to view the entity (otherwise we have a
-      //   information disclosure security problem).
+      // Show the 'entity:' URI as the entity autocomplete would.
       $entity_manager = \Drupal::entityManager();
-      if ($entity_manager->getDefinition($entity_type, FALSE)) {
-        $entity = \Drupal::entityManager()->getStorage($entity_type)->load($entity_id);
-        if ($entity) {
-          $label = ($entity->access('view')) ? $entity->label() : t('- Restricted access -');
-          $displayable_string = $label . ' (' . $entity_id . ')';
-        }
+      if ($entity_manager->getDefinition($entity_type, FALSE) && $entity = \Drupal::entityManager()->getStorage($entity_type)->load($entity_id)) {
+        $displayable_string = EntityAutocomplete::getEntityLabels(array($entity));
       }
     }
 
@@ -214,6 +205,10 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
       $element['uri']['#target_type'] = 'node';
       // Disable autocompletion when the first character is '/', '#' or '?'.
       $element['uri']['#attributes']['data-autocomplete-first-character-blacklist'] = '/#?';
+
+      // The link widget is doing its own processing in
+      // static::getUriAsDisplayableString().
+      $element['uri']['#process_default_value'] = FALSE;
     }
 
     // If the field is configured to allow only internal links, add a useful
diff --git a/core/modules/system/src/Tests/Entity/Element/EntityAutocompleteElementFormTest.php b/core/modules/system/src/Tests/Entity/Element/EntityAutocompleteElementFormTest.php
index a2ce824..1fcdb72 100644
--- a/core/modules/system/src/Tests/Entity/Element/EntityAutocompleteElementFormTest.php
+++ b/core/modules/system/src/Tests/Entity/Element/EntityAutocompleteElementFormTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\system\Tests\Entity\Element;
 
+use Drupal\Core\Entity\Element\EntityAutocomplete;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Form\FormInterface;
 use Drupal\Core\Form\FormState;
@@ -141,6 +142,18 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       ),
     );
 
+    $form['single_access'] = array(
+      '#type' => 'entity_autocomplete',
+      '#target_type' => 'entity_test',
+      '#default_value' => $this->referencedEntities[0],
+    );
+    $form['tags_access'] = array(
+      '#type' => 'entity_autocomplete',
+      '#target_type' => 'entity_test',
+      '#tags' => TRUE,
+      '#default_value' => array($this->referencedEntities[0], $this->referencedEntities[1]),
+    );
+
     return $form;
   }
 
@@ -274,6 +287,32 @@ public function testInvalidEntityAutocompleteElement() {
     $this->assertEqual(count($form_state->getErrors()), 0);
   }
 
+  /**
+   * Tests that access is properly checked by the EntityAutocomplete element.
+   */
+  public function testEntityAutocompleteAccess() {
+    $form_builder = $this->container->get('form_builder');
+    $form = $form_builder->getForm($this);
+
+    // Check that the current user has proper access to view entity labels.
+    $expected = $this->referencedEntities[0]->label() . ' (' . $this->referencedEntities[0]->id() . ')';
+    $this->assertEqual($form['single_access']['#value'], $expected);
+
+    $expected .= ', ' . $this->referencedEntities[1]->label() . ' (' . $this->referencedEntities[1]->id() . ')';
+    $this->assertEqual($form['tags_access']['#value'], $expected);
+
+    // Set up a non-admin user that is *not* allowed to view test entities.
+    \Drupal::currentUser()->setAccount($this->createUser(array(), array()));
+
+    // Rebuild the form.
+    $form = $form_builder->getForm($this);
+
+    $expected = t('- Restricted access -') . ' (' . $this->referencedEntities[0]->id() . ')';
+    $this->assertEqual($form['single_access']['#value'], $expected);
+
+    $expected .= ', ' . t('- Restricted access -') . ' (' . $this->referencedEntities[1]->id() . ')';
+    $this->assertEqual($form['tags_access']['#value'], $expected);
+  }
 
   /**
    * Returns an entity label in the format needed by the EntityAutocomplete
@@ -286,7 +325,7 @@ public function testInvalidEntityAutocompleteElement() {
    *   A string that can be used as a value for EntityAutocomplete elements.
    */
   protected function getAutocompleteInput(EntityInterface $entity) {
-    return $entity->label() . ' (' . $entity->id() . ')';
+    return EntityAutocomplete::getEntityLabels(array($entity));
   }
 
 }
