.NET Core serving HTML file outside wwwroot can't load JS files - asp.net-core

I am experimenting with different ways to secure an Angular CLI app with .NET Core Authorization.
To make it as secure as possible, I would like to keep all of the Angular CLI output files from being publicly available and keep them in the default "dist" folder preconfigured by the CLI.
I can load the index.html from an authorized controller by returning a PhysicalFileResult...
public IActionResult Index()
{
return PhysicalFile(Path.Combine(Directory.GetCurrentDirectory(), "dist", "index.html"),"text/HTML");
}
But I get 404s on all of the bundle.js files when the page loads.
Is it possible to serve the app this way without involving the static file middleware or making the files publicly available (preferably without having to manually change the src for each bundled js file in index.html)?

Take a look at this article from the asp.net core docs (excerpt included below): https://learn.microsoft.com/en-us/aspnet/core/fundamentals/static-files#static-file-authorization

Just place your authorization middleware before the static one.
// has to be first so user gets authenticated before the static middleware is called
app.UseIdentity();
app.Use(async (context, next) =>
{
// for pathes which begin with "app" check if user is logged in
if(context.Request.Path.StartsWith("app") && httpContext.User==null)
{
// return "Unauthorized"
context.Response.StatusCode = 401;
return;
}
// If user is logged in, call next middleware
await next.Invoke();
});
app.UseStaticFiles();

Related

Check if request is made to Razor Page

How can I check within middleware code if current request is made to Razor Page not to any other resource (static file, or API)?
All my APIs are located within api folder, so if (!context.Request.Path.StartsWithSegments("/api")) {} filters out APIs, but that will not work for static content as these files and libraries are placed within number of folders what results in number of URL segments.
Could not find any relevant property in context.
First step - place the middleware after app.UseRouting(). Any request for a static file will never reach your middleware because the static files middleware will short circuit the request. Also, after this point, the routing middleware will have selected the endpoint and populated the endpoint metadata. Then you can test the endpoint metadata collection to see if it includes PageRouteMetaData, which tells you that this is a Razor page route:
app.Use((context, next) => {
var endpoint = context.GetEndpoint();
if (endpoint != null)
{
foreach(var md in endpoint.Metadata)
{
if( md is PageRouteMetadata)
{
// this is a page route
}
}
}
return next(context);
});

How to create resource file in ASP.NET Core

I use ASP.NET Core. I want to "embbed" text file into application and use it during seeding DataContext only. I don't want anyone to access this file.
As far as I know, if you don't put this text file into the wwwroot folder and don't use the Directory browsing for the whole application, that means no one could access the root path' file.
If you still don't want to let anyone access it trough the http or https. You could write a custom middleware to check the request path, if this path contains the .txt, you could return the access denied response.
More details, you could refer to below codes:
app.Use(async (context, next) => {
if (context.Request.Path.Value.Contains(".txt"))
{
await context.Response.WriteAsync(
$"Acess Denied");
}
else
{
await next();
}
} );
Result:

URL Rewrite exceptions for Blazor WebAssembly Hosted deployment

