Reputation: 3858
Is it possible to "disable" authentication in ASP.NET Core application without changing its logic?
I have a .net website which uses an external identity server app for authentication. Anyway I would like to be able to mock the authentication when I'm developing it (ASPNETCORE_ENVIRONMENT = Development), airing access to all actions ignoring the authorization attributes.
Is it possible to do it just mocking some services in the service collection?
Upvotes: 85
Views: 70254
Reputation: 134
in Net6 and above use :
app.UseEndpoints(endpoints =>
{
if (env.EnvironmentName == "Development")
{
endpoints.MapControllers().AllowAnonymous();
}
});
Upvotes: 0
Reputation: 681
As this thread comes up if you're looking up the error "System.NotSupportedException: Negotiate authentication requires a server that supports IConnectionItemsFeature like Kestrel." with .Net 7, the only way I was able to get around this issue with an MVC .Net Core project was to change my Program.cs.
Basically, removing the Authentication from the Services.
Note: You will have to manage your web apps launchSettings.json file and it's ASPNETCORE_ENVIRONMENT values. Which will be a part of your deployment.
Original Program.cs code:
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = options.DefaultPolicy;
});
New Code:
if (!builder.Environment.IsDevelopment())
{
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = options.DefaultPolicy;
});
}
Upvotes: 0
Reputation: 1956
You can bypass authorization in development environment by applying AllowAnonymousAttribute
to your endpoints.
.NET 6 (ASP.NET Core 6) and newer, dotnet new webapi
template
Use AllowAnonymous
method in Program.cs
to apply AllowAnonymousAttribute
to all controllers:
if (app.Environment.IsDevelopment())
app.MapControllers().AllowAnonymous();
else
app.MapControllers();
.NET Core 3.0 - .NET 5 (ASP.NET Core 3.0-5), dotnet new webapi
template
Use WithMetadata
method in Startup.Configure()
to apply AllowAnonymousAttribute
to all controllers:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// preceding code omitted for brevity
app.UseEndpoints(endpoints =>
{
if (env.IsDevelopment())
endpoints.MapControllers().WithMetadata(new AllowAnonymousAttribute());
else
endpoints.MapControllers();
});
}
Minimal API in .NET 6 (ASP.NET Core 6) and newer, dotnet new webapi -minimal
template
Use AllowAnonymous
method to apply AllowAnonymousAttribute
to a minimal API endpoint:
var hiEndpoint = app
.MapGet("/hi", () => "Hello!")
.RequireAuthorization();
if (app.Environment.IsDevelopment())
hiEndpoint.AllowAnonymous();
Details
endpoints
and app
from the examples above, both implement IEndpointRouteBuilder
which has multiple Map
extension methods like MapControllers()
and MapGet(...)
that return IEndpointConventionBuilder
.
WithMetadata
(available since .NET Core 3.0) and AllowAnonymous
(available since .NET 5) are extensions for IEndpointConventionBuilder
and can be called upon the results of those Map
methods.
AllowAnonymousAttribute
's description from the docs:
Specifies that the class or method that this attribute is applied to does not require authorization.
Upvotes: 76
Reputation: 1022
In ASP.NET Core 6, we managed to disable the authorization without changing any other part from the productive code, just the following logic in Program.cs:
if (!builder.Environment.IsDevelopment())
{
app.MapControllers();
}
else
{
app.MapControllers().AllowAnonymous();
}
Upvotes: 5
Reputation: 19628
This is to clarify @Kirill Lutsenko's answer about the method he found on the IllucIT blog post (note that in my case this is for .NET Core 2.0. I see other answers saying the AllowAnonymousFilter
method won't work in .NET Core 3.1):
The Startup class has an overloaded constructor. One of the overloads takes an IHostingEnvironment
parameter. You need to use this version of the constructor.
In the Startup class create a property of type IHostingEnvironment
. Call it, say, Environment
. Then set that property in the constructor.
Then, in the ConfigureServices
method, you can use Environment.IsDevelopment()
.
public class Startup
{
public Startup(IHostingEnvironment environment)
{
Environment = environment;
}
public IHostingEnvironment Environment { get; }
public IServiceProvider ConfigureServices(IServiceCollection services)
{
//...
services.AddMvc(options =>
{
// This uses the Environment property populated in the constructor.
if (Environment.IsDevelopment())
{
options.Filters.Add(new AllowAnonymousFilter());
}
// Set other options here. For example:
options.ModelBinderProviders.Insert(0, new UTCDateTimeModelBinderProvider());
//...
});
//...
}
}
As a side note, in real life we use a different overload of the constructor, which takes both an IConfiguration
object and an IHostingEnvironment
object as parameters. That allows us to configure services based on an appsettings.json configuration file.
For example:
public class Startup
{
public Startup(IConfiguration configuration, IHostingEnvironment environment)
{
Configuration = configuration;
Environment = environment;
}
public IConfiguration Configuration { get; }
public IHostingEnvironment Environment { get; }
public IServiceProvider ConfigureServices(IServiceCollection services)
{
//...
// Data access via Entity Framework
services.AddDbContext<ContainersDbContext>(options =>
{
options.UseNpgsql(Configuration.GetConnectionString("OrdersDatabase"));
});
//...
}
}
Upvotes: 1
Reputation: 1107
On updating to net core 3.1, the mvc AllowAnonymousFilter
was not working for us any more. We found conditionally adding a custom IAuthorizationHander
to be the simplest way forward to conditionally bypass auth.
eg.
/// <summary>
/// This authorisation handler will bypass all requirements
/// </summary>
public class AllowAnonymous : IAuthorizationHandler
{
public Task HandleAsync(AuthorizationHandlerContext context)
{
foreach (IAuthorizationRequirement requirement in context.PendingRequirements.ToList())
context.Succeed(requirement); //Simply pass all requirements
return Task.CompletedTask;
}
}
Then register this handler conditionally in Startup.ConfigureServices
.
private readonly IWebHostEnvironment _env;
public Startup(IWebHostEnvironment env)
{
_env = env;
}
public void ConfigureServices(IServiceCollection services)
{
{...}
//Allows auth to be bypassed
if (_env.IsDevelopment())
services.AddSingleton<IAuthorizationHandler, AllowAnonymous>();
}
Note AddAuthentication
and AddAuthorization
services are still registered and configured as per prod code (which is nice).
To allow our unit test to bypass auth, we added a new anonymous testbase with a startup class that added this line without any conditions. Nice and simple!
Upvotes: 71
Reputation: 1129
Another solution you may want to consider is using the IPolicyEvaluator. This means that you can keep all the existing security elements.
public class DisableAuthenticationPolicyEvaluator : IPolicyEvaluator
{
public async Task<AuthenticateResult> AuthenticateAsync(AuthorizationPolicy policy, HttpContext context)
{
// Always pass authentication.
var authenticationTicket = new AuthenticationTicket(new ClaimsPrincipal(), new AuthenticationProperties(), JwtBearerDefaults.AuthenticationScheme);
return await Task.FromResult(AuthenticateResult.Success(authenticationTicket));
}
public async Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context, object resource)
{
// Always pass authorization
return await Task.FromResult(PolicyAuthorizationResult.Success());
}
}
In the Startup.cs, ensure this appears at the top of the ConfigureServices method. Eg.
public void ConfigureServices(IServiceCollection services)
{
if (env.IsDevelopment())
{
// Disable authentication and authorization.
services.TryAddSingleton<IPolicyEvaluator, DisableAuthenticationPolicyEvaluator>();
}
...
Rather than Startup.cs (and thanks to the comments below) if you are using Core 3.1 and you wish to use the WebApplicationFactory, you can do the following:
public class MyWebApplicationFactory : WebApplicationFactory<Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services =>
{
// Disable Authentication.
services.RemoveAll<IPolicyEvaluator>();
services.AddSingleton<IPolicyEvaluator, DisableAuthenticationPolicyEvaluator>();
});
}
}
Upvotes: 35
Reputation: 347
I've found sollution for this problem on illucIT Blog.
This code must work:
if (env.IsDevelopment()) {
services.AddMvc(opts =>
{
opts.Filters.Add(new AllowAnonymousFilter());
});
} else {
services.AddMvc();
}
Upvotes: 18
Reputation: 13704
It's tricky to give a detailed answer without more details on your end, but I have previously achieved this by conditionally registering:
it looked something like:
public class Startup
{
public Startup(IHostingEnvironment env)
{
Environment = env;
}
public IHostingEnvironment Environment { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(x =>
{
if (!Environment.IsDevelopment())
{
var authenticatedUserPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
x.Filters.Add(new AuthorizeFilter(authenticatedUserPolicy));
}
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseStaticFiles();
if (!Environment.IsDevelopment())
{
// Register external authentication middleware
}
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
In my case, the authorization filter was applied globally, so every single action of the MVC app required an authenticated user.
If you have different requirements - fine-grained [Authorize]
attributes on some actions - then you could probably achieve the same result by changing how the associated authorization policies are built. They could basically contain no requirements at all.
AuthorizationPolicy yourCustomPolicy = null;
if (Environment.IsDevelopment())
{
yourCustomPolicy = new AuthorizationPolicyBuilder().Build();
}
else
{
yourCustomPolicy = new AuthorizationPolicyBuilder()
// chaining appropriate methods to suit your needs
.Build();
}
Upvotes: 8