Is there any way to reconfigure middleware after startup? - asp.net

In the ConfigureServices() method of my ASP.NET 6.0 app, I have the following
// Code has been simplified for the purposes of this example.
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.All;
// Only trust "X-Forward*" headers from user-specified hosts.
foreach (var proxy in ApplicationSettings.GetKnownProxies())
{
options.KnownProxies.Add(proxy);
}
});
The ApplicationSettings.GetKnownProxies() method returns something according to what the end-user specified in the app's system configuration screen.
The problem is that the end user can change the settings during the lifetime of the application (either adding or removing proxies). However, since the "configure" code is only run on startup, any changes won't take effect until the web application is restarted.
Is there any way to avoid this? That is to say, is there any way to reconfigure the ForwardedHeaders feature without restarting the entire web application?

Related

Why does GraphQL .Net HttpConext not have the correct user session?

I am using GraphQL .net to respond to graphql queries on the backend of an Asp.net Core website. All the cookies seem to be passed with the requests but for some reason my graphql.net requests do not have the proper user session set on the HttpContext. The ClaimPrincipal is mostly empty via graphql.net while my Asp.net Core WebApi/Mvc style endpoints have the correct principal with user id even though both GraphQl.Net requests and non-graphql.net requests are happening at the same time.
I checked the payload and all the same cookies are passed in both requests. So it makes me wonder why are my regular WebApi endpoints able to (auto-magically) get the claims principal and why can't the graph.net endpoints do the same. As far as I know from previous usages of GraphQl.net I wasn't aware that any special session code had to be added (other than passing the user from the HttpContext to graphQL.net).
I've been reading through GraphQL.Net and Asp.net core source code and docs, but so far I haven't found any obvious offenses or leads.
What might cause some issue like this? what are some common causes?
Should I just try to figure out how to manually read in the cookie to Asp.net core and pull the principal?
Perhaps I'm missing a special header value? I don't think the headers are weird but I haven't done a side by side comparison between the headers in graphql.net and asp.net core requests.
In this snippet is where I first detect a problem. If I put a breakpoint here then the claimsprinical isn't correctly set for the current user session. And also later when I access the HttpContext the user session is not correct for graphql.net requests.
public static GraphQLUserContext InitializeFromContext(HttpContext httpContext)
{
return new GraphQLUserContext
{
User = httpContext.User,
};
}
Here's part of the Graphql.net configuration:
services.AddGraphQL((options, provider) =>
{
options.EnableMetrics = _env.IsDevelopment();
var logger = provider.GetRequiredService<ILogger<WebDependencyInjectionConfig>>();
options.UnhandledExceptionDelegate = ctx => logger.LogError("{Error} occurred", ctx.OriginalException.Message);
})
.AddErrorInfoProvider(opt =>
{
opt.ExposeExceptionStackTrace = _env.IsDevelopment();
opt.ExposeCodes = _env.IsDevelopment();
opt.ExposeCode = _env.IsDevelopment();
opt.ExposeData = _env.IsDevelopment();
opt.ExposeExtensions = _env.IsDevelopment();
})
.AddSystemTextJson()
.AddUserContextBuilder(GraphQLUserContext.InitializeFromContext)
.AddGraphTypes(typeof(PrimarySchema), ServiceLifetime.Scoped);
I'll gladly provide any requested configuration if anyone wants it, but there is a lot of possible code it touches. Thanks!
What does your Configure method look like? Is your app.UseAuthentication() before your GraphQL middleware configuration?
public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
app.UseAuthorization();
app.UseGraphQL<MySchema>();
}
https://github.com/dotnet/aspnetcore/blob/790c4dc2cf59e16e6144f7790328d563ca310533/src/Security/samples/Cookies/Startup.cs#L45-L66

How to create a dotnet gRPC server without using concrete instances