During development, i have used Swagger on the server side of my Blazor WebAssembly App. Always launching (debug) using kestrel instead of IIS Express.
Routing worked as expected, all my component routed properly and if i manually typed /swagger, i got to the swagger page. All good.
We have deployed under IIS on our pre-prod servers, the Server side and Blazor WebAssembly App (client) work as expected and are usable, however, my /swagger url gets rewritten (I assume) to go somewhere in my App instead of letting it go to Swagger, obviously there isn't any component that answers to /swagger.
My only guess is that, when hosted on IIS, the aspnet core app takes care of telling IIS what to rewrite and how (similar to the configs that could be provided thru a web.config for a "Standalone" deployment.)
I can't find how to specify exceptions, I've been following the doc at
https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/blazor/webassembly?view=aspnetcore-3.1#iis
Any idea how i could add an exception for /swagger ?
EDIT:
Turns out it works without issues in Chrome, only Firefox has the unwanted behavior. If i clear my cache, or use Incognito mode, the issue does not happen in Firefox. So, it seems that Firefox caches some stuff and tries to send my URL input to the Blazor Wasm instead of going thru to the server. I will debug some more with the dev tools and fiddler open to try and figure it out, will report back.
Turns out there this is part of the service-worker.js file that is published. It is different in dev than what gets published (which makes sense).
During my debugging i was able to reproduce the issue on all browsers (Edge, Chrome and Firefox), regardless of being in Incognito/Private mode or not.
Once the service-worker is running, it handles serving requests from cache/index.html of the Blazor WebAssembly app.
If you go into your Blazor WebAssembly Client "wwwroot" folder, you'll find a service-worker.js and a service-worker.published.js. In the service-worker.published.js, you will find a function that looks like this :
async function onFetch(event) {
let cachedResponse = null;
if (event.request.method === 'GET') {
// For all navigation requests, try to serve index.html from cache
// If you need some URLs to be server-rendered, edit the following check to exclude those URLs
const shouldServeIndexHtml = event.request.mode === 'navigate'
&& !event.request.url.includes('/connect/')
&& !event.request.url.includes('/Identity/');
const request = shouldServeIndexHtml ? 'index.html' : event.request;
const cache = await caches.open(cacheName);
cachedResponse = await cache.match(request);
}
return cachedResponse || fetch(event.request);
}
Simply following the instructions found in the code comments is gonna fix the issue. So we ended up adding an exclusion for "/swagger" like so :
&& !event.request.url.includes('/swagger')
Hopefully this post is useful for people who are gonna want to serve things outside of the service worker, not only Swagger.
Do you have UseSwagger first in your Startup.Configure method?
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseSwagger();
app.UseSwaggerUI(c =>
c.SwaggerEndpoint("/swagger/v1/swagger.json", "YourAppName V1")
);
In Startup.ConfigureServices I have the Swagger code last.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSwaggerGen(c =>
c.SwaggerDoc(
name: "v1",
info: new OpenApiInfo
{
Title = "YourAppName",
Version = "V1",
}));
}
This is working just fine for us.
Note: You must navigate to https://yourdomain/swagger/index.html

How to grant/limit access on Images sub folders for each Customer?

I have the following structure inside my wwwroot/images:
wwwroot
-------Images
-------------Customers
-----------------------Each costumers has his own folder name.
Each folder has an image which only the customer might have access.
The problem is: If some user types the address of other user's image, it will open.
I'm trying to restrict the access of each folder using the table Company from the data base.
I could do it on MVC 5 using location path on web config.
But how could I do it in appsettings.json on .NET Core?
Thanks guys!
OBS: If you have any other approach for this, will be welcome :D
The Static File Middleware doesn't provide authorization checks. Any files served by it, including those under wwwroot, are publicly accessible. To serve files based on authorization, you could refer Static file authorization.
For another option, you may consider implement a custom middleware check the identity like
app.Map("/specificpath", subApp => {
subApp.Use(async (context, next) =>
{
if (!context.User.Identity.IsAuthenticated)
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
}
else if(context.Request.Path.StartsWithSegments("/specificpath/User1") && context.User.Identity.Name != "User1")
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
}
});
});
app.UseStaticFiles();

How to cache static content using ASP.NET 5 and MVC 6?

This was previously achieved by adding some configuration to the web.config file, but now this file is to be extinguished.
I was expecting to find some methods or properties in the middleware declaration, but I haven't found:
app.UseStaticFiles();
So, which is now the procedure to cache static content as images, scripts, etc.?
Is there another middleware to do this or is this feature not implemented yet in MVC 6?
I'm looking for a way to add the cache-control, expires, etc. headers to the static content.
It is all about Middleware with AspNet Core;
Add the following to your Configure method in the Startup.cs file
app.Use(async (context, next) =>
{
context.Response.Headers.Add("Content-encoding", "gzip");
context.Response.Body = new System.IO.Compression.GZipStream(context.Response.Body,
System.IO.Compression.CompressionMode.Compress);
await next();
await context.Response.Body.FlushAsync();
});
By the way for caching you would add this to the ConfigureServices method
services.AddMvc(options =>
{
options.CacheProfiles.Add("Default",
new CacheProfile()
{
Duration = 60
});
options.CacheProfiles.Add("Never",
new CacheProfile()
{
Location = ResponseCacheLocation.None,
NoStore = true
});
});
And decorate the control with
[ResponseCache(CacheProfileName = "Default")]
public class HomeController : Controller
{
...
Your title says compress, but your question body says cache. I'll assume you mean both.
Minification of css/javascript is already handled by the grunt task runner on publish. Caching and compression outside this seem like something a webserver is more suited to, rather than the application layer, so here's a great article that details the config for nginx to manage caching and compression for kestrel.
If you're using IIS, you can configure caching and compression directly on it, here's a tutorial. Considering the previous versions of MVC configured this functionality in web.config\system.Webserver which basically sets IIS config values, you can likely still use a web.config for the purposes of configuring IIS (only).