asp.net core favicon.ico goes through custom middleware - asp.net-core

In my CustomMiddleware, I have a simple logging and some Authenticated user related code.
It seems like favicon.ico request goes through CustomMiddleware, but request path is "/" same as index page page. can not differentiate.
If I open up a link like this - https://localhost:5001/favicon.ico, it does not hit my debug point.
I need help to understand why first time ONLY request "/", it goes through CustomMiddleware ???
In the CustomMiddleware, first two request path "/" (one is Index), IsAuthenticated is false.
after that, it is always true as it goes through OIDC authentication.

You could read the offcial document:https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-6.0
The ASP.NET Core request pipeline consists of a sequence of request delegates, called one after the other.
Each delegate can perform operations before and after the next delegate. Exception-handling delegates should be called early in the pipeline, so they can catch exceptions that occur in later stages of the pipeline.
When a delegate doesn't pass a request to the next delegate, it's called short-circuiting the request pipeline. Short-circuiting is often desirable because it avoids unnecessary work. For example, Static File Middleware can act as a terminal middleware by processing a request for a static file and short-circuiting the rest of the pipeline.
You could write two custommiddle and understand how middlewareworks
public class MyCustomMiddleWare
{
private RequestDelegate _next;
public MyCustomMiddleWare(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var pathstring = context.Request.Path.ToString();
var pathlist = pathstring.Split("/").ToList();
if (pathlist[1]=="")
{
await _next.Invoke(context);
}
else
{
await context.Response.WriteAsync("Result");
}
}
}
public class MyAnotherCustomMiddleWare
{
private RequestDelegate _next;
public MyAnotherCustomMiddleWare(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
await _next.Invoke(context);
}
}
in startupclass:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
.......
app.UseStaticFiles();
app.UseMiddleware<MyCustomMiddleWare>();
app.UseMiddleware<MyAnotherCustomMiddleWare>();
.......
}
Test Result:
If you open up a link of staticfile and the request hit your custommiddleware behind UseStaticFile Middleware,check if the static file exists.
(Has the BuildAction property of file set as "content"?and check the codes in csproj related which update content files)

The order of the Middlewares is very important and they run from top to bottom They can receive the request, process it, and pass that request to the next Middleware, or not. When the file request like https://localhost:5001/favicon.ico reaches the UseStaticFile Middleware, it processes that request and no longer sends it to its lower Middleware
But when the request is not a file request like https://localhost:5001/, the UseStaticFile Middleware receives the request and passes it to the next middleware.
This is why the request does not reach your custom Middleware. If you want it to be called, you must register your Middleware before the UseStaticFile Middleware like this :
app.UseMiddleware<CustomMiddlware>();
app.UseStaticFiles();
You only need to pay attention to one point: static files like css and ... are cached by the browser after first request. If you request them again, your request will not reach your APP and will be processed by the browser.

Related

Is there any way to avoid challenging all registered authentication schemes when one of them succeeds?