So I am trying to build a .Net Core app that has both REST and gRPC.
Expected results: To have one app running that supports a working REST on one port and gRPC on another.
REST is easy. It's where the app starts. But to configure gRPC's port I saw I needed to create a Server instance:
Server server = new Server
{
Services = { Greeter.BindService(new GreeterImpl()) }
Ports = { new ServerPort("0.0.0.0", 5001, ServerCredentials.Insecure) }
};
server.Start();
That's all fine until we actually put it to use and like any other "Controller" GreeterImpl needs Dependency Injection of services:
private readonly IExampleService _exampleService;
public GreeterImpl (IExampleService exampleService)
{
_exampleService = exampleService;
}
Now the first code snippet will not work since the "new GreeterImpl()" requires an IExampleService.
I searched the web on how to get a ServerServiceDefinition (the thing returned from Greeter.BindService() ) without the use of concrete implementations but found nothing. So, how should this be done or am I on a totally wrong path?
So, I was going at it with the wrong idea. Turns our you can use
app.ApplicationServices.GetService(typeof({YOUR_SERVICE}))
Where "app" is "IApplicationBuilder"
And then just use the resulting service in the ".BindService()".
In the end we don't change the ".BindService()" but pass it a working service to bind.
Important note: You have to register your service first. In my case I used AutoFac to register it

Pre-Load Web Application Pages After Deployment to Prevent Slow Loading

