Asp.net core, when in debug, bypass Authorization - asp.net-core

I have a test web site which uses the aspnetCore [AuthorizeAttribute] at the entire controller level to ensure only Authenticated Users can hit this site.
While we debug and test new features, we constantly have to comment out the attribute in each controller, which I know will be easy to forget and might get merged some day.
We've had good success with checking to see if a Debugger is attached before...I am wondering which AuthenticationScheme I should specify to allow anonymous, only if debugging.
I extend the base AuthorizeAttribute so I have an easy place to shim in some code.
public class MyAppAuthorizeAttribute : AuthorizeAttribute
{
public MyAppAuthorizeAttribute()
: base(Policies.MyAppAuthorize)
{
if (System.Diagnostics.Debugger.IsAttached)
{
Console.WriteLine("Skipping auth for debug"); //we hit this line but...
this.AuthenticationSchemes = AllowAnonymousAttribute //this setting does not work
}
else
{
this.AuthenticationSchemes = "IntegratedWindowsAuthentication";
}
}
}

Seems like a good candidate for IWebHostingEnvironment.IsDevelopment():
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (!env.IsDevelopment())
{
app.UseAuthentication();
app.UseAuthorization(); // maybe optional, depends on your case
}
...

Your requirement may be necessary in some cases where the user is required to be authenticated BUT not actually referenced in the code (e.g: the code does not access any info on the current identity, especially related to the business model of the user).
So I assume that you are aware of that because the following will just simply remove the requirement to check for authenticated user when the code runs in the development environment. There is another way to auto sign-in a dummy user which can be better in some scenarios.
Here is the first solution, it configures a default policy only which does not include the DenyAnonymousAuthorizationRequirement (which is the only requirement contained in the default policy). That means if you have multiple policies used somewhere (with AuthorizeAttribute), only the default will be ignored (while debugging). The second solution (shown later) may suit that scenario better.
//inject IHostingEnvironment in the Startup constructor
public Startup(IConfiguration configuration, IHostingEnvironment env){
HostingEnvironment = env;
}
public IHostingEnvironment HostingEnvironment { get; }
//in the ConfigureServices method in Startup.cs
services.AddAuthorization(o => {
if (HostingEnvironment.IsDevelopment()){
o.DefaultPolicy = new AuthorizationPolicyBuilder(CookieAuthenticationDefaults.AuthenticationScheme)
//there should be at least 1 requirement
//here we add a simple always-passed assertion
.RequireAssertion(e => true).Build();
}
//...
});
We need to use IHostingEnvironment (in .net core 2.2, since 3.0 we have 2 alternatives IWebHostEnvironment and IHostEnvironment) so we inject it in the Startup constructor and store it in a readonly property (as you see above). There is another way is try to get the ASPNETCORE_ENVIRONMENT variable directly like this:
var isDevelopment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development";
Here is the second solution in which you use a custom global IAsyncAuthorizationFilter to auto sign-in a dummy user (so it's always authenticated for all requests).
public class AllowAnonymousFilterAttribute : Attribute, IAsyncAuthorizationFilter
{
readonly IHostingEnvironment _hostingEnvironment;
public AllowAnonymousFilterAttribute(IHostingEnvironment env){
_hostingEnvironment = env;
}
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
if (_hostingEnvironment.IsDevelopment() && !context.HttpContext.User.Identity.IsAuthenticated)
{
//prepare a dummy user to auto sign-in
HttpContext.User = new ClaimsPrincipal(new[] {
new ClaimsIdentity(new []{ new Claim(ClaimTypes.NameIdentifier,"admin")},
CookieAuthenticationDefaults.AuthenticationScheme)
});
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
HttpContext.User);
}
}
}
Register the filter globally, here I write code for .net core 2.2:
services.AddMvc(o => {
//register the filter only for development (should be debugging)
if (HostingEnvironment.IsDevelopment()){
o.Filters.Add<AllowAnonymousFilterAttribute>();
}
//...
});
I'm sure there are still some other solutions but what I've introduced here are fairly simple and good enough for your purpose.
P/S: the first solution I've introduced above suits for .net core 2.2 (actually currently I do not have access to newer versions of .net core, it's a pity). For the newer versions, the Authorization middleware is separate so you may just simply not call .UseAuthorization() middleware in the Configure method (of course for development environment only) as one other answer suggests.

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.

.Net Core 3.1 ClaimsTransformation Manually Added Claims Not Persisting

I will be accessing several tables to determine if a user is "Validated" or not as well as adding custom roles to a Windows authenticated user for authorization. For now I'm running a test in a basic .net Core web application just to see how I should be doing this. I have setup a RequiredClaim in my Fallback Policy and a ClaimsLoader and it works great:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddTransient<IClaimsTransformation, ClaimsLoader>();
services.AddAuthentication(IISDefaults.AuthenticationScheme);
services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireClaim("ValidatedUser")
.Build();
});
}
public class ClaimsLoader : IClaimsTransformation
{
public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
var claimsIdentity = (ClaimsIdentity)principal.Identity;
claimsIdentity.AddClaim(new Claim("ValidatedUser", ""));
return await Task.FromResult(principal);
}
}
As long as that AddClaim line is in there, they can access the app, without it they get a not-authorized response which is what I want.
Based on what I've read I thought any claims/roles I add in the transformation should come back each time but they do not. In the code above I have the AddClaim running every time so it's working, but in reality I will be going to a database to determine if I should add that claim which is an expensive process. I want to persist the results across multiple requests. So I want to check if the claim is already there and not bother getting it again if it is. For whatever reason it is NEVER there when it comes back for a second request.
From what I've read here back in 2.x the claims should persist:
https://philipm.at/2018/aspnetcore_claims_with_windowsauthentication.html
But here in my 3.1 application they do not.