I am facing an issue related to handling multiple authentication schemes in an asp.net core application which exposes API.
In particular, I am attempting to make two different authentication modes available, by registering two schemes, each with its own handler.
At the moment my configuration code (simplified) looks like this:
public class Startup {
public void ConfigureServices(IServiceCollection services) {
services
.AddAuthentication()
.AddScheme<AuthenticationSchemeOptions, BasicAuthHandler>("Basic", o => {});
.AddScheme<AuthenticationSchemeOptions, ApiKeyAuthHandler>("ApiKey", o => {});
services.AddAuthorization(o => {
o.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("Basic", "ApiKey").Build();
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
...
app.UseAuthentication();
app.UseAuthorization();
...
}
}
public class BasicAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions> {
protected override async Task<AuthenticateResult> HandleAuthenticateAsync() {
Console.WriteLine("Challenging Basic Authentication");
if( ... ) {
return AuthenticateResult.Success(...);
}
return AuthenticateResult.Fail(...);
}
}
public class ApiKeyAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions> {
protected override async Task<AuthenticateResult> HandleAuthenticateAsync() {
Console.WriteLine("Challenging ApiKey Authentication");
if( ... ) {
return AuthenticateResult.Success(...);
}
return AuthenticateResult.Fail(...);
}
}
The ApiKeyAuthHandler authenticates requests depending on a custom header (say X-ApiKey) value, the BasicAuthHandler authenticates requests depending on credentials provided through Authorization: Basic BASE_ENCODED_USERNAME_AND_PASSWORD header.
Both handlers work fine: I can provide Authorization header with basic auth credentials and I can provide an api-key through the custom header.
Generally speaking, the two registered handlers are executed sequentially, in the same order I've registered them. So far, so good.
However, when I provide both basic credentials in Authorization header and api-key through X-ApiKey header, I noticed that
the first handler (BasicAuthHandler) is executed, returning AuthenticateResult.Success
the second handler (ApiKeyAuthHandler) is executed, even the first already authenticated the request successfylly, also returning AuthenticateResult.Success
the identity finally available in my controller is the one built by the second handler
I'm wondering why the second handler is executed after the first completed authentication successfully: I googled about this but can't found any configuration flags to enable a sort of short-circuit after the first success result.
The reason seems to be this foreach statement that iterates over all available authentication schemes regardless of theirs results.
Does anyone have any idea of a way to challenge available authentication scheme while no one is successful, stopping when the first succeeds?
No doubt I could implement a CompositeAuthHandler in order to manage this requirement, but I would prefer an already-made, official solution to my problem.
A note about the context which justifies the requirement of short-circuit handler evaluation: the example provided above is simplified and uses two very cheap authentication handlers, but the real case I'm facing off involves at least one very slow handler, that perform paid calls to an external system - so I would like to avoid calling it for requests already authenticated by former handlers.

Identity Server 4 OIDC authorization_code flow multi tenancy

I'm facing an issue for authenticating my users on my IS4 server with an application (Blazor Server/WASM).
My IDSRV4 is located on the same app that the API to protect, and each client has it owns database (for users and business models).
To choose the good database, I use an header and have set up a interceptor on EF Core to choose database according to header value.
Everything works great, except authentication. I was using resource_owner_password flow with a JavaScript app (security breach, I know) which allows me to pass custom headers for each calls (calling /connect/token with my custom header).
Now, with the authorization_code flow, I cannot manage to pass the header to choose good tenant, meaning good database, which causes authentication to failed because token is validated on another database that the user is currently register to.
I can pass the tenant to choose database during login process (when posting login info on IDSRV4 side), and I've set up event on client side to add header on when sending back code for having back id_token, but I lost the capability to do that after my idp redirect user after login on /connect/authorize (it seems there no extension on IDSRV4 side for adding custom headers).
So my user post login data (with tenant infos), that I lost when redirecting to /connect/authorize, and I have no way to customize this. Is there any chance to do that ?
Merging all users in the same database is not an option.
You can still add any custom headers to any HttpResponse from your MVC app hosting Identityserver.
// public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseCustomHeaders();
//CustomHeadersMiddleware.cs
public static class CustomHeadersMiddlewareExtensions
{
public static IApplicationBuilder UseCustomHeaders(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<CustomHeadersMiddleware>();
}
}
public class CustomHeadersMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<CustomHeadersMiddleware> _logger;
public CustomHeadersMiddleware(RequestDelegate next,
ILogger<CustomHeadersMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task Invoke(HttpContext context)
{
var watch = new Stopwatch();
watch.Start();
//To add Headers AFTER everything you need to do this
context.Response.OnStarting(() =>
{
if (!context.Response.Headers.ContainsKey("X-Response-Time-Milliseconds"))
context.Response.Headers.Add("X-Response-Time-Milliseconds",
new[] {watch.ElapsedMilliseconds.ToString()});
return Task.FromResult(0);
});
await _next(context);
}
}

ASP.NET Core: How to skip running authentication on [AllowAnonymous]

How to identify if authentication is not needed for current request?
We have a custom authentication handler, simplified here:
internal class CustomAuthHandler : AuthenticationHandler<CustomAuthOptions>
{
static readonly string[] DontCheckAuth = new string[] { "/servers", "/user/profile/", "/highscore/list", "/highscore/metadata/" };
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
/*
if url contains any of DontCheckAuth
then return AuthenticateResult.NoResult()
else
do custom auth
*/
}
}
So everything works OK, but HandleAuthenticateAsync is run on every request, even on URLs I have marked as [AllowAnonymous]. Currently I just check the URL and compare it to hard-coded list of URLs that does not require authentication.
How can I automate this process so I don't have to manually update the list of URLs (which I forget to do for new APIs)?

How to extend IdentityServer4 workflow to run custom code

I have a basic Identityserver4 implementation based on the quick start sample.
In my startup I have the following:
public void ConfigureServices(IServiceCollection services)
{
// configure identity server with in-memory stores, keys, clients and scopes
services.AddIdentityServer()
.AddTemporarySigningCredential()
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients());
}
public void Configure(IApplicationBuilder app)
{
...
app.UseIdentityServer();
}
I want to extend the IdentityServer4 workflow so that after the access token is generated I can run business logic (based on the claims in the access token) and modify the response send to the calling client. I tried creating a .NET core middleware but it seems the IdentityServer middleware short-circuits the rest of the pipeline (no middleware place after the UseIdentityServer will be executed).
Are they any extension method in Identityserver4 that I can use to always modify the response issued by IdentityServer4? I am using the credentials grant. Essentially I want to run some business logic to modify the response send to the client once IdentityServer4 is done with its workflow
Unfortunately, there is no way to do that.
When you request any IdentityServer endpoint, IdentityServer middleware short-circuits the rest of the pipeline.
You can check source code:
IdentityServerMiddleware class.
I believe it was done for a reason. But if you really need to modify the response, you have at least three options:
Create a fork and remove return operator from
IdentityServerMiddleware Invoke method (be careful to short-circuit the rest of the pipeline adding return into your last middleware).
Create your own IdentityServerMiddleware, IdentityServerApplicationBuilderExtensions implementations and use
them instead of default.
Place your middleware before the UseIdentityServer. Your middleware should look like this:
public ResponseBodyEditorMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
// get the original body
var body = context.Response.Body;
// replace the original body with a memory stream
var buffer = new MemoryStream();
context.Response.Body = buffer;
// invoke the next middleware from the pipeline
await _next.Invoke(context);
// get the body as a string
var bodyString = Encoding.UTF8.GetString(buffer.GetBuffer());
// make some changes
bodyString = $"The body has been replaced!{Environment.NewLine}Original body:{Environment.NewLine}{bodyString}";
// update the memory stream
var bytes = Encoding.UTF8.GetBytes(bodyString);
buffer.SetLength(0);
buffer.Write(bytes, 0, bytes.Length);
// replace the memory stream with updated body
buffer.Position = 0;
await buffer.CopyToAsync(body);
context.Response.Body = body;
}

