How do I serve static files only to authorized users?

32.2k Views Asked by At

I have a collection of Excel spreadsheets that I'd like to serve in my ASP.NET 5 webapp only to authorized users.

  1. Where should I store the files? I assume in wwwroot (e.g., wwwroot/files).
  2. If in wwwroot, how do I allow access only to authorized users? (I'd like to serve them up as a [Authorize] FileResult from the controller, but this still leaves the files open to direct access through a URL I believe.)
  3. How do I reference a location in wwwroot through my FileResult action in the controller?

Thanks much!

6

There are 6 best solutions below

9
On BEST ANSWER

Yes, they should go in wwwroot. Currently there is no built-in way to secure wwwroot directories. But creating a middleware module to accomplish it is pretty straightforward. There is an easy to follow tutorial here.

If you're not familiar with developing middleware, I posted a GitHub project that shows how to create middleware in three easy steps. You can download the project here.

You don't need a controller to access static files.

3
On

in .net core create a dedicated directory www in same level as wwwroot, and use the following code:

public HomeController(IHostingEnvironment hostingEnvironment)
{
    _hostingEnvironment = hostingEnvironment;
}

[Authorize(Roles = "SomeRole")]
public IActionResult Performance()
{
    return PhysicalFile(Path.Combine(_hostingEnvironment.ContentRootPath,
                                     "www", "MyStaticFile.pdf"), "application/pdf");
}

Based on the following answer (for .netCore): static file authorization

1
On

For authentication check while retrieving file:

        app.UseStaticFiles(new StaticFileOptions()
        {
            OnPrepareResponse = (context) =>
            {
                if (!context.Context.User.Identity.IsAuthenticated && context.Context.Request.Path.StartsWithSegments("/excelfiles"))
                {
                    throw new Exception("Not authenticated");
                }
            }
        });
4
On

If you have a login form (Login.html), a simple solution is to redirect the user to the login page if user is not authenticated and he's requesting a protected resource (file under /protected folder). In Startup.cs, in Configure method insert this code:

app.Use(async (context, next) =>
{
    if (!context.User.Identity.IsAuthenticated && context.Request.Path.StartsWithSegments("/protected"))
    {
        context.Response.Redirect("/Login.html");
        return;
    }
    await next.Invoke();
});
0
On

To secure your files and available them to the authenticated users, easily create a folder named 'staticfiles' outside of the 'wwwroot' folder. then suppose you want to restric user access to some books, so create a 'Books' folder under the 'staticfiles' and then update your "Program.cs" or your middleware pipeline like this:

app.UseAuthentication();

app.UseStaticFiles(new StaticFileOptions
{
    
    OnPrepareResponse = (ctx) =>
    {
        var context = ctx.Context;
        context.Response.Headers.Add("Cache-Control", "no-store");
        if (context.Request.Path.Value.StartsWith("/staticfiles/Books"))
        {
            if (!context.User.Identity.IsAuthenticated)
            {

                context.Response.Redirect($"{context.Request.Scheme}://{context.Request.Host}{context.Request.PathBase}" + "/Identity/Account/Login");

            }
        }
    },
    FileProvider = new PhysicalFileProvider(
           Path.Combine(builder.Environment.ContentRootPath, "staticfiles")),
    RequestPath = "/StaticFiles",

});

app.UseAuthorization();
4
On

This is a very simple example, but it can be changed to check for specific roles, and the code can be moved out of the Startup.cs for more flexibility.

app.Use(async (context, next) =>
               {
                   if (!context.User.Identity.IsAuthenticated
                       && context.Request.Path.StartsWithSegments("/excelfiles"))
                   {
                       throw new Exception("Not authenticated");
                   }
                   await next.Invoke();
               });