Private files download as different name after anonymous user gets redirected

Created on 7 July 2022, over 1 year ago
Updated 12 February 2024, 12 days ago

Problem/Motivation

If you try to access a file link as an anonymous user, the file gets downloaded with the name "files" after you are redirected and then logged in. Meaning the original file name is changed if the URL was initially accessed while not logged in.

If the user logs in and then goes to the file URL, the file name stays as it should be. But if they go to the file URL first and then have to log in, the expected redirect after that results in the file name change.

Specifically what seems to happen now is that: The user gets redirected to login, and the URL is changed to include "user/login?destination=/" as expected, but the "system/files" part is also changed to include "?file=" before the relative file path. This is then also URL encoded to "%3Ffile%3D". I don't think the encoding there is an issue, but I suspect this additional "file" parameter is likely the cause of this file name problem.

Example:
Initial link: http://site.local/system/files/test.docx
Redirected link: http://site.local/user/login?destination=/system/files%3Ffile%3Dtest.docx
Decoded link: http://site.local/user/login?destination=/system/files?file=test.docx

This issue was initially marked as a duplicate before as it was planned to be resolved through another issue related to redirecting to files with spaces in their name. That eventually lead to that issue also closing as a duplicate and moved to a different issue involving the leading destination slash being stripped. This was ultimately causing the URL space encoding issues, however it did not address the initial file name problem. This issue happens regardless of spaces being present in the URL, and seems to have been forgotten as the issues progressed.

By the way, testing this with files that have spaces in their names still appears to double encode it in the redirect destination URL. However, logging in doesn't cause issues with serving me the file like it used to before the leading slash fix was implemented.

I tested this through a brand new install of Drupal 9.4.1 and only the additional modules R4032Login and PFDP (for access to private files).

Steps to reproduce

  1. Install and enable R4032Login module
  2. Go to anonymous settings page: admin/config/system/r4032login/settings/anonymous
  3. Make sure the option "Redirect anonymous users to the page they tried to access after login" is checked
  4. Create file "test.docx" and put it in your private file folder
  5. Log out of Drupal to become Anonymous user
  6. Try to visit file: "[site_url]/system/files/test.docx"
  7. After being redirected to login page, verify the redirect destination is set as the file URL
  8. Log back into Drupal here
  9. File downloads automatically, but is named "files.docx" instead of "test.docx"

Files that can be opened in your browser instead of auto-downloading (like .txt) will appear, but saving them will try to default it's name to "files" (so "files.txt" for example). However, with this we can test what causes the file name to be changed with the redirected URL. Playing around with this, I figured out a couple things. The "file" parameter also seems to remove the slash after "system/files". Adding it back in before the parameter (while leaving the parameter) changes the saved file name to something like "[site_name] download.txt". What seems to have worked was removing "?file=" (or its encoded version, "%3Ffile%3D") and adding back the slash after "system/files".

Proposed resolution

It seems the redirected destination here shouldn't replace the slash after "system/files" with "?file=" when redirecting an Anonymous user to the login page. This should be taken into account when the destination is created, but that appears to be mostly done through Drupal core code. Doing string replace for "?file=" in the destination after it's received resolves this.

R4032LoginSubscriber.php, around line 160:
$destination = str_replace("?file=", "/", $destination);

I'm not sure if there's a more proper way this can be handled, nor if a good fix can be done here through this module, since it seems to be caused by Drupal core code and private files. But I figured I'd post a more complete explanation of it here so the problem isn't forgotten and I can get your thoughts.

See the following issues for the backstory:
#3039886: Problem with downloadable files names β†’
#3222717: Redirecting to a file with spaces in the name results in 404 β†’
#3200138: Leading slash for destination param should not be stripped, causes URL encoding issues β†’

πŸ› Bug report
Status

Needs review

Version

2.2

Component

Code

Created by

πŸ‡ΊπŸ‡ΈUnited States wolfborg

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates : some issue and comment data are missing.

  • πŸ‡¨πŸ‡¦Canada chrisck

    It seems that a patch for this is not necessary. There is a hook_file_download that we can use:

    This hook allows modules to enforce permissions on file downloads whenever Drupal is handling file download, as opposed to the web server bypassing Drupal and returning the file from a public directory. Modules can also provide headers to specify information like the file's name or MIME type.

    /**
     * Implements hook_file_download().
     */
    function hook_file_download($uri) {
      $filename = \Drupal::service('file_system')->basename($uri);
      $disposition = 'attachment; filename="' . $filename . '"';
      return [
        'Content-disposition' => $disposition,
      ];
    }

    Replace the hook in your function with your custom module name.

Production build https://api.contrib.social 0.61.6-2-g546bc20