Web API 2 return OK response but continue processing in the background

I have create an mvc web api 2 webhook for shopify:
public class ShopifyController : ApiController
{
// PUT: api/Afilliate/SaveOrder
[ResponseType(typeof(string))]
public IHttpActionResult WebHook(ShopifyOrder order)
{
// need to return 202 response otherwise webhook is deleted
return Ok(ProcessOrder(order));
}
}
Where ProcessOrder loops through the order and saves the details to our internal database.
However if the process takes too long then the webhook calls the api again as it thinks it has failed. Is there any way to return the ok response first but then do the processing after?
Kind of like when you return a redirect in an mvc controller and have the option of continuing with processing the rest of the action after the redirect.
Please note that I will always need to return the ok response as Shopify in all it's wisdom has decided to delete the webhook if it fails 19 times (and processing too long is counted as a failure)
I have managed to solve my problem by running the processing asynchronously by using Task:
// PUT: api/Afilliate/SaveOrder
public IHttpActionResult WebHook(ShopifyOrder order)
{
// this should process the order asynchronously
var tasks = new[]
{
Task.Run(() => ProcessOrder(order))
};
// without the await here, this should be hit before the order processing is complete
return Ok("ok");
}
There are a few options to accomplish this:
Let a task runner like Hangfire or Quartz run the actual processing, where your web request just kicks off the task.
Use queues, like RabbitMQ, to run the actual process, and the web request just adds a message to the queue... be careful this one is probably the best but can require some significant know-how to setup.
Though maybe not exactly applicable to your specific situation as you are having another process wait for the request to return... but if you did not, you could use Javascript AJAX kick off the process in the background and maybe you can turn retry off on that request... still that keeps the request going in the background so maybe not exactly your cup of tea.
I used Response.CompleteAsync(); like below. I also added a neat middleware and attribute to indicate no post-request processing.
[SkipMiddlewareAfterwards]
[HttpPost]
[Route("/test")]
public async Task Test()
{
/*
let them know you've 202 (Accepted) the request
instead of 200 (Ok), because you don't know that yet.
*/
HttpContext.Response.StatusCode = 202;
await HttpContext.Response.CompleteAsync();
await SomeExpensiveMethod();
//Don't return, because default middleware will kick in. (e.g. error page middleware)
}
public class SkipMiddlewareAfterwards : ActionFilterAttribute
{
//ILB
}
public class SomeMiddleware
{
private readonly RequestDelegate next;
public SomeMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
await next(context);
if (context.Features.Get<IEndpointFeature>().Endpoint.Metadata
.Any(m => m is SkipMiddlewareAfterwards)) return;
//post-request actions here
}
}
Task.Run(() => ImportantThing() is not an appropriate solution, as it exposes you to a number of potential problems, some of which have already been explained above. Imo, the most nefarious of these are probably unhandled exceptions on the worker process that can actually straight up kill your worker process with no trace of the error outside of event logs or something at captured at the OS, if that's even available. Not good.
There are many more appropriate ways to handle this scenarion, like a handoff a service bus or implementing a HostedService.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-6.0&tabs=visual-studio