I added BasicAuthentication to a WebApplication.
I used this tutorial:
https://www.roundthecode.com/dotnet/how-to-add-basic-authentication-to-asp-net-core-application
When I use it on endpoints, this works fine.
But it doesn't have any effect when I use it with MapWhen (adding UseAuthentication & UseAuthorization pretty much everywhere, but to no avail...).
Why does this not work with MapWen ?
All I do in MapWhen is checking if a request-path starts with <rootPath>
, and if so, use this middleware.
This is my middleware + extension.
Meanwhile, I figured out how to add the middleware with endpoints.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
namespace IdentiyService
{
// https://github.com/dotnet/aspnetcore/blob/146f49fdf09916d0c63e82570bfe059d7fb845e6/src/Middleware/HealthChecks/src/Builder/HealthCheckEndpointRouteBuilderExtensions.cs
public static class JsonServiceMiddlewareExtensions
{
public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseMockJson(
this Microsoft.AspNetCore.Builder.IApplicationBuilder builder
, string rootPath, Microsoft.AspNetCore.Hosting.IWebHostEnvironment env, string jsonFile)
{
// This works fine
#if USE_ENDPOINTS
IApplicationBuilder x = builder.UseEndpoints(endpoints =>
{
Microsoft.AspNetCore.Http.RequestDelegate pipeline = endpoints.CreateApplicationBuilder()
.UseMiddleware<JsonServiceMiddleware>(env, jsonFile)
.Build();
endpoints
.Map(rootPath, pipeline)
.WithDisplayName("MockJson")
// .RequireAuthorization("BasicAuthentication")
;
});
#else
// And this just doesn't want to work ...
IApplicationBuilder x = Microsoft.AspNetCore.Builder.MapWhenExtensions.MapWhen(builder,
// ctx => ctx.Request.Path.StartsWithSegments(rootPath, System.StringComparison.InvariantCultureIgnoreCase),
delegate (Microsoft.AspNetCore.Http.HttpContext ctx)
{
bool ret = ctx.Request.Path.StartsWithSegments(rootPath, System.StringComparison.InvariantCultureIgnoreCase);
return ret;
}
,
delegate (Microsoft.AspNetCore.Builder.IApplicationBuilder app)
{
IApplicationBuilder fsck = app.UseRouting().UseAuthentication().UseAuthorization();
// app.UseAuthentication().UseAuthorization();
IApplicationBuilder foo = Microsoft.AspNetCore.Builder.UseMiddlewareExtensions
.UseMiddleware<JsonServiceMiddleware>(
fsck // app
, env
, jsonFile
);
foo.UseAuthentication().UseAuthorization();
// Microsoft.AspNetCore.Builder.AuthAppBuilderExtensions.UseAuthentication(app);
}
);
x = x.UseAuthentication().UseAuthorization();
#endif
return x;
} // End Extension method UseMockJson
} // End Class JsonServiceMiddlewareExtensions
// [Middleware.BasicAuthorization()] // has no effect ...
public class JsonServiceMiddleware
{
private readonly Microsoft.AspNetCore.Http.RequestDelegate next;
private readonly Microsoft.AspNetCore.Hosting.IWebHostEnvironment m_env;
private readonly string m_jsonFile;
public JsonServiceMiddleware(Microsoft.AspNetCore.Http.RequestDelegate next,
Microsoft.AspNetCore.Hosting.IWebHostEnvironment env,
string jsonFile)
{
this.next = next;
this.m_env = env;
this.m_jsonFile = jsonFile;
} // End Constructor
// https://localhost:44373/api/jid/v1/units
[Middleware.BasicAuthorization()] // this has EFFECT !
public async System.Threading.Tasks.Task InvokeAsync(Microsoft.AspNetCore.Http.HttpContext context)
{
RouteData route = context.GetRouteData();
string name = (string)route.Values["name"];
// string path = System.IO.Path.Combine(m_env.ContentRootPath, "json");
// string result = FindFile(path, name+".json");
// System.Console.WriteLine(result);
if (name == null)
{
// context.Response.StatusCode = (int)System.Net.HttpStatusCode.BadRequest;
// return;
name = System.IO.Path.GetFileNameWithoutExtension(this.m_jsonFile);
}
name = name.ToLowerInvariant();
string output = @"{
""type"": """ + m_jsonFile + @""",
""webRoot"": """ + m_env.WebRootPath + @""",
""contentRoot"": """ + m_env.ContentRootPath + @"""
}";
// output = System.IO.Path.Combine(m_env.ContentRootPath, "json", this.m_jsonFile);
output = System.IO.Path.Combine(m_env.ContentRootPath, "json", name + ".json");
output = System.IO.Path.GetFullPath(output);
if (!output.StartsWith(m_env.ContentRootPath))
{
// context.Response.StatusCode = (int)System.Net.HttpStatusCode.Forbidden;
context.Response.StatusCode = (int)System.Net.HttpStatusCode.UnavailableForLegalReasons;
return;
}
if (!System.IO.File.Exists(output))
{
context.Response.StatusCode = (int)System.Net.HttpStatusCode.BadRequest;
return;
}
output = System.IO.File.ReadAllText(output, System.Text.Encoding.UTF8);
// userType = EmployeeUser
// modifiedSince = 2021 - 06 - 23T04 % 3A40 % 3A36Z
context.Response.StatusCode = 200;
context.Response.ContentType = "application/json; charset=utf-8";
await Microsoft.AspNetCore.Http.HttpResponseWritingExtensions.WriteAsync(context.Response, output);
} // End Task InvokeAsync
} // End Class JsonServiceMiddleware
}
This is my Startup.cs
using IdentiyService.Middleware;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace IdentiyService
{
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication().AddScheme<
Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions,
Middleware.BasicAuthenticationHandler>("BasicAuthentication", options => { });
services.AddAuthorization(options =>
{
options.AddPolicy("BasicAuthentication",
new Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder("BasicAuthentication")
.RequireAuthenticatedUser()
.Build()
);
});
// services.AddHealthChecks();
// https://dotnetcoretutorials.com/2017/01/24/using-gzip-compression-asp-net-core/
// services.AddResponseCompression();
services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.Providers.Add(new BrotliCompressionProvider());
options.Providers.Add(new GzipCompressionProvider());
options.Providers.Add(new DeflateCompressionProvider());
// https://github.com/MikeStall/DataTable
// https://github.com/stevehansen/csv/
// https://joshclose.github.io/CsvHelper/
// https://joshclose.github.io/CsvHelper/examples/
// https://github.com/JoshClose/CsvHelper
options.MimeTypes = new[] {
"text/plain", "text/html", "text/css", "text/csv"
,"application/javascript", "application/json", "application/xml"
,"image/x-icon", "image/png", "image/gif", "image/jpeg", "image/webp", "image/tiff", "image/svg+xml"
};
});
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "IdentiyService", Version = "v1" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "IdentiyService v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
// app.UseWhen(x => true, delegate(IApplicationBuilder app) { });
// Why is this not working ? No matter what I do, it has absolutely no effect ...
// But it works just fine with endpoints ?
// https://localhost:44373/api/jid/v1/buildings
app.UseMockJson("/api/jid/v1/buildings", env, "buildings.json").UseAuthentication().UseAuthorization();
// https://localhost:44373/api/jid/v1/units
app.UseMockJson("/api/jid/v1/units", env, "units.json").UseAuthentication().UseAuthorization();
// https://localhost:44373/api/jid/v1/users
app.UseMockJson("/api/jid/v1/users", env, "users.json").UseAuthentication().UseAuthorization();
// This is for use with EndPoints
// app.UseMockJson("/api/jid/v1/{name}", env, "users.json");
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
// https://learn.microsoft.com/en-us/answers/questions/528683/how-to-do-authorization-for-usehealthcheck-middlew.html
// IEndpointConventionBuilder ep = endpoints.MapHealthChecks("/health");
// ep.RequireAuthorization();
endpoints.MapSomeMiddleware("/foo").RequireAuthorization("BasicAuthentication");
}); //.UseAuthentication().UseAuthorization();
}
}
}
Answering my own question.
Apparently, UseAuthentication() and UseAuthorization() is thought for use for endpoints only.
So if you applied UseMiddleware with UseAuthentication/UseAuthorization, then authentication/authorization doesn't apply to this middleware.
The only problem is the name. It's meant for endpoints, so you can't use it to protect the folders or other things.
That also explains why you have to apply UseAuthentication/UseAuthorization after UseRouting and before UseEndpoints.
It must logically go after UseRouting, so that route information is available
for authentication decisions.
But before UseEndpoints, so that users are authenticated before accessing the endpoints.
That makes a lot of sense. It would be dumb to try to figure out if a request was authorized before you even knew which endpoint was requested and whether this endpoint required authentication(authorization or not.
Als app.UseAddAuthentication() is a cross-cutting concern that occurs after routing is applied.
So not all routes will have authentication applied to them.
So caution when you use UseAuthentication/UseAuthorization/UseAddAuthentication - it might not actually do what the name implies - which is rather dangerous.