.NET Core DI passing info of AddHttpContextAccessor to a service

In my solution I have projects for my API, my Web App and also have another project which includes services, that are getting some information from a database and formatting them, these are currently only used by this API, but these could be used by other API projects in the future.
My API have a couple controllers that are returning JSON data from the result returned by the services.
In some cases the services needs to call the API to process some information before calling the request to the database. Since I have dev/staging/prod environment with their own URL I don't want to hardcode the URLs in the services I want to use DI to get these dynamicaly depending on the context.
In the Startup.cs of my API I have added services.AddHttpContextAccessor(); in the ConfigureServices(IServiceCollection services) section to gain access to the current http context :
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHttpContextAccessor();
...
}
With that I know I can now access the information directly into my controller which I tried and it worked :
public class DataController : ControllerBase
{
...
private readonly string _baseUrl;
public FeaturesController(...
,IHttpContextAccessor httpContextAccessor)
{
...
_baseUrl = UrlHelpers.ShowBaseURL(httpContextAccessor) ?? throw new ArgumentNullException(nameof(_baseUrl));
}
}
public static class UrlHelpers
{
public static string ShowBaseURL(IHttpContextAccessor httpcontextaccessor)
{
var request = httpcontextaccessor.HttpContext.Request;
var absoluteUri = string.Concat(
request.Scheme,
"://",
request.Host.ToUriComponent(),
request.PathBase.ToUriComponent());
return absoluteUri;
}
}
I could do just about the same thing in the services but to me they should not act directly on the httpcontext, since this is not the job they are meant to do. I am sure I could do better by adding a class injected of some sort that would have then make the specific value available to my services.
I know I could also pass the _baseUrl directly as an argument when calling the services from my controller but since I am trying to better understand DI and use it I would rather find another way if it is viable.
I can't give credit but I went with Steven solution which make the most sens

GetRequiredService from within Configure

I'm trying to access one of my services from within the Configure call within Startup.cs in aspnet core. I'm doing the following however I get the following error "No service for type 'UserService' has been registered." Now I know it is registered because I can use it in a controller so I'm just doing something wrong when it comes to using it here. Please can someone point me in the right direction. I'm happy with taking a different approach to setting up Tus if there's a better way of achieving what I want.
var userService = app.ApplicationServices.GetRequiredService<UserService>();
userService.UpdateProfileImage(file.Id);
The below is where I'm wanting to use
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
... Other stuff here...
app.InitializeSimpleInjector(container, Configuration);
container.Verify();
app.UseTus(httpContext =>
{
var restaurantEndpoint = "/restaurant/images";
var userEndpoint = "/account/images";
var endPoint = "/blank/images";
if (httpContext.Request.Path.StartsWithSegments(new PathString(restaurantEndpoint)))
{
endPoint = restaurantEndpoint;
}
if (httpContext.Request.Path.StartsWithSegments(new PathString(userEndpoint)))
{
endPoint = userEndpoint;
}
return new BranchTusConfiguration
{
Store = new TusDiskStore(#"C:\tusfiles\"),
UrlPath = endPoint,
Events = new Events
{
OnBeforeCreateAsync = ctx =>
{
return Task.CompletedTask;
},
OnCreateCompleteAsync = ctx =>
{
return Task.CompletedTask;
},
OnFileCompleteAsync = async ctx =>
{
var file = await ( (ITusReadableStore)ctx.Store ).GetFileAsync(ctx.FileId, ctx.CancellationToken);
var userService = app.ApplicationServices.GetRequiredService<UserService>();
userService.UpdateProfileImage(file.Id);
}
}
};
});
... More stuff here...
};
My end goal is to move this to an IApplicationBuilder extension to clean up my startup.cs but that shouldn't affect anything if it's working from within startup.cs
Edit: Add to show the registration of the userService. There is a whole lot of other stuff being registered and cross wired in the InitializeSimpleInjector method which I've left out. can add it all if need be..
public static void InitializeSimpleInjector(this IApplicationBuilder app, Container container, IConfigurationRoot configuration)
{
// Add application presentation components:
container.RegisterMvcControllers(app);
container.RegisterMvcViewComponents(app);
container.Register<UserService>(Lifestyle.Scoped);
container.CrossWire<IServiceProvider>(app);
container.Register<IServiceCollection, ServiceCollection>(Lifestyle.Scoped);
}
Please read the Simple Injector integration page for ASP.NET Core very closely, as Simple Injector integrates very differently with ASP.NET Core as Microsoft documented how DI Containers should integrate. The Simple Injector documentation states:
Please note that when integrating Simple Injector in ASP.NET Core, you do not replace ASP.NET’s built-in container, as advised by the Microsoft documentation. The practice with Simple Injector is to use Simple Injector to build up object graphs of your application components and let the built-in container build framework and third-party components
What this means is that, since the built-in container is still in place, resolving components using app.ApplicationServices.GetRequiredService<T>()—while they are registered in Simple Injector—will not work. In that case you are asking the built-in container and it doesn't know about the existence of those registrations.
Instead, you should resolve your type(s) using Simple Injector:
container.GetInstance<UserService>()

