diff --git a/file_entity.field.inc b/file_entity.field.inc
index 43016ff..dd47830 100644
--- a/file_entity.field.inc
+++ b/file_entity.field.inc
@@ -15,6 +15,14 @@ function file_entity_field_formatter_info() {
     'field types' => array('file', 'image'),
     'settings' => array('file_view_mode' => 'default'),
   );
+  $info['file_download_link'] = array(
+    'label' => t('Download link'),
+    'description' => t('Displays a link that will force the browser to download the file.'),
+    'field types' => array('file', 'image'),
+    'settings' => array(
+      'text' => t('Download'),
+    ),
+  );
   return $info;
 }
 
@@ -35,6 +43,15 @@ function file_entity_field_formatter_settings_form($field, $instance, $view_mode
       // Never empty, so no #empty_option
     );
   }
+  elseif ($display['type'] = 'file_download_link') {
+    $element['text'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Link text'),
+      '#description' => t('This field support tokens.'),
+      '#default_value' => $settings['text'],
+      '#required' => TRUE,
+    );
+  }
 
   return $element;
 }
@@ -51,6 +68,9 @@ function file_entity_field_formatter_settings_summary($field, $instance, $view_m
     $view_mode_label = file_entity_view_mode_label($settings['file_view_mode'], t('Unknown'));
     $summary = t('View mode: %mode', array('%mode' => $view_mode_label));
   }
+  elseif ($display['type'] == 'file_download_link') {
+    $summary = t('Link text: %text', array('%text' => $settings['text']));
+  }
 
   return $summary;
 }
@@ -112,6 +132,23 @@ function file_entity_field_formatter_view($entity_type, $entity, $field, $instan
       }
     }
   }
+  elseif ($display['type'] == 'file_download_link') {
+    foreach ($items as $delta => $item) {
+      $file = (object) $item;
+      if (file_entity_access('download', $file)) {
+        $text = token_replace(t($settings['text']), array('file' => $file), array('clear' => TRUE));
+        $token = drupal_get_token("file/{$file->fid}/download");
+        $element[$delta] = array(
+          '#type' => 'link',
+          '#title' => $text,
+          '#href' => "file/{$file->fid}/download",
+          '#options' => array(
+            'query' => array('token' => $token),
+          ),
+        );
+      }
+    }
+  }
 
   return $element;
 }
diff --git a/file_entity.pages.inc b/file_entity.pages.inc
index f6776a9..c14febd 100644
--- a/file_entity.pages.inc
+++ b/file_entity.pages.inc
@@ -35,6 +35,11 @@ function file_entity_view_page($file) {
  * Menu callback; download a single file entity.
  */
 function file_entity_download_page($file) {
+  // Ensure there is a valid token to download this file.
+  if (!isset($_GET['token']) || !drupal_valid_token($_GET['token'], "file/$file->fid/download")) {
+    return MENU_ACCESS_DENIED;
+  }
+
   // If the file does not exist it can cause problems with file_transfer().
   if (!is_file($file->uri)) {
     return MENU_NOT_FOUND;
diff --git a/tests/file_entity.test b/tests/file_entity.test
index dbbcde6..2435075 100644
--- a/tests/file_entity.test
+++ b/tests/file_entity.test
@@ -171,6 +171,16 @@ class FileEntityTestHelper extends DrupalWebTestCase {
 
     return $file;
   }
+
+  /**
+   * Overrides DrupalWebTestCase::drupalGetToken() to support the hash salt.
+   *
+   * @todo Remove when http://drupal.org/node/1555862 is fixed in core.
+   */
+  protected function drupalGetToken($value = '') {
+    $private_key = drupal_get_private_key();
+    return drupal_hmac_base64($value, $this->session_id . $private_key . drupal_get_hash_salt());
+  }
 }
 
 class FileEntityUnitTestCase extends FileEntityTestHelper {
@@ -1104,14 +1114,19 @@ class FileEntityAccessTestCase extends FileEntityTestHelper {
     $this->drupalGet("file/{$file->fid}/view");
     $this->assertResponse(200, 'Users with access can access the file view page');
 
+    $url = "file/{$file->fid}/download";
     $web_user = $this->drupalCreateUser(array());
     $this->drupalLogin($web_user);
-    $this->drupalGet("file/{$file->fid}/download");
+    $this->drupalGet($url, array('query' => array('token' => $this->drupalGetToken($url))));
     $this->assertResponse(403, 'Users without access can not download the file');
     $web_user = $this->drupalCreateUser(array('download any document files'));
     $this->drupalLogin($web_user);
-    $this->drupalGet("file/{$file->fid}/download");
+    $this->drupalGet($url, array('query' => array('token' => $this->drupalGetToken($url))));
     $this->assertResponse(200, 'Users with access can download the file');
+    $this->drupalGet($url, array('query' => array('token' => 'invalid-token')));
+    $this->assertResponse(403, 'Cannot download file with in invalid token.');
+    $this->drupalGet($url);
+    $this->assertResponse(403, 'Cannot download file without a token.');
 
     $web_user = $this->drupalCreateUser(array());
     $this->drupalLogin($web_user);
diff --git a/views/views_handler_field_file_link_download.inc b/views/views_handler_field_file_link_download.inc
index cd2fe46..ff17182 100644
--- a/views/views_handler_field_file_link_download.inc
+++ b/views/views_handler_field_file_link_download.inc
@@ -23,7 +23,7 @@ class views_handler_field_file_link_download extends views_handler_field_file_li
 
     $this->options['alter']['make_link'] = TRUE;
     $this->options['alter']['path'] = "file/$file->fid/download";
-    $this->options['alter']['query'] = drupal_get_destination();
+    $this->options['alter']['query']['token'] = drupal_get_token($this->options['alter']['path']);
 
     $text = !empty($this->options['text']) ? $this->options['text'] : t('Download');
     return $text;