We build and deploy our web application to our dev environment automatically every night (using VSTS). When we come into the office in the morning, the first person to access the application has to wait an extended period for each page to load the first time. Subsequent loads are very fast.
The problem has a greater impact in our live environment where, after a deployment, it is potentially an end-user who is the first person to access the application and complain of slowness. To mitigate for this, a member of the team is currently accessing every page of the application manually after deployment to the live environment so that they 'pre-load' every page, which works, but is obviously time-consuming!
I've done a fair bit of searching on the subject, and have configured the appropriate Application Pool in our IIS server (IIS 8.5) so that its Start Mode is set to "AlwaysRunning". I've also edited our applicationHost file and set the appropriate Sites with the preloadEnabled="true" attribute. I did this after reading the instructions in this very helpful Microsoft documentation.
However, if I'm reading that documentation correctly, any pre-loading of the website which might alleviate the issue we're having (and I'm not even certain that this is the kind of pre-loading that I'm thinking of) only takes place when the server, the IIS service of the Application Pool are restarted. This isn't happening in our case. We need the pre-loading to take place following a deployment of the application to the IIS server.
Is there a way to automate this pre-loading?
One way of doing this would be to perform a HTTP request automatically:
As soon as the app was deployed (by running a task from the deploying machine)
Before the application pool has the chance to shut itself down (using Task Scheduler for instance)
Personally, I use a tool that is run in both cases to keep the site warmed up.
Advantages
Robust control over how and when this warm-up is executed.
It's completely independent from any IIS or web.config setup.
Disadvantages
Generates "bogus" log information.
Keeps the app permanently in memory (the Pool would never time-out, essentially wasting server resources for sites with a low # of visitors).
Sample
Such a tool could be a simple console app written as follows:
var taskInfo = new {
Url = "http://www.a-website-to-keep-warm.url",
UseHostHeader = true,
HostHeader = "www.a-website-to-keep-warm.url",
HttpMethod = "head"
};
HttpStatusCode statusCode = HttpStatusCode.Unused;
long contentLength = 0;
try
{
Dictionary<string, string> headers = new Dictionary<string, string>();
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(taskInfo.Url);
webRequest.Method = taskInfo.HttpMethod.ToUpper();
if(taskInfo.UseHostHeader)
webRequest.Host = taskInfo.HostHeader;
using (HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse())
{
//did we warm-up the site successfully?
statusCode = webResponse.StatusCode;
contentLength = webResponse.ContentLength;
//optionally read response headers
foreach (string header in webResponse.Headers)
{
headers.Add(header, webResponse.Headers[header]);
}
}
decimal kilobytes = Math.Round(contentLength / 1024M, 1);
Debug.WriteLine($"Got {kilobytes:F1} kB with statuscode: \"{statusCode} \" ...");
}
catch (Exception ex)
{
Debug.WriteLine($"taskInfo failed with exception: {ex.Message}");
}
In my case, I read a bunch of taskInfo objects from a json file and execute them asynchronously every X minutes, making sure X is lower than the Pool-timeout value. It is also run immediately after every deploy.
Because we're not interested in getting the entire content, it uses a HTTP HEAD request instead of GET. Lastly, it supports multiple sites on the same host by adding a Host header to the request.

How to read ASP.NET Session on IdentityServer3? OWIN Pipeline Order is broken

Problem: I'm trying to use ASP.NET Session on IdentityServer3 Controllers, but I just can't make it work.
I found this similar question that explains how to enable session on OWIN middleware, and it worked perfectly: I created some controllers outside of IdentityServer (that is, outside of the pipeline Mapped in "/core") and it worked perfectly. I also added Autofac to my controllers to make sure that Autofac (used extensivly in IdentityServer) was not the problem, and it works fine.
This is working code - Startup.cs:
// register middleware that enables session using SetSessionStateBehavior(SessionStateBehavior.Required)
app.RequireAspNetSession();
var config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterType<DummyClassCreatedByAutofac>();;
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
//app.UseIdentityServer(); // if I add IdentityServer, context.Session is always null
This works fine. The middleware (SetSessionStateBehavior) runs on every requisition, and the session is available on the constructor. However, as soon as I add app.UseIdentityServer() to my Startup.cs, IdentityServer starts working perfectly (and all requisitions pass through the session middleware) but now Session is null for all controllers - not only for IdentityServer controllers but also for my other controllers (which are outside of IdentityServer pipeline) - they stop working.
If I completely remove all my code and just stick with regular IdentityServer pipeline, I just can't find the correct place to add RequireAspNetSession() in the pipeline. I tried adding in many different places, but couldn't make it work.
After some trial-and-error, I realized that the session middleware RequireAspNetSession() was being broken by the following two calls: app.UseEmbeddedFileServer() and app.ConfigureCookieAuthentication. When both calls were removed, Session was available to IdentityServer controllers (and also were available again to my other controllers). Probably the problem is because some of those OWIN middlewares run on specific stages (using UseStageMarker and defining a PipelineStage), and probably I'm breaking the priority order of the middlewares. UseEmbeddedFileServer for example runs on stage PipelineStage.MapHandler, as well as
RequireAspNetSession.
I tried adding app.RequireAspNetSession() both before and after those middlewares, but it didn't work either. In fact, app.RequireAspNetSession() never works when it is used inside Map() method, which is where those middlewares are configured (inside UseIdentityServer()):
app.Map("/core", coreApp =>
{
// this DOESN'T work, even if I remove
// UseEmbeddedFileServer() or ConfigureCookieAuthentication()
coreApp.RequireAspNetSession();
//...
coreApp.UseIdentityServer(idsrvOptions);
}
_
// this WORKS as long as inside the Map() I don't call
// UseEmbeddedFileServer() or ConfigureCookieAuthentication()
app.RequireAspNetSession();
app.Map("/core", coreApp =>
{
//...
coreApp.UseIdentityServer(idsrvOptions);
}
Last, if I don't use the Map method (and setup the IdentityServer API directly on root folder), it works fine (session is available to all controllers, even if I keep the UseEmbeddedFileServer() and ConfigureCookieAuthentication() middlewares). But it's not acceptable because I need to run APIs on a mapped folder.
In summary: If I use RequireAspNetSession() inside Map(), it doesn't work (session is always null). If I use RequireAspNetSession() outside Map() but keep the UseEmbeddedFileServer() or ConfigureCookieAuthentication() inside the Map(), it also doesn't work.
How to make the RequireAspNetSession() work in IdentityServer3 pipeline?
Also, how could Map("/core") affect (and broke) the pipeline for my DefaultController that is hosted outside of that pipeline?
It is not broken. It is by design. IdentityServer clears session on purpose.
You have a couple alternatives.
1) you can create a cookie helper
2) the SignInMessage holds most of what you need -> Don't forget about LoginViewModel
Once your user or application is signed in, session is under your control again.

Get URL information in Startup

In an asp.net MVC 5 project I'm using a katana owin based middlewere to handle the authentication. Inside the Start.cs file I have the Startup class with the Configuration method.
Is there a way to get the full URL of the request inside the Configuration method? I need to get the last part of it to be stored in a cookie
public void Configuration(IAppBuilder app) {
app.UseCookieAuthentication(new CookieAuthenticationOptions { ... }
// something here to get the full URL
// other authentication code here
}
Startup runs outside of the request-cycle. In fact, it only runs once, and then multiple successive URLs can be serviced before it ever runs again (when AppPool recycles, server restarts, etc.)
Long and short, even if you could access the URL, it wouldn't do you any good because it would simply be the first random URL that was accessed, which may or may not be applicable to whatever you're trying to do here.

Resources