How create a middleware with api endpoints in .NET Core

I have created the web application with the web api. The application contains some Controllers for example TodoController:
namespace TodoApi.Controllers
{
[Route("api/[controller]")]
public class TodoController : Controller
{
private readonly TodoContext _context;
public TodoController(TodoContext context)
{
_context = context;
}
[HttpGet]
public IEnumerable<TodoItem> GetAll()
{
return _context.TodoItems.ToList();
}
}
}
If I create the GET request - /api/todo - I get the list of Todos from database.
I have a list of controllers and api endpoints like above.
I would like distribute this api to another application ideally like middleware - my idea is register in Startup.cs like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddTodoApi();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseTodoApi();
}
This will be awesome use case for my api but I don't know how this controllers api endpoints rewrite like middleware and return same JSON data same approache like using classic Controllers.
How can I write the middleware in .NET Core for creating API endpoints?
Instead of the separate middleware, you may configure the MVC middleware to discovery controllers from another assembly:
// using System.Reflection;
public void ConfigureServices(IServiceCollection services)
{
...
services
.AddMvc()
.AddApplicationPart(typeof(TodoController).GetTypeInfo().Assembly);
Controllers are part of MVC middleware, they are not a separate part of request pipeline (but this is what middlewares are). When you register the custom middleware, it by default invokes on each request and you have HttpContext context as an input parameter to work with/edit
Request/Response data. But ASP.NET Core provides Map* extensions that are used as a convention for branching the pipeline.
Map branches the request pipeline based on matches of the given request path. If the request path starts with the given path, the branch is executed.
Example:
private static void HandleMapTodo(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("/api/todo was handled");
});
}
public void Configure(IApplicationBuilder app)
{
app.Map("/api/todo", HandleMapTodo);
}
Note, that as middleware knows nothing about MVC middleware, you have only access to "raw" request and do not have features like model binding or MVC action filters.
Because it looks like the perfect microservices approach (similar than what my team is doing right now) I'd create a client assembly that can consume your API, the one that contains your TodoController, if you define a contract, and interface, for that API you can register it in your other assembly as it was a midleware and also you could mock that behaviour in your unit tests.
So, as I said, you could inject your client in ConfigureServices method, you can create:
public static IServiceCollection AddTodoRestClient(this IServiceCollection services)
{
services.AddSingleton<ITodoRestClient, TodoRestClient>();
return services;
}
Also consider that you will need to provide the enpoint so, it might looks like:
public static IServiceCollection AddConfiguredTodoClient(this IServiceCollection services, string todoEndpoint)
{
AddTodoClient(services);
ITodoRestClient todoRestClient = services.BuildServiceProvider().GetService<ITodoRestClient>();
// Imagine you have a configure method...
todoRestClient.Configure(services, todoEndpoint);
return services;
}
You can create those methods in a TodoRestClientInjector class and use them in Configure method on your startup.
I hope it helps
--- MORE DETAILS TO ANSWER COMMENTS ---
For me TodoClient is a Rest client library that implements calls to the ToDo API, (I've edited previous code to be TodoRestClient) methos like, i.e., CreateTodoItem(TodoDto todoItem) which implementation would call to the TodoController.Post([FromBody] item) or GetTodos() which wuold call TodoController.Get() and so on and so forth....
Regarding the enpoints... This approach implies to have (at least) two different applications (.NET Core apps), on the one hand the ASP NET Core app that has your TodoController and on the other hand a console application or another ASP NET Core API on which startup class you'll do the inyection adn the Rest client (the Todo Rest client) configuration ...
In a microservices approach using docker, in a dev environment, you'll use docker-compose-yml, but in a traditional approach you'll use concrete ports to define the endpoints...
So, imagine that you have in the second service a controller that need to use TodoController, to achieve so I'll use the above aproach and the "SecondController" would look like:
public class SecondController : Controller
{
private readonly SecondContext _context;
private readonly TodoRestClient _todoRestClient;
public TodoController(SecondContext context, ITodoRestClient todoRestClient)
{
_context = context;
_todoRestClient= todoRestClient;
}
// Whatever logic in this second controller... but the usage would be like:
_todoRestClient.GetTodos()
}
Just few final hints: it's key to minimize calls between services because it increases latency, and more and more if this happens on cascade. Also consider Docker usage, looks challenging but it is quite easy to start and, indeed, is thought to be used in scenarios that the one you presented and solutions like mine.
Again, I hope it helps.
Juan