We have the requirement in our enterprise environment to serve static file content from a network share in our ASP.NET Core application. Basically, it gets served under some sub path /content. For this, we have the following code, which works fine:
app.UseFileServer(new FileServerOptions
{
FileProvider = new PhysicalFileProvider("//our/network/share"),
RequestPath = new PathString("/content"),
EnableDirectoryBrowsing = false
});
Now in production the system user under whose context the web application is hosted has no access to the file share. Thus, we have to use a certain technical domain user to access the files and for this we have to provide credentials (username/password) of this system user to the file server.
Unfortunately, we did not find an option to provide credentials to UseFileServer(). Is it anyway possible?
According to the documentation for UseFileServer it combines the functionality of among other things UseStaticFiles. According to the middleware documentation, the static file module provides no auth checks. They do give you some options on how to accomplish file serving with authorization (again from the middleware docs):
If you want to serve files based on authorization:
Store them outside of wwwroot and any directory accessible to the static file middleware.
Deliver them through a controller action, returning a FileResult where authorization is applied.
Not sure how you are going to pass the username/password to the server. If you plan to use something like basic authentication (and don't want to use the methods outlined above), you can probably modify the headers (when serving the static files) to accomplish the desired effect, but that is a workaround and probably not a good idea.
I would use middleware to protect contents. I will try to write simple example(I assumed you are using any authentication middleware to authenticate your users and my example is for static files).
-- Below code is untested and is just for an illustration--
First, you need to create a middleware something like this:
public class ProtectFileMiddleware
{
private readonly RequestDelegate _next;
public ProtectFileMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
if (context.Request.Path.StartsWithSegments("/protected"))
{
if (!context.User.IsInRole("Admin"))
{
await context.Authentication.ChallengeAsync();
return;
}
}
await _next(context);
}
}
and use this middleware like below:
public void Configure(IApplicationBuilder app)
{
app.Use(?)Authentication();// it depends on your design
app.UseMiddleware<ProtectFileMiddleware>();
app.UseStaticFiles();
// other
}
Result: if you try to access /protected url as an admin user, you will get expected response otherwise you will take a 401/403 response.
For more flexible way take a look at http://odetocode.com/blogs/scott/archive/2015/10/06/authorization-policies-and-middleware-in-asp-net-5.aspx
Yeah, those answers assume you're asking about client credentials. What you really need is a IFileProvider implementation that has credentials to access a specific resource. I don't know that .NET is very good at accessing files as different users, it usually relies on impersonation. See How to present credentials in order to open file?
Related
As per Microsoft's recommendation, I am using a custom AuthenticationStateProvider service for handling authentication/authorization for a Blazor server page.
It all works fine within razor components, where I can use the [Authorize] attribute or the AuthorizeView/Authorized/NotAuthorized tags.
Now, I wanted to serve static files outside the wwwroot folder but have control if the user is authenticated or not in order to serve the files.
Is there a way to control access to static files served outside the wwwroot folder?
What I found is something similar to (in program or startup):
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = (context) =>
{
if (context.Context.Request.Path.StartsWithSegments("/MyRequestPath"))
{
context.Context.Response.Headers.Add("Cache-Control", "no-store");
if (!context.Context.User.Identity.IsAuthenticated)
{
context.Context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
context.Context.Response.ContentLength = 0;
context.Context.Response.Body = Stream.Null;
}
}
},
FileProvider = new PhysicalFileProvider("PathToMyFilesOutsidewwwroot"),
RequestPath = "/RequestPath"
});
The problem with that is that is uses Context.User.Identity.IsAuthenticated, i.e., it uses HTTPContext, which is not available within a Blazor page (and that is why we have to use AuthenticationStateProvider).
I'd like to stick to just using Blazor best practices, and not try to circumvent it via scaffoldding, javascript, or whatever.
Thanks in advance.
In the end I sticked to using the app.UseStaticFiles... approach, but in order to make it work I had to add authentication via cookies (outside of Blazor). Inside of Blazor I still use AuthenticationStateProvider, so the only thing that I had to take care is to authenticate via cookies and AuthenticationStateProvider at the same time when a user logs in. I suppose it makes sense, because authentication via cookies (prior to entering the Blazor "environment") gives me also the chance to call controllers or other pages out the razor components while still being authenticated (via cookies).
I have a Blazor Server Project based on ASP.NET Core 5. I want to host my own openid-configuration discovery file. Since this file is served while running the OIDC workflow I want to verify what is the correct way to host this file. So far I have tried the following and only option 2 works.
using wwwroot/.well-known
This involves hosting the openid-configuration file statically in the wwwroot folder of my blazor server project.
After this if I run he project and try to access the file using localhost:44382/.well-known/openid-configuration, the file is not served.
Using Controllers
For this I just added a simple controller to my blazor project and specified .well-known/openid-configuration as a route for my anonymous controller HTTPGET action.
public class OidcConfigurationController : Controller
{
[HttpGet(".well-known/openid-configuration")]
public JsonResult OpenIdConfiguration()
{
return Json(new Storage.Storables.Security.OIDC.Configuration());
}
}
Now if I run the project with Option 2 and try to reach the localhost:44382/.well-known/openid-configuration the configuration JSON is served correctly.
Is option 2 the correct way to serve the OpenId-Configuration using ASP.NET Core and Blazor server project ? Will it cause any issues if I publish the server (for e.g. to Azure)
The reason why your first method is not working is that you don't serve a static file in a way the static file extensions assume you do. You missing a file ending, otherwise, the request isn't recognized as a file.
That said, you can write your own middleware. Give the file a proper ending like .json. If the resources /.well-known/openid-configuration/ is requested, you change the requested path to /.well-known/openid-configuration.json and let the static file extension handle the rest.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.Use(async (context, next) =>
{
if (context.Request.Path == "/.well-known/openid-configuration")
{
context.Request.Path = "/.well-known/openid-configuration.json";
}
await next();
});
app.UseStaticFiles();
...
}
For more information about writing a middleware have a look at the documentation https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write
However, you ran into the problem - I guess - because mostly this document is generated on the fly based on the configuration of your open id connect server like IdentityServer. So, maybe there is away around the static file?
Is there any way I can give access to static content based on their role. In my application, the authentication is done through Azure Active AD and the contents should be accessed based on their role e.g. employee should access all employee pages and students should access student pages. This is how my solution explorer looks like.
Solution Explorer
I know this is duplicate of Secure requests to .html files in ASP.NET Core but I couldn't find any approach to implement the solution. I have made new folder intranet outside the wwwRoot to serve my static content but still need to know how can I authorize the user and and serve role based static files.
As the document said, you could store the static files outside of wwwroot and any directory accessible to the Static File Middleware (for example: MyStaticFiles folder, like this), then, you could serve them via an action method to which authorization is applied and return a FileResult object:
[Authorize(Roles = "User")]
public IActionResult BannerImage()
{
var filePath = Path.Combine(
_env.ContentRootPath, "MyStaticFiles", "images", "Image1.jpg");
return PhysicalFile(filePath, "image/jpeg");
}
Then, you could view image by click the following link:
<a asp-action="BannerImage" asp-controller="Home">View Image</a>
[Note] After using the above method, if the authorize not working, try to clear the cache, perhaps the issue is related to the browser cache. Besides, if you meet the "HTTP Error 404.15 - Not Found" error, try to add [AllowAnonymous] attribute for other controller action method.
If hosting a web application with IIS I know there's an app_offline.htm file that can be used, but I'm hosting a .NET core web app in a Linux environment with apache. Anyone know what the safest approach is to taking an app offline in this situation so that I can make changes to my app without breaking anything?
EDIT: The intent is to keep the website online, but prevent login or any interactions within a web application until maintenance tasks are complete and app restarted.
After a little research, I made something that should serve the purposes of an 'app_offline' by using an ActionFilter.
Using dependency injection, this filter takes a boolean value set in my appsettings.json and if set to true it's read into the action filter and redirects users to a 'down for maintenance' page. It is my understanding that using IOptionsSnapshot instead of IOptions will get the value if changed during runtime. This is also nice when you have a public facing website but only want the backend application to prevent any actions until changes have been made and the app restarted. Below is an example of my ActionFilter. Only thing I don't like is that this filter has to be set on pretty much every controller in the app. I'd prefer coming up with something that could be checked sooner in the pipeline, so if anyone has a better approach I'd love to see it.
private readonly ServiceSettings _settings;
public AppStateAttribute(IOptionsSnapshot<ServiceSettings> options)
{
_settings = options.Value;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if(_settings.AppOffline)
{
var redirectOnFail = "/maintenance";
context.HttpContext.Response.Redirect(redirectOnFail, true);
}
base.OnActionExecuting(context);
}
I understand there is more than one way of handling service authentication/authorization, but I cannot make it work for static files.
Is there a way of configuring the behavior to be the same as with services; if not authenticated a request to index.html should redirect to login page the same as a request to secured dto/service.
I am currently looking into RawHttpHandlers but since it is too early in the pipeline how do I get the authentication setup in the apphost config?
thanks in advance
Gjergji
You would have to use IAppHost.RawHttpHandlers because that's the only custom handler in ServiceStack's Request Pipeline that gets executed before the built-in static file handling is accessed.
But you should still be able to access the Users Session with the available extension methods, e.g:
this.RawHttpHandlers.Add(httpReq =>
{
var isStaticFileRequest = httpReq.PathInfo.StartsWith("/static");
if (isStaticFileRequest)
{
var session = httpReq.GetSession();
if (!session.HasRole("TheRole"))
return new ForbiddenHttpHandler();
}
return null;
});
This handler simply checks if it's a request for a static file, in this case the path info starts with /static, and if is checks the user session if they have the required role, if not it returns a Forbidden request, otherwise it returns null to tell ServiceStack to continue executing the request.
Note: if it's needed you can access any registered dependency from outside of ServiceStack with HostContext.Resolve, e.g:
var authRepo = HostContext.Resolve<IAuthRepository>();