Sometimes a user needs to be able to access protected and secure files for download to their local browser. The challenge is that a browser must be able to access an anonymously accessible endpoint in order to download a file from an API via a URL. In order to do this, the API supports the use of a record stored in the DownloadToken
table of our database. The user would obtain the token via an API endpoint that is secured by their user credentials and role-level access.
Here is the DownloadToken.cs
file that maps back to the DownloadToken database table. The properties are documented in the code:
using System.ComponentModel.DataAnnotations.Schema;
namespace LymeTemplateApi.Models;
/// <summary>
/// The DownloadToken entity is intended to help facilitate
/// secure file downloads. In order to easily provide a user with a
/// secure DownloadToken, you can use the static method
/// FileDownloads.CreateDownloadToken to allow your API Controllers
/// to create a token by a secured user and return that token for
/// use with the DownloadFile API controller.
/// </summary>
[Table("DownloadToken")]
public class DownloadToken
{
/// <summary>
/// Primary key
/// </summary>
public int Id { get; set; }
/// <summary>
/// The physical file path of the file to be downloaded.
/// </summary>
public string? FilePath { get; set; }
/// <summary>
/// The optional overriding filename that should be delivered to the user.
/// </summary>
public string? FileName { get; set; }
/// <summary>
/// The GUID Token created for the file download.
/// </summary>
public Guid Token { get; set; }
/// <summary>
/// The date the token was created.
/// </summary>
public DateTime DateEntered { get; set; }
/// <summary>
/// The date that the token expires.
/// </summary>
public DateTime ExpirationDate { get; set; }
/// <summary>
/// The date that the token was redeemed.
/// </summary>
public DateTime? Redeemed { get; set; }
}
In order for a user to redeem a token to download a file, the user can visit an API endpoint that resembles the following:
https://www.lymetemplate.com/api/DownloadFile?token=INSERT_TOKEN_GUID_VALUE_HERE
Here's a sample API endpoint that uses this pattern to secure a file:
/// <summary>
/// This endpoint is secured by default using Microsoft Entra ID.
/// </summary>
/// <returns>A GUID token value as a string.</returns>
[Route("SampleFileDownload")]
[HttpGet]
public ActionResult<string> SampleFileDownload()
{
var token = FileDownloads.CreateDownloadToken(db, @"C:\Temp\SuperSecretGuide.pdf");
return Ok(token.Token);
}
In the front end app, you can use the DownloadSecureFileService
Angular service to help facilitate the usage of this pattern.
import { DownloadSecureFileService } from './download-secure-file.service';
...
constructor(private downloadSecureFile: DownloadSecureFileService) {}
...
downloadSecuredFile() {
this.downloadSecureFile('MySecuredEndpoint/SampleFileDownload').subscribe();
}