Reputation: 1186
I have a collection of Excel spreadsheets that I'd like to serve in my ASP.NET 5 webapp only to authorized users.
Thanks much!
Upvotes: 44
Views: 34705
Reputation: 1096
The sample code 8.x/StaticFileAuth_ and 9.x/StaticFileAuth in GitHub dotnet/AspNetCore.Docs
repo provides a different solution from the sample code for 6.x.
The solution is an endpoint returning TypedResults.PhysicalFile
. It requires neither UseStaticFiles
nor UseFileServer
.
The code below is essential parts of the sample code with some modifications. For a complete sample, please follow the link above.
I modified TypedResults.PhysicalFile
to Results.File
.
I modified /private/{fileName}
to /private/{**fileName}
, which allows the route mapping to match paths like /private/docs/index.html
.
Don't forget to also change the string fileName
argument to nullable, string? fileName
. It makes the mapping work for /private
as well.
I also added FileExtensionContentTypeProvider
to open the content on browsers.
const string ROOT_DIR_OF_PRIVATE_CONTENTS = "wwwprivate";
builder.Services.AddAuthentication();
builder.Services.AddAuthorization(option =>
{
option.AddPolicy("AuthenticatedUser", b => b.RequireAuthenticatedUser());
});
var app = builder.Build();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/private/{**fileName}", IResult (string? fileName) =>
{
var filePath = Path.Combine(builder.Environment.ContentRootPath, ROOT_DIR_OF_PRIVATE_CONTENTS, fileName ?? "");
if (File.Exists(filePath))
{
var provider = new FileExtensionContentTypeProvider();
if (!provider.TryGetContentType(filePath, out var contentType))
{
contentType = "application/octet-stream";
}
return Results.File(filePath, contentType);
}
return Results.NotFound("No file found with the supplied file name");
})
.WithName("GetFileByName")
.RequireAuthorization("AuthenticatedUser");
Upvotes: 0
Reputation: 5687
This has changed to a simpler setup for .Net 8. Microsoft has a full writeup here https://learn.microsoft.com/en-us/aspnet/core/fundamentals/static-files?view=aspnetcore-8.0#static-file-authorization.
Essentially, it involves setting a FallbackPolicy
as follows;
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
var app = builder.Build();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles")),
RequestPath = "/StaticFiles"
});
Note from the page on secure outside of wwwroot;
In the preceding code, the fallback authorization policy requires all users to be authenticated. Endpoints such as controllers, Razor Pages, etc that specify their own authorization requirements don't use the fallback authorization policy. For example, Razor Pages, controllers, or action methods with [AllowAnonymous] or [Authorize(PolicyName="MyPolicy")] use the applied authorization attribute rather than the fallback authorization policy.
RequireAuthenticatedUser adds DenyAnonymousAuthorizationRequirement to the current instance, which enforces that the current user is authenticated.
Static assets under wwwroot are publicly accessible because the default Static File Middleware (app.UseStaticFiles();) is called before UseAuthentication. Static assets in the MyStaticFiles folder require authentication. The sample code demonstrates this.
They link to a full demo with source on GitHub at https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/fundamentals/static-files/samples/6.x
Upvotes: 0
Reputation: 2868
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();
Upvotes: 0
Reputation: 151
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");
}
}
});
Upvotes: 15
Reputation: 3846
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();
});
Upvotes: 4
Reputation: 4700
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.
Upvotes: 36
Reputation: 514
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();
});
Upvotes: 4
Reputation: 1502
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
Upvotes: 10