JWT Authentication and file downloads
For a while I have been using JWT (JSON Web Tokens) to handle authentication information about users of webapps. JWT is quite nice (I won’t go into the details, but there are many nice tutorials and guides around), though it makes handling of file downloads a bit more problematic.
Why is it so ? html links and headers don’t play well together, so a simple link like
<a href="/path/to/my/file">My file</a>
will fail to carry over any authentication header, so if you want to download a file that is also secured by the token, you have to try other means.
It’s not hard coming up with a number of alternatives.
We can for example get the data through XHR and save the file client side. Unfortunately receiving binary data is not exactly straightforward. Also if you want to avoid loading the whole file into memory, you have to use tricks and APIs that are browser specific (here is a nice example that will work only in Chrome).
Another choice is to save the token as a Cookie. On server side, the download url should not be protected by the regular JWT authentication; in its place the backend will need to manually extract the information from the token and validate its content. Moreover, one has to keep in mind that Cookie-based authentication and Header-based authentication have a few subtle differences. For example the former doesn’t allow authetication of different user on the same website in different tabs , while the latter does. Using this technique and then trying to download two different file in different tabs may cause an unexpected error. Also, if you don’t remember to delete the cookie after the download, you will send the authentication two times on every call, wasting bandwidth.
We can also decide to send the token as part of the url. This is similar to the above, but without the drawbacks of mixing Header-based and Cookie-based authentication. Generated url can be passed around, since the url contains all the necessary information (this may be a feature or a bug, depending on the case :) ). The url will contain the whole authentication token (which is unencrypted even on HTTPS), so it may share with the world more information than one would like.
Finally, we can generate temporary unprotected links. This requires two calls for each download. First a call to a protected url, requesting a download url, then a call the the newly generated url to really download the file. Not too far from the previous options, with two notable distinctions: one can customize the expiry date, since it’s not tied to the authentication token, and requires to open a single url, not two.
None of those alternative is completely better than the others. Each of them has some strength and weaknesses, but I like the idea of using temporary links. It’s not too hard to implement (expecially if it has to run on a single server) and works fine. The XHR solution is much more flexible, and if you like to use bleeding edge browser functionality it can be fun to code. It is a lot of work though, so unless downloading file is one of the primary tasks of your website, I think it’s not worth the hassle.